📄 server.c
字号:
#include <stdio.h> /* standard C i/o facilities */#include <unistd.h> /* Unix System Calls */#include <sys/types.h> /* system data type definitions */#include <sys/socket.h> /* socket specific definitions */#include <netinet/in.h> /* INET constants and stuff */#include <arpa/inet.h> /* IP address conversion stuff */#include <stdlib.h> /* malloc */#include <pthread.h> /* Posix Threads stuff */#include <unistd.h> /* 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 10int 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;i<MAXCLIENTS;i++) { if ((i!=from) && (clients[i])) { nclients++; n = write(client_sd[i],msg,msglen); if (n<0) { printf("Error writing to client %d\n",i); exit(1); } } } /* release the lock */ releaselock(); return(nclients);}#define BUFSIZE 100/* this is the function called (as a new thread) whenever a new client joins in. handle_client watches for input from the client and broadcasts any messages received. To avoid race conditions, when main() creates a new handle_client thread it dynamically allocates a clientstruct and passes the address of it to handle_client - we have to free this when we are done with it.*/void *handle_client( void *arg) { clientptr me = (clientptr) arg; int clientnum = me->clientnum; 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 %ld 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) { int passivesock = (int ) arg; char buff[100]; char hostname[100] ; int n,i; int len; struct sockaddr_in skaddr; /* and out 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;i<MAXCLIENTS;i++) { if (clients[i]) n++; } printf("There are currently %d active clients (MAX is %d)\n",n,MAXCLIENTS); for (i=0;i<MAXCLIENTS;i++) { printf("Client[%d] ",i); if (clients[i]) { printf("Active\n"); } else { printf("empty\n"); } } releaselock(); break; } } return(NULL);}/* The message we send to new clients if we are full */char *fullmsg = "Sorry - no vacancy\n";/* The main sets up a passive socket and accepts new connections. For each new connection: - if the server is full a message is sent to the client and the client connection is terminated. - otherwise a thread is started to handle the client*/int main() { int ld,sd; struct sockaddr_in skaddr; struct sockaddr_in from; int addrlen,length; int i; clientptr newcli; pthread_t pt; if ((ld = socket( PF_INET, SOCK_STREAM, 0 )) < 0) { perror("Problem creating socket\n"); exit(1); } skaddr.sin_family = AF_INET; skaddr.sin_addr.s_addr = htonl(INADDR_ANY); skaddr.sin_port = htons(0); if (bind(ld, (struct sockaddr *) &skaddr, sizeof(skaddr))<0) { perror("Problem binding\n"); exit(0); } /* find out what port we were assigned and print it out */ length = sizeof( skaddr ); if (getsockname(ld, (struct sockaddr *) &skaddr, &length)<0) { perror("Error getsockname\n"); exit(1); } pport=ntohs(skaddr.sin_port); printf("%d\n",pport); /* put the socket into passive mode (waiting for connections) */ if (listen(ld,5) < 0 ) { perror("Error calling listen\n"); exit(1); } /* do some initialization (just to make sure) */ for (i=0;i<MAXCLIENTS;i++) clients[i]=0; /* start up the server status module */ pthread_create(&pt,NULL,server_status,(void *)ld); /* now process incoming connections forever ... */ while (1) { printf("Ready for a connection...\n"); addrlen=sizeof(skaddr); if ( (sd = accept( ld, (struct sockaddr*) &from, &addrlen)) < 0) { perror("Problem with accept call\n"); exit(1); } printf("Got a connection - processing...\n"); for (i=0;i<MAXCLIENTS;i++) { if (clients[i]==0) break; } if (i==MAXCLIENTS) { /* TOO MANY CLIENTS */ write(sd,fullmsg,strlen(fullmsg)); close(sd); } else { grablock(); client_sd[i]=sd; clients[i]=1; /* release the lock */ releaselock(); newcli = malloc(sizeof(clientstruct)); newcli->clientnum=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 %ld\n",pt); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -