完成端口之服务器篇
/* * 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> #include <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 of how the I/O // is progressing for each individual file handle. struct ContextKey { SOCKET sock; // Input char InBuffer[4]; OVERLAPPED ovIn; // Output int nOutBufIndex;//总字节 int nCurrentBufIndex;//每次读取的个数 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 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 always 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 *)&serverAddress, 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) { FatalError("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)((DWORD)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.dwNumberOfProcessors * 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 lpOverlapped; UNREFERENCED_PARAMETER(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'; // For 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,//每次接受的个数 &numRead, &pCntx->ovIn ); // It succeeded immediately, but do not process it // here, wait for the 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 == ERROR_NOT_ENOUGH_QUOTA || 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); }
完成端口之客户端
/* * 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_PORT 5554 #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"); }