简体   繁体   English

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

[英]Socket Programming C/C++ - recv function hangs in Server

I am having problem in usage of recv function 我在使用recv函数时遇到问题

I have an application that send some data from client, these data are received by the server & send a responses based on data. 我有一个从客户端发送一些数据的应用程序,这些数据被服务器接收并基于数据发送响应。

The implementation works fine when I send less than ~16 requests. 当我发送少于16个请求时,该实现工作正常。 But when I send more than 16 request (one after the other) from the client, the response are fine from the server until 16th request but after this the server hangs. 但是,当我从客户端发送超过16个请求(一个接一个)时,从服务器直到第16个请求的响应都很好,但是在此之后服务器挂起。 I could see that the data are transmitted from client but are not received by server. 我可以看到数据是从客户端传输的,但服务器未接收到。 I am using the function recv 我正在使用功能recv

The reception is happening in a loop which is exited only when termination request is received from the client. 接收发生在一个循环中,只有当从客户端收到终止请求时,该循环才会退出。

Server Code: 服务器代码:

   // 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();

Client Code: 客户代码:

 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();

If you use recv without making your socket non-blocking mode, your recv is doing a right thing. 如果使用recv而不使套接字成为非阻塞模式,则recv做正确的事。 It blocks until it has something to read. 它会阻塞,直到有一些要读取的内容为止。

[UPDATE] [更新]

From the code, you are indeed using blocking socket. 根据代码,您确实在使用阻塞套接字。 I recommend you use non-blocking socket at least for your server. 我建议您至少对服务器使用非阻塞套接字。 I believe you can easily google how to make non-blocking socket and handle async IO in Windows. 我相信您可以轻松地通过Google搜索如何制作非阻塞套接字并在Windows中处理异步IO。 Good Luck! 祝好运!

[UPDATE2] [UPDATE2]

First of all, your server code seems to close connection once recv reads something. 首先,一旦recv读取某些内容,您的服务器代码似乎就关闭了连接。 Since TCP does not care data boundary, you can't just close the connection. 由于TCP不在乎数据边界,因此您不能仅关闭连接。 Remember one send call in client may require multiple recv calls in server with TCP or vice versa. 请记住,客户端中的一个send调用可能需要使用TCP的服务器中的多个recv调用,反之亦然。

For your specific problem, I'm pretty sure there is nothing to read and that's why recv is blocking your program. 对于您的特定问题,我很确定没有什么要阅读的,这就是recv阻止您的程序的原因。 Make it sure that your client really sends something successfully. 确保您的客户确实发送了一些成功的邮件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM