#include /* standard C i/o facilities */ #include /* Unix System Calls */ #include /* system data type definitions */ #include /* socket specific definitions */ #include /* INET constants and stuff */ #include /* IP address conversion stuff */ #include /* malloc */ #include /* Posix Threads stuff */ #include /* gethostname */ /* Chat Server - this is a threaded, concurrent server. The server accepts TCP connections (up to MAXCLIENTS at a time) and then routes any input received (from any client) to all other clients. The input from each client is handled by a separate thread, any thread will write any input received to the complete list of clients kept in the global variables. Access to the global variable is restricted to a single thread at a time using a pthread_mutex. The server also starts a thread to handle input from stdin - this provides an interface to some status functions. */ /*==========================================================*/ /* Global variables used by all the threads. clients is a list of flags, a 1 means the client is active (0 means available). client_sd is a list of socket descriptors, the only valid socket descriptors correspond to clients that are active (client_sd[i] is meaningless unless clients[i] is a 1). */ #define MAXCLIENTS 10 int clients[MAXCLIENTS]; /* Array of flags 1 means connected*/ int client_sd[MAXCLIENTS]; /* array of socket descriptors */ int pport; /* Passive mode port number */ /* mutex used to restrict access to clients and client_sd to a single thread at a time */ pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; /* functions to grab and release the mutex If anything goes wrong the server is shut down (somewhat rudely) */ void grablock() { /* grab the lock */ if (pthread_mutex_lock(&mtx)!=0) { printf("error getting mutex lock\n"); exit(1); } } void releaselock() { /* release the lock */ if (pthread_mutex_unlock(&mtx)!=0) { printf("error releasing mutex lock\n"); exit(1); } } /* a clientstruct is passed to each thread, to let it know which client it should handle */ typedef struct { int clientnum; int sd; } clientstruct, *clientptr; /* broadcast_message is called whenever input is received by a thread. This function sends a message to all clients (except the originator - from) No interpretation of messages is done, everything received is blindly written to all clients. */ int broadcast_message(int from, char *msg, int msglen ) { int i,n; int nclients=0; /* grab the lock */ grablock(); for (i=0;iclientnum; int sd = me->sd; char buf[BUFSIZE]; int n; free(arg); /* DLH: By default a new thread is joinable, we don't really want this (unless we do something special we end up with the thread equivalent of zombies). So we explicitly change the thread type to detached */ pthread_detach(pthread_self()); printf("Thread %d started for client number %d (sd %d)\n", pthread_self(),clientnum,client_sd[clientnum]); while ( (n=read(sd,buf,BUFSIZE))>0) { broadcast_message(clientnum,buf,n); } /* handle EOF */ if (n==0) { grablock(); close(client_sd[clientnum]); clients[clientnum]=0; printf("Client %d has left the building\n",clientnum); releaselock(); } else { printf("Error reading from client %d - quitting\n",clientnum); exit(1); } return(NULL); } /* This supports a status interface on STDIN - the user running the server can find out the status of things by typing commands to the server. A separate thread handles this interaction. The argument to the thread is the socket descriptor of the passive server socket (we need this so we can find out and print out the server port number). */ char *statushelp = "Commands:\n" " ? this help\n" " status show status\n"; void * server_status(void *arg) { char buff[100]; char hostname[100] ; int n,i; /* get our hostname */ gethostname(hostname,100); printf("Server Status Module XK-7413 operational...\n"); while ((n=read(0,buff,100))>0) { switch(buff[0]) { case '?': case 'h': case 'H': printf(statushelp); /* Help command */ break; case 's': case 'S': printf("\n\n-------------OPSCON Server Status Report------------\n"); printf("CHAT_OPS server up and running on %s, port %d\n", hostname,pport); printf("MANDMS security status:\n"); printf("\tCODEBLUE infiltration detection agent status: OK\n"); printf("\tMSGPOK trace: enabled\n"); printf("\tNIGHTTIME availability: unknown\n"); printf("\tQWERTY: reverse-encoding (22 bits)\n"); printf("\tCHOCCHIP module: missing and assumed digested\n\n"); /* must get a lock before looking at the globals (they might change in the middle of things). */ grablock(); n=0; for (i=0;iclientnum=i; newcli->sd=sd; /* Start up a thread for the client */ /* DLH: The book says you can pass NULL as the first arg to pthread_create, but when I do it (under Linux) I get a SEGV. I don't really care what the thread ids are, but as long as I get it I print it out.*/ pthread_create(&pt,NULL,handle_client,(void *) newcli); printf("Started thread %u\n",pt); } } }