Created
September 9, 2014 22:47
-
-
Save zweng/51ddf1c24a2f39c417bd to your computer and use it in GitHub Desktop.
a cheezy multiperson chat 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
/* | |
** selectserver.c -- a cheezy multiperson chat server | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <netdb.h> | |
#define PORT "9034" // port we're listening on | |
// get sockaddr, IPv4 or IPv6: | |
void *get_in_addr(struct sockaddr *sa) | |
{ | |
if (sa->sa_family == AF_INET) { | |
return &(((struct sockaddr_in*)sa)->sin_addr); | |
} | |
return &(((struct sockaddr_in6*)sa)->sin6_addr); | |
} | |
int main(void) | |
{ | |
fd_set master; // master file descriptor list | |
fd_set read_fds; // temp file descriptor list for select() | |
int fdmax; // maximum file descriptor number | |
int listener; // listening socket descriptor | |
int newfd; // newly accept()ed socket descriptor | |
struct sockaddr_storage remoteaddr; // client address | |
socklen_t addrlen; | |
char buf[256]; // buffer for client data | |
int nbytes; | |
char remoteIP[INET6_ADDRSTRLEN]; | |
int yes=1; // for setsockopt() SO_REUSEADDR, below | |
int i, j, rv; | |
struct addrinfo hints, *ai, *p; | |
FD_ZERO(&master); // clear the master and temp sets | |
FD_ZERO(&read_fds); | |
// get us a socket and bind it | |
memset(&hints, 0, sizeof hints); | |
hints.ai_family = AF_UNSPEC; | |
hints.ai_socktype = SOCK_STREAM; | |
hints.ai_flags = AI_PASSIVE; | |
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) { | |
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv)); | |
exit(1); | |
} | |
for(p = ai; p != NULL; p = p->ai_next) { | |
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol); | |
if (listener < 0) { | |
continue; | |
} | |
// lose the pesky "address already in use" error message | |
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); | |
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) { | |
close(listener); | |
continue; | |
} | |
break; | |
} | |
// if we got here, it means we didn't get bound | |
if (p == NULL) { | |
fprintf(stderr, "selectserver: failed to bind\n"); | |
exit(2); | |
} | |
freeaddrinfo(ai); // all done with this | |
// listen | |
if (listen(listener, 10) == -1) { | |
perror("listen"); | |
exit(3); | |
} | |
// add the listener to the master set | |
FD_SET(listener, &master); | |
// keep track of the biggest file descriptor | |
fdmax = listener; // so far, it's this one | |
// main loop | |
for(;;) { | |
read_fds = master; // copy it | |
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { | |
perror("select"); | |
exit(4); | |
} | |
// run through the existing connections looking for data to read | |
for(i = 0; i <= fdmax; i++) { | |
if (FD_ISSET(i, &read_fds)) { // we got one!! | |
if (i == listener) { | |
// handle new connections | |
addrlen = sizeof remoteaddr; | |
newfd = accept(listener, | |
(struct sockaddr *)&remoteaddr, | |
&addrlen); | |
if (newfd == -1) { | |
perror("accept"); | |
} else { | |
FD_SET(newfd, &master); // add to master set | |
if (newfd > fdmax) { // keep track of the max | |
fdmax = newfd; | |
} | |
printf("selectserver: new connection from %s on " | |
"socket %d\n", | |
inet_ntop(remoteaddr.ss_family, | |
get_in_addr((struct sockaddr*)&remoteaddr), | |
remoteIP, INET6_ADDRSTRLEN), | |
newfd); | |
} | |
} else { | |
// handle data from a client | |
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) { | |
// got error or connection closed by client | |
if (nbytes == 0) { | |
// connection closed | |
printf("selectserver: socket %d hung up\n", i); | |
} else { | |
perror("recv"); | |
} | |
close(i); // bye! | |
FD_CLR(i, &master); // remove from master set | |
} else { | |
// we got some data from a client | |
for(j = 0; j <= fdmax; j++) { | |
// send to everyone! | |
if (FD_ISSET(j, &master)) { | |
// except the listener and ourselves | |
if (j != listener && j != i) { | |
if (send(j, buf, nbytes, 0) == -1) { | |
perror("send"); | |
} | |
} | |
} | |
} | |
} | |
} // END handle data from client | |
} // END got new incoming connection | |
} // END looping through file descriptors | |
} // END for(;;)--and you thought it would never end! | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment