Created
July 19, 2012 04:57
-
-
Save jsanders/3140843 to your computer and use it in GitHub Desktop.
Simple echo server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <netinet/in.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#define MAX_CONNECTIONS 5 | |
#define BUFSIZE 1024 | |
typedef struct { | |
int fds[MAX_CONNECTIONS]; | |
int num; | |
} connections_t; | |
void die(char *msg) { | |
perror(msg); | |
exit(1); | |
} | |
unsigned short get_port(int argc, char **argv) { | |
if (argc != 2) { | |
fprintf(stderr, "usage: %s <port>\n", argv[0]); | |
exit(1); | |
} | |
return (unsigned short) atoi(argv[1]); | |
} | |
void bind_address(int listener, unsigned short port) { | |
struct sockaddr_in address; | |
memset(&address, 0, sizeof(address)); | |
address.sin_family = AF_INET; | |
address.sin_addr.s_addr = htonl(INADDR_ANY); | |
address.sin_port = htons(port); | |
if(bind(listener, (struct sockaddr *) &address, sizeof(address)) < 0) { | |
die("ERROR on binding"); | |
} | |
} | |
int create_listener(unsigned short port) { | |
int listener = socket(AF_INET, SOCK_STREAM, 0); | |
if (listener < 0) { die("ERROR opening socket"); } | |
bind_address(listener, port); | |
if(listen(listener, 5) < 0) { die("ERROR on listen"); } | |
return listener; | |
} | |
int set_fds_for_connections(connections_t *connections, fd_set *fds, int fd_max) { | |
int curr_fd, i; | |
for(i = 0; i < (*connections).num; i++) { | |
curr_fd = (*connections).fds[i]; | |
FD_SET(curr_fd, fds); | |
if(curr_fd > fd_max) { fd_max = curr_fd; } | |
} | |
return fd_max; | |
} | |
int set_fds_for_select(int listener, connections_t *connections, fd_set *fds) { | |
int fd_max = listener; | |
FD_ZERO(fds); | |
FD_SET(listener, fds); | |
return set_fds_for_connections(connections, fds, fd_max); | |
} | |
void add_connection(connections_t *connections, int fd) { | |
if((*connections).num + 1 > MAX_CONNECTIONS) { die("ERROR too many connections"); } | |
(*connections).fds[(*connections).num++] = fd; | |
} | |
int accept_connection(int listener) { | |
struct sockaddr_in address; | |
socklen_t address_size = sizeof(address); | |
int fd = accept(listener, (struct sockaddr *) &address, &address_size); | |
if(fd < 0) { die("ERROR on accept"); } | |
return fd; | |
} | |
void handle_connection(int fd) { | |
char buf[BUFSIZE]; | |
memset(&buf, 0, BUFSIZE); | |
int n = read(fd, buf, BUFSIZE); | |
if(n < 0) { die("ERROR reading from socket"); } | |
printf("server received %d bytes: %s", n, buf); | |
n = write(fd, buf, strlen(buf)); | |
if(n < 0) { die("ERROR writing to socket"); } | |
} | |
void handle_echo(connections_t *connections, fd_set *fds) { | |
int curr_fd, i; | |
for(i = 0; i < (*connections).num; i++) { | |
curr_fd = (*connections).fds[i]; | |
if(FD_ISSET(curr_fd, fds)) { | |
handle_connection(curr_fd); | |
} | |
} | |
} | |
void handle_connections(int listener, connections_t *connections) { | |
fd_set fds; | |
int fd_max = set_fds_for_select(listener, connections, &fds); | |
int ready = select(fd_max + 1, &fds, NULL, NULL, NULL); | |
if(ready < 0) { die("ERROR on select"); } | |
if(ready == 0) { return; } | |
if(FD_ISSET(listener, &fds)) { | |
add_connection(connections, accept_connection(listener)); | |
} | |
handle_echo(connections, &fds); | |
} | |
int main(int argc, char **argv) { | |
unsigned short port = get_port(argc, argv); | |
int listener = create_listener(port); | |
connections_t connections; | |
memset(&connections, 0, sizeof(connections)); | |
while(1) { | |
handle_connections(listener, &connections); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's more a case of being idiomatic. Generally, when presented with a pointer (which is a fine decision), experienced C programmers will almost always dereference it with
->
instead of(*)
. But that's really a nitpick.