套接字编程C / C ++ - recv函数在服务器中挂起

时间:2011-09-06 04:24:51

标签: c++ c windows sockets winsock

我在使用 recv功能

时遇到问题

我有一个从客户端发送一些数据的应用程序,这些数据由服务器&根据数据发送回复。

当我发送少于约16个请求时,实现工作正常。 但是当我从客户端发送超过16个请求(一个接一个)时,从服务器到第16个请求的响应都很好,但在此之后服务器挂起。我可以看到数据是从客户端传输的,但服务器没有收到。我使用的功能是 recv

接收发生在一个循环中,只有在收到客户端的终止请求时才会退出。

服务器代码:

   // Request Winsock version 2.2
   fprintf(stderr,"Performing WSAStartup\n");
   if ((retval = WSAStartup(0x202, &wsaData)) != 0)
   {
      fprintf(stderr,"FAILED with error %d\n", retval);
      WSACleanup();
      return -1;
   }
   else
   {
      printf("OK\n");
   }

   if (port == 0)
   {
      Usage(argv[0]);
   }


   /* open socket connection */
   printf("Opening socket\n");
   local.sin_family = AF_INET;
   local.sin_addr.s_addr = (!ip_address) ? INADDR_ANY:inet_addr(ip_address);
   /* Port MUST be in Network Byte Order */
   local.sin_port = htons(port);
   // TCP socket
   listen_socket = socket(AF_INET, socket_type,0);

   if (listen_socket == INVALID_SOCKET){
      fprintf(stderr,"socket() failed with error %d\n", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {   
      printf("OK\n");
   }

   // bind() associates a local address and port combination with the socket just created.
   // This is most useful when the application is a
   // server that has a well-known port that clients know about in advance.
   printf("Bind address and port to socket\n");
   if (bind(listen_socket, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR)
   {
      fprintf(stderr,"bind() failed with error %d\n", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {  
      printf("OK\n");
   }

   // So far, everything we did was applicable to TCP as well as UDP.
   // However, there are certain steps that do not work when the server is
   // using UDP. We cannot listen() on a UDP socket.
   if (socket_type != SOCK_DGRAM)
   {
      printf("TCP: listening on socket\n");
      if (listen(listen_socket,5) == SOCKET_ERROR)
      {
         fprintf(stderr,"listen() failed with error %d\n", WSAGetLastError());
         WSACleanup();

         return -1;
      }
      else
      {
         printf("OK\n");

      }
   }


//Perform Applcation task
//initisations

   printf("Server is listening and waiting for a connection\non port %d, protocol %s\n",port, (socket_type == SOCK_STREAM)?"TCP":"UDP");

   executeServer = 1;
   while(executeServer == 1)

   {
      fromlen =sizeof(from);
      // accept() doesn't make sense on UDP, since we do not listen()
      if (socket_type != SOCK_DGRAM)
      {
         printf("TCP: Waiting for connection (accept())\n");
         msgsock = accept(listen_socket, (struct sockaddr*)&from, &fromlen);
         if (msgsock == INVALID_SOCKET)
         {
            fprintf(stderr,"accept() error %d\n", WSAGetLastError());
            WSACleanup();
            return -1;
         }
         else
         {   
            printf("OK\n");
            printf("accepted connection from %s, port %d\n", inet_ntoa(from.sin_addr), htons(from.sin_port)) ;
         }
      }
      else
      {   
         msgsock = listen_socket;
      }

      // In the case of SOCK_STREAM, the server can do recv() and send() on
      // the accepted socket and then close it.

      // However, for SOCK_DGRAM (UDP), the server will do recvfrom() and sendto()  in a loop.

      printf("Receiving data");
      if (socket_type != SOCK_DGRAM)
      {   
         retval = recv(msgsock, Buffer, sizeof(Buffer), 0);
      }
      else
      {
         retval = recvfrom(msgsock,Buffer, sizeof(Buffer), 0, (struct sockaddr *)&from, &fromlen);
         printf("Received datagram from %s\n", inet_ntoa(from.sin_addr));

      }


      if (retval == SOCKET_ERROR)
      {
         fprintf(stderr,"recv() failed: error %d\n", WSAGetLastError());
         closesocket(msgsock);
         return -2;
      }
      else
      {
         printf("OK\n");
      }

      if (retval == 0)
      {
         printf("Client closed connection.\n");
         closesocket(msgsock);

      }
      else
      {
         printf("Received %d bytes, data \"%s\" from client\n", retval, Buffer);
      }

      printf("Processing Data\n");
      if (!stricmp(Buffer, "exit"))
      {
         wsprintf(AckBuffer,"ACK");
         executeServer = 0;
      }
      else
      {
        // Perform use task here based on recieved data

      }


      printf("Sending answer to client\n");
      if (socket_type != SOCK_DGRAM)
      {   
         retval = send(msgsock, AckBuffer, sizeof(AckBuffer), 0);
      }
      else
      {   
         retval = sendto(msgsock, AckBuffer, sizeof(AckBuffer), 0, (struct sockaddr *)&from, fromlen);
      }


      if (retval == SOCKET_ERROR)
      {
         fprintf(stderr,"send() failed: error %d\n", WSAGetLastError());
      }
      else
      {   
         printf("OK\n");
      }

      /* close TCP connection */
      if (socket_type != SOCK_DGRAM)
      {
         closesocket(msgsock);
      }


   }
   printf("terminating server\n");
   closesocket(msgsock);
   WSACleanup();

客户代码:

 fprintf(stderr,"Performing WSAStartup");
   if ((retval = WSAStartup(0x202, &wsaData)) != 0)
   {

      fprintf(stderr,"WSAStartup() failed with error %d\n", retval);
      WSACleanup();

      return -1;
   }
   else
   {
      printf("OK\n");
   }

   if (port == 0)

   {
      Usage(argv[0]);
   }

   // Attempt to detect if we should call gethostbyname() or gethostbyaddr()
   printf("Translate hastname to address -> gethostbyaddr()\n");
   if (isalpha(server_name[0]))
   {   // server address is a name
      hp = gethostbyname(server_name);
   }
   else
   { // Convert nnn.nnn address to a usable one
      addr = inet_addr(server_name);
      hp = gethostbyaddr((char *)&addr, 4, AF_INET);
   }
   if (hp == NULL )
   {
      fprintf(stderr,"Cannot resolve address \"%s\": Error %d\n", server_name, WSAGetLastError());
      WSACleanup();
      exit(1);
   }
   else
   {
      printf("OK\n");
   }

   // Copy the resolved information into the sockaddr_in structure
   printf("Opening socket\n");
   memset(&server, 0, sizeof(server));
   memcpy(&(server.sin_addr), hp->h_addr, hp->h_length);
   server.sin_family = hp->h_addrtype;
   server.sin_port = htons(port);

   conn_socket = socket(AF_INET, socket_type, 0); /* Open a socket */
   if (conn_socket <0 )
   {
      fprintf(stderr,"Error Opening socket: Error %d\n", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {
      printf("OK\n");
   }

   // Notice that nothing in this code is specific to whether we
   // are using UDP or TCP.
   // We achieve this by using a simple trick.
   //    When connect() is called on a datagram socket, it does not
   //    actually establish the connection as a stream (TCP) socket
   //    would. Instead, TCP/IP establishes the remote half of the
   //    (LocalIPAddress, LocalPort, RemoteIP, RemotePort) mapping.
   //    This enables us to use send() and recv() on datagram sockets,
   //    instead of recvfrom() and sendto()
   printf("Client connecting to: %s.\n", hp->h_name);
   if (connect(conn_socket, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
   {
      fprintf(stderr,"connect() failed: %d\n", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {  
      printf("OK\n");
   }

   /* copy options string to buffer */     
   strcpy(Buffer,Options);
   printf("Sending Data \"%s\"\n", Buffer);
   retval = send(conn_socket, Buffer, sizeof(Buffer), 0);
   if (retval == SOCKET_ERROR)
   {
      fprintf(stderr,"send() failed: error %d.\n", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {
      printf("OK\n");
   }

   printf("Receiving status from server\n");
   retval = recv(conn_socket, Buffer, sizeof(Buffer), 0);
   if (retval == SOCKET_ERROR)
   {
      fprintf(stderr,"recv() failed: error %d.\n", WSAGetLastError());
      closesocket(conn_socket);
      WSACleanup();
      return -1;
   }
   else
   {   
      printf("OK\n");
   }


   // We are not likely to see this with UDP, since there is no
   // 'connection' established.
   if (retval == 0)
   {
      printf("Client: Server closed connection.\n");
      closesocket(conn_socket);
      WSACleanup();
      return -1;
   }

   printf("Received %d bytes, data \"%s\" from server.\n", retval, Buffer);
   closesocket(conn_socket);
   WSACleanup();

1 个答案:

答案 0 :(得分:1)

如果您使用recv而未使套接字处于非阻止模式,则recv正在做正确的事情。它会阻塞,直到它有东西要读。

[UPDATE]

从代码中,您确实使用了阻塞套接字。我建议您至少为您的服务器使用非阻塞套接字。我相信你可以轻松谷歌如何在Windows中制作非阻塞套接字和处理异步IO。祝你好运!

[UPDATE2]

首先,一旦recv读取内容,您的服务器代码似乎会关闭连接。由于TCP不关心数据边界,因此您不能只关闭连接。请记住,客户端中的一个send调用可能需要在TCP服务器中进行多次recv调用,反之亦然。

针对您的具体问题,我非常确定无需阅读,这就是recv阻止您的计划的原因。确保您的客户确实成功发送了一些东西。