Server of port completion
/** Echosrv. c ** sample code for multithreading applications in Win32 * this is from chapter 6, listing 6-4 ** demonstrates how to use I/O Completion Ports * with TCP on the Internet. this sample * server can only be run on Windows NT, * version 3.51 or later. the client (echocli) * can be run on Windows 95. */# define win32_lean_and_mean # include <stdio. h> # include <stdlib. h> # include <windows. h> # I Nclude <tchar. h> # include <string. h> # include <Winsock. h> # include <Io. h> # include "mtverify. H "// pick a port number that seems to be away from all others # define serv_tcp_port 5554 # define maxline 512 // structure definition // The context key keeps track how the I/O // is progressing for each individual file handle. struct contextkey {socket sock; // input char inbuffer [4]; overlapped ovin; // outp Ut int noutbufindex; // total byte intncurrentbufindex; // Number of reads each time char outbuffer [maxline]; overlapped ovout; DWORD dwwritten ;}; /// global variables // handle ghcompletionport; // function prototypes // void createworkerthreads (); DWORD winapi threadfunc (lpvoid pvoid ); void issueread (struct contextkey * pcntx); void checkosversion (); void fatalError (char * s ); //////////////////////////////////////// /// // int Main (INT argc, char * argv []) {socket listener; socket newsocket; wsadata; struct sockaddr_in serveraddress; struct sockaddr_in clientaddress; int clientaddresslength; int err; checkosversion (); err = wsastartup (0x0101, & wsadata); If (ERR = socket_error) {fatalError ("wsastartup failed"); Return exit_failure ;} /** open a TCP socket connection to the server * by default, a socket is alw Ays opened * For overlapped I/O. do not attach this * socket (listener) to the I/O completion * port! */Listener = socket (af_inet, sock_stream, 0); If (listener <0) {fatalError ("socket () failed"); Return exit_failure ;} /** bind our local address */memset (& serveraddress, 0, sizeof (serveraddress); serveraddress. sin_family = af_inet; serveraddress. sin_addr.s_addr = htonl (inaddr_any); // inet_addr serveraddress. sin_port = htons (serv_tcp_port); err = BIND (listener, (struct sockaddr *) & serveraddres S, sizeof (serveraddress); If (ERR <0) fatalError ("BIND () failed"); ghcompletionport = createiocompletionport (invalid_handle_value, null, // No prior port 0, // No Key 0 // use default # of threads); If (ghcompletionport = NULL) fatalError ("createiocompletionport () failed"); createworkerthreads (ghcompletionport); Listen (listener, 5); fprintf (stderr, "echo server with I/O Completion Ports \ n "); Fprintf (stderr, "running on TCP port % d \ n", serv_tcp_port); fprintf (stderr, "\ npress Ctrl + C to stop the server \ n "); /// loop forever accepting requests new connections // and starting reading from them. // For (;) {struct contextkey * pkey; clientaddresslength = sizeof (clientaddress); newsocket = accept (listener, (struct sockaddr *) & clientaddress, & clientaddresslength ); if (newsocket <0) {fa Talerror ("accept () failed"); Return exit_failure;} // create a context key and initialize it. // calloc will zero the buffer pkey = calloc (1, sizeof (struct contextkey); pkey-> sock = newsocket; pkey-> ovout. hevent = createevent (null, true, false, null); // set the event for writing so that packets // will not be sent to the completion port when/A write finishes. pkey-> ovout. hevent = (handle) (d Word) pkey-> ovout. hevent | 0x1); // associate the socket with the completion port createiocompletionport (handle) newsocket, ghcompletionport, (DWORD) pkey, // No Key 0 // use default # of threads); // kick off the first read issueread (pkey);} return 0;} void createworkerthreads () {system_info sysinfo; DWORD dwthreadid; DWORD dwthreads; dword I; getsysteminfo (& sysinfo); dwthreads = sysinfo. dwnumber Ofprocessors * 2 + 2; for (I = 0; I <dwthreads; I ++) {handle hthread; hthread = createthread (null, 0, threadfunc, null, 0, & dwthreadid); closehandle (hthread) ;}/// each worker thread starts here. DWORD winapi threadfunc (lpvoid pvoid) {bool bresult; DWORD dwnumread; struct contextkey * pcntx; lpoverlapped metadata; convert (pvoid); // loop forever on getting packets from // The I/ O completion port. for (;) {bresult = getqueuedcompletionstatus (ghcompletionport, & dwnumread, & (DWORD) pcntx, & lpoverlapped, infinite); If (bresult = false & lpoverlapped = NULL) {fatalError ("threadfunc-illegal call to getqueuedcompletionstatus");} else if (bresult = false & lpoverlapped! = NULL) {// This happens occasionally instead of // end-of-file. not sure why. closesocket (pcntx-> sock); free (pcntx); fprintf (stderr, "threadfunc-I/O operation failed \ n");} else if (dwnumread = 0) {closesocket (pcntx-> sock); free (pcntx); fprintf (stderr, "threadfunc-end of file. \ n ");} // got a valid data block! // Save the data to our buffer and write it // All back out (echo it) if we have see A \ n else {// figure out where in the buffer to save the character // char * PCH = & pcntx-> outbuffer [pcntx-> noutbufindex ++]; memcpy (pcntx-> outbuffer + pcntx-> noutbufindex, pcntx-> inbuffer, pcntx-> ncurrentbufindex); pcntx-> noutbufindex + = pcntx-> ncurrentbufindex; /// * PCH ++ = pcntx-> inbuffer [0]; // * PCH = '\ 0'; // Debugging, writefile doesn't care if (pcntx-> inbuffer [pcntx-> ncurrentbufindex-1] = '\ n ') {pcntx-> outbuffer [pcntx-> noutbufindex] = '\ 0'; writefile (handle) (pcntx-> sock), pcntx-> outbuffer, pcntx-> noutbufindex, & pcntx-> dwwritten, & pcntx-> ovout); pcntx-> noutbufindex = 0; fprintf (stderr, "echo on socket % x. \ n ", pcntx-> sock);} // start a new read issueread (pcntx) ;}} return 0 ;}/ ** call readfile To start an overlapped Request * On a socket. make sure we handle errors * That are recoverable. */void issueread (struct contextkey * pcntx) {int I = 0; bool bresult; int err; int numread; while (++ I) {// request a single character bresult = readfile (handle) pcntx-> sock, pcntx-> inbuffer, 1, // number of records accepted each time & numread, & pcntx-> ovin); // It succeeded immediately, but do not process it // here, wait for Completion packet. if (bresult) {pcntx-> ncurrentbufindex = numread; return;} err = getlasterror (); // This is what we want to happen, it's not an error if (ERR = error_io_pending) return; // handle recoverable error if (ERR = error_invalid_user_buffer | err = role | err = error_not_enough_memory) {if (I = 5) // I just picked a number {sleep (50); // wait around and try Later continue;} fatalError ("issueread-system ran out of non-paged space");} break;} fprintf (stderr, "issueread-readfile failed. \ n ") ;}/// make sure we are running under the right versions // of Windows NT (3.51, 4.0, or later) // void checkosversion () {osversioninfo ver; bool bresult; ver. dwosversioninfosize = sizeof (osversioninfo); bresult = getversionex (lposversioninfo) & Ver); If ((! Bresult) | (ver. dwplatformid! = Ver_platform_win32_nt) {fatalError ("echosrv requires Windows NT 3.51 or later. ") ;}//// error handler // void fatalError (char * s) {fprintf (stdout," % s \ n ", S ); exit (exit_failure );}
Client with completed Port
/* * EchoCli.c * * Sample code for "Multithreading Applications in Win32" * This is from Chapter 6. * * This is a command line client to drive the ECHO server. * Run the server in one Commmand Prompt Window, * then run this program in one or more other windows. * EchoCli will wait for you to type in some text when * it starts up. Each line of text will be sent to the * echo server on TCP port 5554. */#include <windows.h>#include <tchar.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <winsock.h>/* Function Prototypes */void FatalError(char *s);int writen(SOCKET sock, char *ptr, int nBytes);int readline(SOCKET sock, char *ptr, int maxlen);void DoClientLoop(FILE *fp, SOCKET sock);/* Constants */#define MAXLINE 512#define SERVER_TCP_PORT5554#define SERVER_ADDRESS"127.0.0.1"/* * Error handler */void FatalError(char *s){ fprintf(stdout, "%s\n", s); exit(EXIT_FAILURE);}/* * Write bytes to the port with proper block size handling. */int writen(SOCKET sock, char *ptr, int nBytes){ int nleft; int nwritten; nleft = nBytes; while (nleft > 0) { nwritten = send(sock, ptr, nBytes, 0 ); if (nwritten == SOCKET_ERROR) { fprintf(stdout, "Send Failed\n"); exit(1); } nleft -= nwritten; ptr += nwritten; } return nBytes - nleft;}/* * Read a line of text of the port. This version * is very inefficient, but it's simple. */int readline(SOCKET sock, char *ptr, int maxlen){ int n; int rc; char c; for (n=1; n<maxlen; n++) { if ( ( rc= recv(sock, &c, 1, 0)) == 1) { *ptr++ = c; if (c=='\n') break; } else if (rc == 0) { if (n == 1) return 0; else break; } else return -1; /* Error */ } *ptr = '\0'; return n;}int main(int argc, char *argv[]){ WSADATA WsaData; SOCKET sock; struct sockaddr_in serv_addr; int err; puts("EchoCli - client for echo server sample program\n"); puts("Type a line of text followed by Return."); puts("Exit this program by typing Ctrl+Z followed by Return."); err = WSAStartup(0x0101, &WsaData); if (err == SOCKET_ERROR) FatalError("WSAStartup Failed"); /* * Bind our local address */ memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; // Use the local host serv_addr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS); serv_addr.sin_port = htons(SERVER_TCP_PORT); /* * Open a TCP socket (an Internet stream socket) */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) FatalError("socket() failed -- do you have TCP/IP networking installed?"); if (connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) FatalError("connect() failed -- is the server running?"); DoClientLoop(stdin, sock); closesocket(sock); return EXIT_SUCCESS;}/* * As long as there is input from "fp", copy it to * the server, read what the server gives back, * and print it. */void DoClientLoop(FILE *fp, SOCKET sock){ int n; char sendline[MAXLINE]; char recvline[MAXLINE+1]; while (fgets(sendline, MAXLINE, fp) != NULL) { n = strlen(sendline); if (writen(sock, sendline, n) != n) FatalError("DoClientLoop: writen() error"); n = readline(sock, recvline, MAXLINE); if (n < 0) FatalError("DoClientLoop: readline() error"); recvline[n] = '\0'; fputs(recvline, stdout); } if (ferror(fp)) FatalError("DoClientLoop: error reading file");}