/* Server listens on a given port number, accepts connections and ** capitalises any text received from clients and sends back the text */ #include #include #include #include #include #include /* For atoi() */ #include /* For toupper() */ #include /* For read() */ #include /* for gethostbyaddr() */ #include char* outBuffer = NULL; int outBufferLen = 0; /* outBuffer memory size */ int charsInBuffer = 0; /* predicate - "charsInBuffer > 0" */ pthread_mutex_t bufferMutex; pthread_cond_t bufferCond; int open_listen(int port) { int fd; struct sockaddr_in serverAddr; int optVal; /* Create a socket - Internet - TCP */ fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { perror("Error creating socket"); exit(1); } /* Set option on socket to immediately reuse address - otherwise ** we can get an address in use error until timeout (after exit) */ optVal = 1; if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(int)) < 0) { perror("Error setting socket option"); exit(1); } /* Create address structure for the adddress we're listening on */ serverAddr.sin_family = AF_INET; /* Internet address */ serverAddr.sin_port = htons(port); /* Port number - in network byte order*/ serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any IP address - in network byte order */ /* Bind the socket to this address - note the cast of address types */ if(bind(fd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_in)) < 0){ /* Error in bind - maybe address was already in use */ perror("Error binding socket to port"); exit(1); } /* Start listening for incoming connections. Second argument is ** the maximum number of unaccepted connection requests that will be ** queued by the OS. Agave value SOMAXCONN is 128. */ if(listen(fd, SOMAXCONN) < 0) { perror("Error listening"); exit(1); } return fd; } char* capitalise(char* buffer, int len) { int i; for(i=0; i 0) { /* Any data read is converted to upper case */ capitalise(buffer, numBytesRead); /* Send converted data back over the connection */ write(fd, buffer, numBytesRead); /* Append the data to the output buffer - need to lock ** the mutex first, i.e. wait until we can get exclusive access */ pthread_mutex_lock(&bufferMutex); /* Check if we need to grow the buffer */ if(charsInBuffer + numBytesRead > outBufferLen) { outBufferLen = charsInBuffer + numBytesRead; outBuffer = realloc(outBuffer, outBufferLen); } /* Append the data to the buffer */ memcpy(outBuffer + charsInBuffer, buffer, numBytesRead); charsInBuffer += numBytesRead; /* Setting our predicate */ /* Signal the condition variable that data is available, then ** unlock the mutex */ pthread_cond_signal(&bufferCond); pthread_mutex_unlock(&bufferMutex); } /* If we get here, the connection was closed, or an error occurred */ if(numBytesRead < 0) { perror("Error reading from socket"); exit(1); } /* printf("Done\n"); */ fflush(stdout); close(fd); pthread_exit(NULL); return NULL; } /* fdServer is the fd we're listening on */ void process_connections(int fdServer) { int fd; struct sockaddr_in fromAddr; int fromAddrSize; struct hostent *hp; pthread_t thread_id; /* Forever */ while(1) { fromAddrSize = sizeof(struct sockaddr_in); /* Accept a connection - wait if none are pending */ /* Note that fd is a new one */ fd = accept(fdServer, (struct sockaddr*)&fromAddr, &fromAddrSize); if(fd < 0) { perror("Error accepting connection"); exit(1); } /* Determine where the connection request came from and print it out */ hp = gethostbyaddr((char *)&fromAddr.sin_addr.s_addr, sizeof(fromAddr.sin_addr.s_addr), AF_INET); /* Comment out standard output printing, since it may interfere ** with out output thread */ /* printf("Accepted request from %s (%s), port %d\n", inet_ntoa(fromAddr.sin_addr), hp->h_name, ntohs(fromAddr.sin_port)); */ pthread_create(&thread_id, NULL, client_thread, (void*)fd); pthread_detach(thread_id); } } int main(int argc, char* argv[]) { int portnum; int fdServer; int outputThreadID; if(argc != 2) { fprintf(stderr, "Usage: %s port-num\n", argv[0]); exit(1); } /* Convert our ASCII port number to an integer */ portnum = atoi(argv[1]); /* Check our port number is in the valid range */ if(portnum < 1024 || portnum > 65535) { fprintf(stderr, "Invalid port number: %s\n", argv[1]); exit(1); } fdServer = open_listen(portnum); outBuffer = (char*)malloc(1024); outBufferLen = 1024; pthread_mutex_init(&bufferMutex, NULL); pthread_cond_init(&bufferCond, NULL); pthread_create(&outputThreadID, NULL, output_thread, NULL); process_connections(fdServer); return 0; }