Here’s a little multiuser chat server written for various POSIX-compatible operating systems. Written and tested on Mac OS X 10.6, but it should work on your favorite Linux, too.
Placed in public domain, use it for whatever you want (since it’s so simple). 177 lines of pure C powah, dood.
Code follows after the break.
server.c:
#include#include #include #include #include #include #include #include #include #include #include #include #define SLOTCOUNT 300 int listener = 0; int slots[SLOTCOUNT] = {0}; int maxslot = 0; int usage(char* call) { char* fn = strrchr(call, '/')+1; if(!fn) fn = call; printf("usage: %s port\n", fn); return 1; } void broadcast_message(int sender, char* msg) { int i; for(i = 0; i < SLOTCOUNT; i++) { if(!slots[i]) continue; if(i == sender) continue; send(i, msg, strlen(msg), 0); } if(sender != 0) printf("%s", msg); } int work() { maxslot = listener; do { fd_set set; int i; FD_ZERO(&set); FD_SET(1, &set); FD_SET(listener, &set); for(i=0; i < SLOTCOUNT; i++) { if(slots[i]) { FD_SET(slots[i], &set); } } int koliko = select(maxslot+1, &set, NULL, NULL, NULL); if(koliko==-1) err(5, "select()"); if(FD_ISSET(1, &set)) { int size; if(ioctl(1, FIONREAD, &size) != -1){ char *buf; // printf("Reading %d bytes...\n", size); buf = malloc(size+1); read(1, buf, size); buf[size] = 0; // printf("Received %s\n", buf); broadcast_message(0, buf); free(buf); } else err(7, "ioctl() stdio"); } if(FD_ISSET(listener, &set)) { int accepted = accept(listener, NULL, NULL); if(accepted == -1) err(6, "accept()"); slots[accepted] = accepted; if(accepted > maxslot) maxslot = accepted; } for(i = 0; i < SLOTCOUNT; i++) { if(slots[i] && FD_ISSET(slots[i], &set)) { // printf("Received on %d\n", i); int size; if(ioctl(i, FIONREAD, &size) != -1){ if(size > 0) { char *buf; // printf("Reading %d bytes...\n", size); buf = malloc(size+1); read(i, buf, size); buf[size] = 0; // printf("Received %s\n", buf); broadcast_message(i, buf); free(buf); } else { shutdown(i, SHUT_RDWR); close(i); slots[i] = 0; // printf("Disconnect on %d\n", i); } } else err(8, "ioctl() net"); } } } while(1); return 0; } int main(int argc, char** argv) { if(argc < 2) return usage(argv[0]); if((listener = socket(PF_INET, SOCK_STREAM, 0))==-1) err(1, "socket()"); int one=1; setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); struct sockaddr_in socketAddress; memset(&socketAddress, 0, sizeof(socketAddress)); socketAddress.sin_len = sizeof(socketAddress); socketAddress.sin_family = AF_INET; // Address family (IPv4 vs IPv6) socketAddress.sin_port = htons(atoi(argv[1])); // If we passed 0, the actual port would get assigned automatically by kernel and we'd have to retrieve it. Also, we use network byte order for 16bit numbers here by using htons socketAddress.sin_addr.s_addr = htonl(INADDR_ANY); // We must use "network byte order" format (big-endian) for the 32bit value here by using htonl printf("Listener: %d\n", listener); if(bind(listener, (struct sockaddr*) &socketAddress, sizeof(socketAddress))==-1) err(2, "bind()"); if(listen(listener, 1) == -1) err(3, "listen()"); return work(); }
Makefile:
CFLAGS=-g3 LDFLAGS=-g3 all: server server: server.o gcc server.o $(LDFLAGS) -o server clean: -rm server server.o
Note, in Makefiles, instead of two spaces, use tabs. You need to.
Try it out:
make ./server 1337
And on a few other terminals (or other machines):
telnet localhost 1337
Works over the network, too, of course.
Nota bene, you will probably need to install telnet on your Linux machine. Also, telnet does not come with latest Windows editions (Vista and 7); you will need to use putty over there.
–
via blog.vucica.net