With a complete reorganisation of the directory structure and most of the content converted to pandoc-flavoured markdown. Some TODO's left before this can go live: - Main page - Atom feeds - Bug tracker
180 lines
4.8 KiB
C
180 lines
4.8 KiB
C
/*
|
|
* echoserv.c - a simple single-threaded TCP server test
|
|
* by //YorHel
|
|
*
|
|
* Copyright 2006 Y. Heling,
|
|
* February 2006
|
|
* License: MIT
|
|
*
|
|
* Use it at your own risk
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/select.h>
|
|
|
|
#define LISTEN_PORT 1337
|
|
|
|
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
|
;
|
|
#define MAX_CONNECTIONS 10
|
|
#define READ_BUFFER_SIZE 512
|
|
|
|
#define CST_FREE 0
|
|
#define CST_READ 1
|
|
#define CST_WRITE 2
|
|
|
|
typedef struct {
|
|
int fd;
|
|
char state;
|
|
char *buf;
|
|
} connections;
|
|
static connections *conns;
|
|
|
|
void error(const char *);
|
|
void close_and_free(int);
|
|
|
|
int main() {
|
|
printf("Simple TCP server by //YorHel\n\n");
|
|
|
|
printf("* Creating socket\n");
|
|
int l;
|
|
if((l = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
error("Can't create socket");
|
|
|
|
printf("* Setting SO_REUSEADDR on main socket\n");
|
|
int set = 1;
|
|
if(setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (void *) &set, sizeof(set)) < 0)
|
|
error("Can't set SO_REUSEADDR on main socket");
|
|
|
|
printf("* Binding socket\n");
|
|
struct sockaddr_in serv_addr;
|
|
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
|
|
serv_addr.sin_family = AF_INET;
|
|
serv_addr.sin_port = htons(LISTEN_PORT);
|
|
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
|
if(bind(l, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
|
|
error("Can't bind socket");
|
|
|
|
printf("* Listening to socket\n");
|
|
listen(l, 5);
|
|
|
|
printf("* Creating connections table\n");
|
|
conns = (connections *) malloc( sizeof(connections) * MAX_CONNECTIONS );
|
|
if(conns == (connections *) 0)
|
|
error("Can't allocate memory for connections table");
|
|
int cnum;
|
|
for(cnum=MAX_CONNECTIONS; cnum--;)
|
|
conns[cnum].state = CST_FREE;
|
|
|
|
printf("* Accepting connections\n");
|
|
/* re-use the same vars over-and-over again
|
|
* is a little faster than defining them each time */
|
|
int n, cn, newid, clifd;
|
|
char stop = 0;
|
|
fd_set rd, wr;
|
|
struct sockaddr_in cliaddr;
|
|
unsigned int clilen;
|
|
/* main loop */
|
|
while(!stop) {
|
|
/* set FDs for select() */
|
|
FD_ZERO(&rd); FD_ZERO(&wr);
|
|
n = 0;
|
|
FD_SET(l, &rd);
|
|
n = MAX(l, n);
|
|
|
|
/* set FDs for the connections */
|
|
for(cn=MAX_CONNECTIONS;cn--;) {
|
|
if(conns[cn].state == CST_READ) {
|
|
FD_SET(conns[cn].fd, &rd);
|
|
n = MAX(n, conns[cn].fd);
|
|
} else if(conns[cn].state == CST_WRITE) {
|
|
FD_SET(conns[cn].fd, &wr);
|
|
n = MAX(n, conns[cn].fd);
|
|
}
|
|
}
|
|
|
|
n = select(n + 1, &rd, &wr, (fd_set *) NULL, (struct timeval *) 0);
|
|
if(n < 0)
|
|
error("select() failed...");
|
|
if(n == 0)
|
|
continue;
|
|
|
|
/* Something happend, handle it :) */
|
|
if(FD_ISSET(l, &rd)) { /* new connection */
|
|
/* get free slot, if one */
|
|
newid = -1;
|
|
for(cn=MAX_CONNECTIONS;cn--;)
|
|
if(conns[cn].state == CST_FREE) {
|
|
newid = cn; break;
|
|
}
|
|
if(newid < 0)
|
|
printf(" *WARNING: Too many connections\n");
|
|
else {
|
|
/* accept the connection */
|
|
clilen = sizeof(cliaddr);
|
|
memset(&cliaddr, 0, clilen);
|
|
clifd = accept(l, (struct sockaddr *) &cliaddr, &clilen);
|
|
if(clifd < 0)
|
|
error("Can't accept connection");
|
|
printf(" [%d] We have a connection!!\n", newid);
|
|
conns[newid].state = CST_WRITE;
|
|
if((conns[newid].buf = malloc(READ_BUFFER_SIZE)) < 0)
|
|
error("Can't allocate memory");
|
|
sprintf(conns[newid].buf, "Hello world!\n");
|
|
conns[newid].fd = clifd;
|
|
}
|
|
}
|
|
|
|
/* checking active sockets */
|
|
for(cn=MAX_CONNECTIONS;cn--;) {
|
|
/* we can write */
|
|
if(conns[cn].state == CST_WRITE && FD_ISSET(conns[cn].fd, &wr)) {
|
|
if(write(conns[cn].fd, conns[cn].buf, strlen(conns[cn].buf)) <= 0)
|
|
close_and_free(cn);
|
|
printf(" [%d] Sent: %s", cn, conns[cn].buf);
|
|
conns[cn].state = CST_READ;
|
|
}
|
|
/* we can read */
|
|
if(conns[cn].state == CST_READ && FD_ISSET(conns[cn].fd, &rd)) {
|
|
memset((void *) conns[cn].buf, 0, READ_BUFFER_SIZE);
|
|
if(read(conns[cn].fd, conns[cn].buf, READ_BUFFER_SIZE) <= 0)
|
|
close_and_free(cn);
|
|
printf(" [%d] G0t: %s", cn, conns[cn].buf);
|
|
conns[cn].state = CST_WRITE;
|
|
if(strstr(conns[cn].buf, "exit") == conns[cn].buf) {
|
|
printf(" [%d] Closing connection\n", cn);
|
|
close_and_free(cn);
|
|
} else if(strstr(conns[cn].buf, "die()") == conns[cn].buf) {
|
|
printf("Got die() from connection #%d, dying!\n", cn);
|
|
stop = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* close all connections before dying */
|
|
for(cnum=MAX_CONNECTIONS;cnum--;)
|
|
if(conns[cnum].state != CST_FREE)
|
|
close_and_free(cnum);
|
|
close(l);
|
|
|
|
|
|
return 0;
|
|
}
|
|
void close_and_free(int cn) {
|
|
close(conns[cn].fd);
|
|
conns[cn].state = CST_FREE;
|
|
free(conns[cn].buf);
|
|
}
|
|
|
|
|
|
void error(const char *msg) {
|
|
perror(msg);
|
|
exit(1);
|
|
}
|
|
|