• win32 socket编程帮助(二)


    server:

    Socket Server in Win32 Select Model

    Windows provides different models for programming with sockets. This winsock tutorial explains, one of the models which uses select function. The other winsock models are using event object, overlapped model and IO Completion port.

      This model design is based on Berkeley socket implementation. The basic idea is to use the select function to determine if a socket is to be read or to be written. This can be used without the problems of blocking.

     The select function takes 3 parameters. All of them are of type FD_SET. One parameter of type FD_SET is used to check if the socket is to be read, the other is used to check if the socket is to be written and the 3 is used to check if there is any out of band data. The most important among the 3 parameters is of course the first two. The sample program explained in this winsock tutorial will give a good idea on how to use them. To summarize,

    Read fd_set can check 

    • Data is available for reading
    • Connection has been closed or terminated

    Write fd_set can identify the following
     

    • Data can be sent
    • If a non-blocking connect call is being processed, the connection has succeeded.


     If you look at the following winsock tutorial program, it uses ioctlsocket function to make the socket non-blocking. Then it uses select function in an infinite loop to check the type of network event and executes the commands accordingly.

    Let us look at a sample program to understand this.


     #include ....

    #define PORT 1150
    #define BUFFERSIZE 8192

    typedef struct _MYSOCKET_INFORMATION {
    CHAR Buffer[BUFFERSIZE];
    WSABUF DataBuf;
    SOCKET Socket;
    DWORD SendBytes;
    DWORD RecvBytes;
    } SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

    BOOL CreateSocketInformation(SOCKET s);
    void FreeSocketInformation(DWORD Index);

    DWORD TotalSockets = 0;
    LPSOCKET_INFORMATION SocketList[FD_SETSIZE];

    void main(void)
    {

        //some of hte basic declarations required for this winsock tutorial
         SOCKET ListenSocket;
         SOCKET AcceptSocket;
         SOCKADDR_IN InternetAddr;
         WSADATA wsaData;
         FD_SET Writer;
         FD_SET Reader;
         ULONG NonBlock;
         DWORD Flags;
    //  .. and other declarations

         if ((Ret = WSAStartup(MAKEWORD(2,0),&wsaData)) != 0)
         {
           //startup failed
         }

    // Create a socket for the winsock tutorial.

         if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
             WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) 
         {
               printf("Winsock tutorial error: WSASocket() failed %d\n", WSAGetLastError());
               return;
          }

         InternetAddr.sin_family = AF_INET;
         InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
         InternetAddr.sin_port = htons(PORT);

         if (bind(ListenSocket, (SOCKADDR *) &InternetAddr, sizeof(InternetAddr))
            == SOCKET_ERROR)
         {
            printf("Winsock tutorial error: Binding failed %d\n", WSAGetLastError());
            return;
          }

        if (listen(ListenSocket, 5))
        {
            printf("Winsock tutorial error: listen failed %d\n", WSAGetLastError());
            return;
        }

    // Change the socket mode on the listening socket from blocking to non-block 

         NonBlock = 1;
         if (ioctlsocket(ListenSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)
         {
              printf("ioctlsocket() failed \n");
              return;
         }

         while(TRUE)
         {
             // Initialize the Read and Write socket set.
               FD_ZERO(&Reader);
               FD_ZERO(&Writer);

              // Check for connection attempts.
              FD_SET(ListenSocket, &Reader);

              // Set Read and Write notification for each socket based on the
              // current state the buffer. 

              for (i = 0; i < TotalSockets; i++)
              if (SocketList[i]->RecvBytes > SocketList[i]->SendBytes)
                   FD_SET(SocketList[i]->Socket, &Writer);
              else
                   FD_SET(SocketList[i]->Socket, &Reader);

             if ((Total = select(0, &Reader, &Writer, NULL, NULL)) == SOCKET_ERROR)
             {
                 printf("Winsock tutorial error: select function returned with error %d\n", WSAGetLastError());
                 return;
              }

            // Check for arriving connections on the listening socket.
             if (FD_ISSET(ListenSocket, &Reader))
             {
                 Total--;
                 if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET)
                 {

                    // Set the accepted socket to non-blocking mode so the server will
                   // not get caught in a blocked condition on WSASends

                   NonBlock = 1;
                   if (ioctlsocket(AcceptSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)
                   {
                        printf("Winsock tutorial error: ioctlsocket() failed with error %d\n", WSAGetLastError());
                        return;
                   }

                 if (CreateSocketInformation(AcceptSocket) == FALSE)
                    return;
         }
         else
         { 
              if (WSAGetLastError() != WSAEWOULDBLOCK)
              {
                  printf("accept() failed with error %d\n", WSAGetLastError());
                  return;
              }
          }
        }

         // Check each socket for Read and Write notification for Total number of sockets

        for ( i = 0; Total > 0 && i < TotalSockets; i++)
        {
            LPSOCKET_INFORMATION SocketInfo = SocketList[i];

            // If the Reader is marked for this socket then this means data
            // is available to be read on the socket.

            if (FD_ISSET(SocketInfo->Socket, &Reader))
            {
                Total--;

                SocketInfo->DataBuf.buf = SocketInfo->Buffer;
                SocketInfo->DataBuf.len = BUFFERSIZE;

                Flags = 0;
                if (WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes,
                        &Flags, NULL, NULL) == SOCKET_ERROR)
                {
                     if (WSAGetLastError() != WSAEWOULDBLOCK)
                     {
                            printf("Winsock tutorial: Receive failed with error\n");

                            FreeSocketInformation(i);
                 }
                 continue;
                } 
              else
              {
                   SocketInfo->RecvBytes = RecvBytes;
                   printf("%s\n",SocketInfo->DataBuf.buf);
     
                   // If zero bytes are received, this indicates connection is closed.
                   if (RecvBytes == 0)
                   {
                          FreeSocketInformation(i);
                          continue;
                   }
             }
       }


    // If the Writer is marked on this socket then this means the internal
    // data buffers are available for more data.

         if (FD_ISSET(SocketInfo->Socket, &Writer))
         {
              Total--;

              SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->SendBytes;
              SocketInfo->DataBuf.len = SocketInfo->RecvBytes - SocketInfo->SendBytes;

              if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0,
                 NULL, NULL) == SOCKET_ERROR)
              {
                    if (WSAGetLastError() != WSAEWOULDBLOCK)
                    {
                         printf("Send failed with error\n");
                         FreeSocketInformation(i);
              }

              continue;
           }
           else
          {
              SocketInfo->SendBytes += SendBytes;

              if (SocketInfo->SendBytes == SocketInfo->RecvBytes)
              {
                 SocketInfo->SendBytes = 0;
                 SocketInfo->RecvBytes = 0;
              }
            }
          }
        }
      }
    }

    BOOL CreateSocketInformation(SOCKET s)
    {
         LPSOCKET_INFORMATION SI;

         printf("Accepted socket\n");

         if ((SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR,
            sizeof(SOCKET_INFORMATION))) == NULL)
         {
             printf("Winsock tutorial error: GlobalAlloc() failed\n");
             return FALSE;
         }

    // Prepare SocketInfo structure for use.

         SI->Socket = s;
         SI->SendBytes = 0;
         SI->RecvBytes = 0;

         SocketList[TotalSockets] = SI;

         TotalSockets++;

         return(TRUE);
    }

    void FreeSocketInformation(DWORD Index)
    {
         LPSOCKET_INFORMATION SI = SocketList[Index];
         DWORD i;

         closesocket(SI->Socket);

         printf("Closing socket\n");

         GlobalFree(SI);

         // Remove from the socket array
         for (i = Index; i < TotalSockets; i++)
         {
             SocketList[i] = SocketList[i + 1];
         }

         TotalSockets--;
    }
     


      The base idea is follows. As for any server socket, the socket needs to be created, bind and then it should start listening at a specified port. Then the socket must be made Non-blocking by calling ioctlsocket. After that, check for any new connections, read or write using select function. 

      When the socket has to be checked for readability add the socket to the readfds set and wait for the function select to complete. When the call to select is complete, the socket is to be checked if it is still part of readfds set. If that is true, then the socket has some data to read. Any one of the three parameters must contain a socket handle while usage.

       This winsock tutorial manipulates the fd_set structures using the following functions. FD_CLR, FD_ISSET, FD_SET and FD_ZERO. The initialization and verifying the fd_set handles have to be done by the above four macros and then the select function has to be called.

     If you run the above program and try connecting using a client, it will receive the data and display it on the console.


    Note:
    The socket programs in MFC need the library ws2_32.lib to be referenced before linking. Otherwise the VC++ linker throws errors.

  • 相关阅读:
    cf1108E2 线段树类似扫描线
    poj1185 状态压缩经典题
    cf1110F 离线+树上操作+线段树区间更新
    tarjan求lca :并查集+dfs
    cf1110E 思维
    cf1110d 线性dp
    cf842D 01字典树|线段树 模板见hdu4825
    cf842C 树形dp+gcd函数
    cf581F 依赖背包+临时数组 好题
    hdu5758 思维,树形dp
  • 原文地址:https://www.cnblogs.com/lqc1002000/p/2555467.html
Copyright © 2020-2023  润新知