Skip to content
Snippets Groups Projects
Commit d0ed30ec authored by Sebastian Schinzel's avatar Sebastian Schinzel
Browse files

Merge branch 'master' into 'master'

original(er) echo_server

See merge request !3
parents 2cd92e8e e3092caf
Branches master
No related tags found
No related merge requests found
echo-server
# Projekteinstellungen
cmake_minimum_required(VERSION 3.2)
project(echo_server
VERSION 2.1.0)
set(PROJECT_DESCRIPTION
"Echo-Server")
set(PROJECT_URL
"https://git.fh-muenster.de/schinzel/PSE2017-Test")
# "Debug" als Standard wählen
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, using DEBUG")
set(CMAKE_BUILD_TYPE "DEBUG")
endif ()
# Warnungen! Viele!
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -Wall -Werror")
# Ausführbare Datei: echo_server
add_executable(echo_server
echo_server.c)
set_property(TARGET echo_server
PROPERTY C_STANDARD 11)
install(TARGETS echo_server
DESTINATION bin)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
/* Version: 2.1.1 */
#include <errno.h> // errno
#include <netinet/ip.h> // ^
#include <signal.h> // sigaction, struct sigaction, siginfo_t, SIGTERM
#include <stdbool.h> // bool
#include <stdio.h> // fprintf
#include <stdlib.h> // exit, malloc, free
#include <string.h> // memset, strerror
#include <sys/socket.h> // struct sockaddr_in, socket, setsockopt, bind, listen, socklen_t, accept
#include <unistd.h> // read, write, close
#define PORT 31337
#define BUFFER_SIZE 1024
int sockfd; // Socket-Filedescriptor
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen = sizeof(cli_addr);
fd_set fds; // Filedescriptor-Set
int maxfd;
/**
* Globale Variablen.
*/
static bool run = true;
/**
* Fehlerbehandlung.
*/
void error(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(1);
static void error(char *msg) {
fprintf(stderr, "%s", msg);
if (errno) {
fprintf(stderr, ", errno: %s", strerror(errno));
}
fprintf(stderr, "\n");
exit(1);
}
/**
* Behandlung des SIGINT-Signals (Strg+C) um den Server zu beenden.
*/
static void handle_signal(int signum) {
if (signum != SIGINT) {
error("ERROR unexpected signal");
}
/*
* Beende den Server nach dem Abarbeiten des letzten Clients.
*/
run = false;
}
/**
* Registriert das SIGINT-Signal (Strg+C) um den Server beenden zu können.
*/
static void register_signal() {
struct sigaction action;
/*
* Konfigurieren des Signal-Handlers.
*/
memset(&action, 0, sizeof(action));
action.sa_handler = handle_signal;
/*
* Registrierung des Signal-Handlers.
*/
if (sigaction(SIGINT, &action, NULL) < 0) {
error("ERROR registering signal handler");
}
}
/**
* Erstellt und konfiguriert den Netzwerk-Socket, über den die Verbindungen
* angenommen werden.
*/
void setup_socket() {
int opt = 1;
/*
* Setzt Konfigurationsvariablen für den Socket, z. B. die Portnummer.
*/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT);
/*
* Erstelle den Socket.
*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
/*
* Verwende den Socket, selbst wenn er aus einer vorigen Ausführung
* im TIME_WAIT Status ist.
*/
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(int));
/*
* Melde, dass der Socket eingehende Verbindungen akzeptieren soll.
*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
/*
* Horche auf dem Socket nach eingehenden Verbindungen. Es werden maximal
* fünf gleichzeitige Verbindungen erlaubt.
*/
listen(sockfd, 5);
/* Lege fest in welchem 'Bereich' später select() 'warten' soll. ( hier: 0 - 3)*/
maxfd = 1 + ((sockfd > STDIN_FILENO) ? sockfd : STDIN_FILENO);
static int setup_socket() {
#ifdef STDIN_ONLY
return STDOUT_FILENO;
#endif
int opt = 1;
int sockfd = 0;
struct sockaddr_in serv_addr;
/*
* Setzt Konfigurationsvariablen für den Socket, z.B. die Portnummer.
*/
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT);
/*
* Erstelle den Socket.
*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
error("ERROR opening socket");
}
/*
* Verwende den Socket, selbst wenn er aus einer vorigen Ausführung
* im TIME_WAIT Status ist.
*/
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt,
sizeof(int)) < 0)
error("ERROR on setsockopt");
/*
* Melde, dass der Socket eingehende Verbindungen akzeptieren soll.
*/
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
error("ERROR on binding");
}
/*
* Horche auf dem Socket nach eingehenden Verbindungen. Es werden maximal
* fünf gleichzeitige Verbindungen erlaubt.
*/
if (listen(sockfd, 5) < 0) {
error("listen");
}
return sockfd;
}
/**
* Die Hauptschleife, in der eingehende Verbindungen angenommen werden.
*/
void main_loop() {
ssize_t length = 0;
char *buffer = malloc(BUFFER_SIZE);
if (buffer == NULL) {
error("ERROR at malloc.");
static void main_loop(int sockfd) {
int newsockfd;
ssize_t length;
#ifndef STDIN_ONLY
struct sockaddr_in cli_addr;
socklen_t clilen = sizeof(cli_addr);
#endif
void *const buffer = malloc(BUFFER_SIZE);
if (buffer == NULL) {
error("ERROR at malloc.");
}
/*
* Die Hauptshleife des Programms.
*/
while (run) {
#ifndef STDIN_ONLY
/*
* Der accept()-Aufruf blockiert, bis eine neue Verbindung rein kommt.
*/
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if (newsockfd < 0) {
/*
* Wenn der Server mit dem SIGINT-Signal beendet wird, schlägt accept
* mit EINTR (interrupted) fehl.
*/
if (errno == EINTR) {
break;
}
error("ERROR on accept");
}
#else
newsockfd = STDIN_FILENO;
#endif
/*
* Die Hauptschleife des Programms.
* Lies die ankommenden Daten von dem Socket in das Array buffer.
*/
while (1) {
/* "Datenhygiene" */
bzero(buffer, BUFFER_SIZE);
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
FD_SET(STDIN_FILENO, &fds);
/*
* select() erlaubt es auf mehrere file descriptors (FD_SETs)
* (z.B. socket und stdin) zu warten, bis einer "ready" für
* jegliche I/O operation (z.B. 'read()' ) ist.
*/
select(maxfd, &fds, NULL, NULL, NULL);
if (FD_ISSET(STDIN_FILENO, &fds)) {
length = read(STDIN_FILENO, buffer, BUFFER_SIZE - 1);
// Testweise Server sauber beenden mit '.' als Input über Konsole
if (buffer[0] == '.')
break;
if (length < 0)
error("ERROR reading from stdin");
}
//Input aus Socket
if (FD_ISSET(sockfd, &fds)) {
const int newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
length = read(newsockfd, buffer, BUFFER_SIZE - 1);
if (length < 0)
error("ERROR reading from socket");
/*
* Schreibe die ausgehenden Daten auf den Socket zurück.
*/
length = write(newsockfd, buffer, length);
if (length < 0)
error("ERROR writing to socket");
/*
* Schließe die Verbindung.
*/
close(newsockfd);
}
/*
* Gib die eingegangenen Daten (zusätzlich) auf der Kommandozeile aus.
*/
length = write(STDOUT_FILENO, buffer, length);
if (length < 0)
error("ERROR writing to stdout");
memset(buffer, 0, BUFFER_SIZE);
length = read(newsockfd, buffer, BUFFER_SIZE - 1);
if (length < 0) {
if (errno == EINTR) {
break;
}
error("ERROR reading from socket");
}
/*
* Schreibe die ausgehenden Daten auf den Socket.
*/
#ifndef STDIN_ONLY
length = write(newsockfd, buffer, (size_t)length);
if (length < 0) {
error("ERROR writing to socket");
}
#else
/*
* Lösche den Buffer und schließe den Socket. Dieser Aufruf sollte wegen der
* Endlosschleife niemals ausgeführt werden.
* Gib die eingegangenen Daten auf der Kommandozeile aus.
*/
free(buffer);
close(sockfd);
}
if (write(STDOUT_FILENO, buffer, length) < 0) {
error("ERROR writing to STDOUT");
}
#endif
/*
* Schließe die Verbindung.
*/
#ifndef STDIN_ONLY
if (close(newsockfd) < 0) {
error("ERROR on close");
}
#endif
}
/*
* Lösche den Buffer und schließe den Socket. Dieser Aufruf sollte wegen der
* Endlosschleife niemals ausgeführt werden.
*/
free(buffer);
#ifndef STDIN_ONLY
if (close(sockfd) < 0) {
error("ERROR on close");
}
#endif
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
register_signal();
const int sockfd = setup_socket();
main_loop(sockfd);
setup_socket();
main_loop();
return 0;
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment