home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / netds / winsock / overlap / overlap.c < prev    next >
C/C++ Source or Header  |  1997-10-08  |  12KB  |  445 lines

  1. /******************************************************************************\
  2. * Sample demonstrating use of Events in Overlapped (Asynchronous) I/O
  3. *
  4. * This code uses AcceptEx()
  5. * YOU MUST HAVE SERVICE PACK 3 on NT 3.51 to use it !!!
  6. *
  7. *       This is a part of the Microsoft Source Code Samples.
  8. *       Copyright 1996-1997 Microsoft Corporation.
  9. *       All rights reserved.
  10. *       This source code is only intended as a supplement to
  11. *       Microsoft Development Tools and/or WinHelp documentation.
  12. *       See these sources for detailed information regarding the
  13. *       Microsoft samples programs.
  14. \******************************************************************************/
  15.  
  16.  
  17. #define WIN32_LEAN_AND_MEAN
  18. #include <winsock2.h>
  19. #include <mswsock.h>
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23.  
  24. #define DEFAULT_PORT 5001
  25. #define MAX_IO_PEND 10        // maximum pending I/O requests
  26.  
  27. #define OP_READ 0x10
  28. #define OP_WRITE 0x20
  29.  
  30. #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
  31. #define xfree(p)   HeapFree(GetProcessHeap(),0,(p))
  32.  
  33. //
  34. // This structure keeps some useful information
  35. //
  36. typedef struct _socklist {
  37.     SOCKET sock;
  38.     OVERLAPPED *overlap;
  39.     char Buffer[128];
  40. }Socklist;
  41.  
  42. int curr_size; //current number of handles we are intersted in
  43.  
  44. int DoWait(HANDLE *,Socklist *) ;
  45. void HandleEvent(int , HANDLE *,Socklist *) ;
  46.  
  47. void Usage(char *progname) {
  48.     fprintf(stderr,"Usage\n%s -e [endpoint] -i [interface]\n",
  49.         progname);
  50.     fprintf(stderr,"Where:\n");
  51.     fprintf(stderr,"\tendpoint is the port to listen on\n");
  52.     fprintf(stderr,"\tinterface is the ipaddr (in dotted decimal notation)");
  53.     fprintf(stderr," to bind to\n");
  54.     fprintf(stderr,"Defaults are 5001 and INADDR_ANY\n");
  55.     WSACleanup();
  56.     exit(1);
  57. }
  58.  
  59. int main(int argc, char **argv) {
  60.  
  61.     char *interface= NULL;
  62.     char *Buffer = xmalloc(256);
  63.     unsigned short port=DEFAULT_PORT;
  64.     int i;
  65.     struct sockaddr_in local;
  66.     WSADATA wsaData;
  67.     SOCKET listen_socket, accept_sock;
  68.     OVERLAPPED *Overlap;
  69.     DWORD bytes_read;
  70.     DWORD lasterror;
  71.     //
  72.     // Handles is the array that stores the Event Handles
  73.  
  74.     HANDLE Handles[MAX_IO_PEND] ;
  75.  
  76.     //
  77.     // socklist is a parallel array that keeps state information for
  78.     // each Handle.
  79.     Socklist socklist[MAX_IO_PEND];
  80.  
  81.     /* Parse arguments */
  82.     if (argc >1) {
  83.         for(i=1;i <argc;i++) {
  84.             if ( (argv[i][0] == '-') || (argv[i][0] == '/') ) {
  85.                 switch(tolower(argv[i][1])) {
  86.  
  87.                     case 'i':
  88.                         interface = argv[++i];
  89.                         break;
  90.                     case 'e':
  91.                         port = atoi(argv[++i]);
  92.                         break;
  93.                     default:
  94.                         Usage(argv[0]);
  95.                         break;
  96.                 }
  97.             }
  98.             else
  99.                 Usage(argv[0]);
  100.         }
  101.     }
  102.     
  103.     if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
  104.         fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());
  105.         WSACleanup();
  106.         return -1;
  107.     }
  108.     
  109.     if (port == 0){
  110.         Usage(argv[0]);
  111.     }
  112.  
  113.     local.sin_family = AF_INET;
  114.     local.sin_addr.s_addr = (!interface)?INADDR_ANY:inet_addr(interface); 
  115.  
  116.     /* 
  117.      * Port MUST be in Network Byte Order
  118.      */
  119.     local.sin_port = htons(port);
  120.  
  121.     listen_socket = socket(AF_INET,SOCK_STREAM,0); // TCP socket
  122.     if(listen_socket == INVALID_SOCKET) {
  123.         fprintf(stderr,"socket() failed with error %d\n",WSAGetLastError());
  124.         WSACleanup();
  125.         return -1;
  126.     }
  127.     
  128.  
  129.     if (bind(listen_socket,(struct sockaddr*)&local,sizeof(local) ) 
  130.         == SOCKET_ERROR) {
  131.         fprintf(stderr,"bind() failed with error %d\n",WSAGetLastError());
  132.         WSACleanup();
  133.         return -1;
  134.     }
  135.  
  136.  
  137.     if (listen(listen_socket,5) == SOCKET_ERROR) {
  138.         fprintf(stderr,"listen() failed with error %d\n",WSAGetLastError());
  139.         WSACleanup();
  140.         return -1;
  141.     }
  142.     printf("%s: Listening on port %d\n",argv[0],port);
  143.         
  144.     //
  145.     // Add the listening socket to our state information for the handle.
  146.     //
  147.     socklist[0].sock = listen_socket;
  148.  
  149.     curr_size =1;
  150.  
  151.     for(i=1;i<MAX_IO_PEND;i++)
  152.         Handles[i] = INVALID_HANDLE_VALUE;
  153.  
  154.     //
  155.     // The structure of the following loop is very similar to a situation
  156.     // where select() might be used. 
  157.     // We use WaitForSingleObject to multiplex between incoming/outgoing
  158.     // data on existing connections.
  159.     //
  160.     // We don't queue an AcceptEx() until someone actually connects to 
  161.     // the previous socket. This is to keep the code simple, not a limitation
  162.     // of the API itself.
  163.  
  164.     while(1) {
  165.  
  166.         // create a socket for AcceptEx()
  167.  
  168.         accept_sock = socket(AF_INET,SOCK_STREAM,0);
  169.  
  170.         //
  171.         // Allocate an overlapped structure.
  172.         // We use the Offset field to keep track of the socket handle
  173.         // we have accpeted a connection on, since there is no other
  174.         // way to pass information to GetOverlappedResult()
  175.         //
  176.         Overlap = xmalloc(sizeof(OVERLAPPED));
  177.         Overlap->Offset = accept_sock;
  178.  
  179.         Overlap->hEvent = CreateEvent(NULL,
  180.                                      TRUE,//manual reset
  181.                                      FALSE, // initially non-signalled
  182.                                      NULL);
  183.  
  184.         if (!Overlap->hEvent) {
  185.             fprintf(stderr,"CreateEvent failed %d\n",GetLastError());
  186.             return -1;
  187.         }
  188.         //
  189.         // Set the appropriate array members
  190.         //
  191.         Handles[0] = Overlap->hEvent;
  192.         socklist[0].overlap = Overlap;
  193.  
  194.         // AcceptEx()
  195.         if (!AcceptEx(listen_socket,
  196.                       accept_sock,
  197.                       Buffer,
  198.                       0, // read nothing from the socket
  199.                       sizeof(struct sockaddr_in) +16,
  200.                       sizeof(struct sockaddr_in) +16,
  201.                       &bytes_read,
  202.                       Overlap)){
  203.             lasterror=WSAGetLastError();
  204.             if(lasterror!=ERROR_IO_PENDING){
  205.                 fprintf(stderr,"acceptex failed %d\n",lasterror);
  206.                 return -1;
  207.             }
  208.         }
  209.         //
  210.         // This loop simple checks the handles to see which one is 
  211.         // signalled. 
  212.         // If error, exit. 
  213.         // If there is a new incoming connection, we break to the outer loop
  214.         // queue another AcceptEx()
  215.         //
  216.         while(1){
  217.             i = DoWait(Handles,socklist);
  218.             if (i<0)
  219.                 break;
  220.             HandleEvent(i,Handles,socklist);
  221.             if (i ==0)
  222.                 break;
  223.         };
  224.         if (i < 0)
  225.             return -1;
  226.  
  227.     }
  228. }
  229. /*
  230.  * This is the main function that handles all the events occuring on the
  231.  * different handles we are watching.
  232.  *
  233.  * Parameters:
  234.  *          index: Index into the Handles[] array. Returned by DoWait()
  235.  *          Handles: Array of Event Handles we are watching
  236.  *          socklist: Helper parallel array of state information
  237.  *
  238.  */
  239. void HandleEvent(int index, HANDLE *Handles,Socklist *socklist) {
  240.     
  241.     OVERLAPPED *Overlap;
  242.     SOCKET newsock;
  243.     DWORD bytes,overlap_err=0,lasterr;
  244.  
  245.     Overlap = socklist[index].overlap;
  246.     //
  247.     // Check the specified handle
  248.     //
  249.     // If a socket is closed by the other side, the error returned is
  250.     // ERROR_NETNAM_DELETED
  251.     //
  252.     if(!GetOverlappedResult(Handles[index], Overlap, &bytes, TRUE) ) {
  253.         fprintf(stderr,"GetOverlappedResult failed with error %d\n",
  254.                     overlap_err=GetLastError());
  255.         if (overlap_err  != ERROR_NETNAME_DELETED) 
  256.             return;
  257.     }
  258.     newsock = Overlap->Offset;
  259.     //
  260.     // If the other side closed the connection, close our socket and 
  261.     // move the last element of the Handles[] array into our 
  262.     // index.
  263.     //
  264.     // The array compaction is done so that we only pass valid handles
  265.     // in the first "curr_size" elements of the array to
  266.     // WaitForMultipleObjects(). The function will fail otherwise.
  267.  
  268.  
  269.     // We should NEVER get this for our listening socket
  270.     if (index && overlap_err == ERROR_NETNAME_DELETED) {
  271.         closesocket(newsock);
  272.         xfree(Overlap);
  273.         Handles[index] = Handles[curr_size-1];
  274.         socklist[index] = socklist[curr_size-1];
  275.         curr_size--;
  276.         return;
  277.     }
  278.  
  279.     if( (index ==0) ) { //listening socket
  280.         if (curr_size >= MAX_IO_PEND) {
  281.             fprintf(stderr,"Too many pending requests\n");
  282.             return;
  283.         }
  284.         //
  285.         // Get the event handle used to queue the AcceptEx(),
  286.         // and re-use it to queue a ReadFile on the socket.
  287.         //
  288.  
  289.         Handles[curr_size] = Overlap->hEvent;
  290.         socklist[curr_size].overlap = Overlap;
  291.  
  292.         //
  293.         // The OffsetHigh field is used to keep track of what we are doing.
  294.         // This enables us to alternate ReadFile and WriteFile on a 
  295.         // connection
  296.  
  297.         Overlap->OffsetHigh = OP_READ;
  298.  
  299.         if (!ReadFile((HANDLE)newsock, socklist[curr_size].Buffer,
  300.                         sizeof(socklist[curr_size].Buffer),
  301.                         &bytes,
  302.                         Overlap) ) {
  303.             lasterr = GetLastError();
  304.  
  305.             // Handle ERROR_NETNAME_DELETED specially
  306.             // Other errors are Not Good
  307.             //
  308.             if (lasterr && lasterr != ERROR_IO_PENDING &&
  309.                 lasterr != ERROR_NETNAME_DELETED ) {
  310.                 fprintf(stderr,"Inital ReadFile failed %d\n");
  311.                 return;
  312.             }
  313.             if (lasterr == ERROR_NETNAME_DELETED) {
  314.                 closesocket(newsock);
  315.                 xfree(Overlap);
  316.                 Handles[index] = Handles[curr_size];
  317.                 socklist[index] = socklist[curr_size];
  318.                 curr_size--;
  319.                 return;
  320.             }
  321.  
  322.         }
  323.         //
  324.         // Increment the last valid handle location in the Handles
  325.         // array.
  326.         curr_size++;
  327.         return;
  328.     }
  329.     //
  330.     // This possibly indicates a closed socket.
  331.     //
  332.     if (  (bytes == 0 ) && (Overlap->OffsetHigh == OP_READ) ){
  333.         closesocket(newsock);
  334.         xfree(Overlap);
  335.         Handles[index] = Handles[curr_size];
  336.         socklist[index] = socklist[curr_size];
  337.         curr_size--;
  338.         return;
  339.     }
  340.     //
  341.     //  If the previos operation was an OP_READ, queue WriteFile on the
  342.     //  socket
  343.     //
  344.     if (Overlap->OffsetHigh == OP_READ) { // ReadFile was queued
  345.         printf("Read buffer [%s]\n",socklist[index].Buffer);
  346.         printf("Echoing back to client\n");
  347.         if (!WriteFile((HANDLE)newsock, socklist[index].Buffer,
  348.                         sizeof(socklist[index].Buffer),
  349.                         &bytes,
  350.                         Overlap) ) {
  351.             lasterr = GetLastError();
  352.             if (lasterr && lasterr != ERROR_IO_PENDING &&
  353.                 lasterr != ERROR_NETNAME_DELETED ) {
  354.                 fprintf(stderr,"WriteFile failed %d\n");
  355.                 ExitProcess(1);
  356.             }
  357.             if ( (lasterr == ERROR_NETNAME_DELETED) || (!lasterr)) {
  358.                     closesocket(newsock);
  359.                     xfree(Overlap);
  360.                     Handles[index] = Handles[curr_size];
  361.                     socklist[index] = socklist[curr_size];
  362.                     curr_size--;
  363.                     return;
  364.             }
  365.         }
  366.         Overlap->OffsetHigh = OP_WRITE;
  367.         return;
  368.     }
  369.     //
  370.     // If we had a WriteFile queued, now do a ReadFile
  371.     //
  372.     else if (Overlap->OffsetHigh == OP_WRITE) { // WriteFile was queued
  373.         printf("Wrote %d bytes\n",bytes);
  374.         printf("Queueing read\n");
  375.         if (!ReadFile((HANDLE)newsock, socklist[index].Buffer,
  376.                         sizeof(socklist[index].Buffer),
  377.                         &bytes,
  378.                         Overlap) ) {
  379.             lasterr =GetLastError();
  380.             if (lasterr && lasterr != ERROR_IO_PENDING) {
  381.                 if (lasterr == ERROR_NETNAME_DELETED) {
  382.                     closesocket(newsock);
  383.                     xfree(Overlap);
  384.                     Handles[index] = Handles[curr_size];
  385.                     socklist[index] = socklist[curr_size];
  386.                     curr_size--;
  387.                     return;
  388.                 }
  389.                 fprintf(stderr,"ReadFile failed %d\n",GetLastError());
  390.                 ExitProcess(1);
  391.             }
  392.         }
  393.         Overlap->OffsetHigh = OP_READ;
  394.         return;
  395.     }
  396.     else {
  397.         fprintf(stderr,"Unknown operation queued\n");
  398.     }
  399.     
  400. }
  401. //
  402. // This is the wait function used to keep track of events
  403. //
  404. int DoWait(HANDLE *Handles,Socklist *socklist ) {
  405.  
  406.     DWORD wait_rc;
  407.     HANDLE hTemp;
  408.     Socklist socklTemp;
  409.  
  410.     int i;
  411.  
  412.     //
  413.     // Rotate the array, beginning at index 1, by one element.
  414.     // This ensures that all handles get a fair chance to be serviced.
  415.     //
  416.     // There is no way to detect how many handles were signalled when
  417.     // WaitForMultipleObjects() returns. We simply pick the first one and 
  418.     // come back to this function later
  419.     // Without the rotation below, this has the potential for starving
  420.     // connections accepted later.
  421.     //
  422.     // Index 0 is avoided, since it is our listening socket. 
  423.     //
  424.     for(i=1;i<curr_size-1;i++){
  425.  
  426.         hTemp = Handles[i+1];
  427.         Handles[i+1] = Handles[i];
  428.         Handles[i] = hTemp;
  429.  
  430.         socklTemp = socklist[i+1];
  431.         socklist[i+1] = socklist[i];
  432.         socklist[i] = socklTemp;
  433.     }
  434.  
  435.     wait_rc = WaitForMultipleObjects(curr_size,Handles,FALSE,
  436.         INFINITE);
  437.  
  438.     if (wait_rc == WAIT_FAILED) {
  439.         fprintf(stderr,"Wait failed Error %d\n",GetLastError());
  440.         return -1;
  441.     }
  442.  
  443.     return (wait_rc - WAIT_OBJECT_0);
  444. }
  445.