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

Socket Programming C/C++ - recv function hangs in Server

本文关键字:函数 服务器端 挂起 recv 编程 c++ 套接字      更新时间:2023-10-16

我在使用recv函数时遇到问题

我有一个应用程序,从客户端发送一些数据,这些数据被服务器接收&发送基于数据的响应。

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

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

服务器代码:

   // Request Winsock version 2.2
   fprintf(stderr,"Performing WSAStartupn");
   if ((retval = WSAStartup(0x202, &wsaData)) != 0)
   {
      fprintf(stderr,"FAILED with error %dn", retval);
      WSACleanup();
      return -1;
   }
   else
   {
      printf("OKn");
   }
   if (port == 0)
   {
      Usage(argv[0]);
   }

   /* open socket connection */
   printf("Opening socketn");
   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 %dn", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {   
      printf("OKn");
   }
   // 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 socketn");
   if (bind(listen_socket, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR)
   {
      fprintf(stderr,"bind() failed with error %dn", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {  
      printf("OKn");
   }
   // 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 socketn");
      if (listen(listen_socket,5) == SOCKET_ERROR)
      {
         fprintf(stderr,"listen() failed with error %dn", WSAGetLastError());
         WSACleanup();
         return -1;
      }
      else
      {
         printf("OKn");
      }
   }

//Perform Applcation task
//initisations
   printf("Server is listening and waiting for a connectionnon port %d, protocol %sn",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 %dn", WSAGetLastError());
            WSACleanup();
            return -1;
         }
         else
         {   
            printf("OKn");
            printf("accepted connection from %s, port %dn", 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 %sn", inet_ntoa(from.sin_addr));
      }

      if (retval == SOCKET_ERROR)
      {
         fprintf(stderr,"recv() failed: error %dn", WSAGetLastError());
         closesocket(msgsock);
         return -2;
      }
      else
      {
         printf("OKn");
      }
      if (retval == 0)
      {
         printf("Client closed connection.n");
         closesocket(msgsock);
      }
      else
      {
         printf("Received %d bytes, data "%s" from clientn", retval, Buffer);
      }
      printf("Processing Datan");
      if (!stricmp(Buffer, "exit"))
      {
         wsprintf(AckBuffer,"ACK");
         executeServer = 0;
      }
      else
      {
        // Perform use task here based on recieved data
      }

      printf("Sending answer to clientn");
      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 %dn", WSAGetLastError());
      }
      else
      {   
         printf("OKn");
      }
      /* close TCP connection */
      if (socket_type != SOCK_DGRAM)
      {
         closesocket(msgsock);
      }

   }
   printf("terminating servern");
   closesocket(msgsock);
   WSACleanup();
客户机代码:

 fprintf(stderr,"Performing WSAStartup");
   if ((retval = WSAStartup(0x202, &wsaData)) != 0)
   {
      fprintf(stderr,"WSAStartup() failed with error %dn", retval);
      WSACleanup();
      return -1;
   }
   else
   {
      printf("OKn");
   }
   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 %dn", server_name, WSAGetLastError());
      WSACleanup();
      exit(1);
   }
   else
   {
      printf("OKn");
   }
   // Copy the resolved information into the sockaddr_in structure
   printf("Opening socketn");
   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 %dn", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {
      printf("OKn");
   }
   // 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: %dn", WSAGetLastError());
      WSACleanup();
      return -1;
   }
   else
   {  
      printf("OKn");
   }
   /* 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("OKn");
   }
   printf("Receiving status from servern");
   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("OKn");
   }

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

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

(更新)

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

(更新2)

首先,你的服务器代码似乎关闭连接一旦recv读的东西。由于TCP不关心数据边界,所以不能直接关闭连接。请记住,客户机中的一个send调用可能需要使用TCP在服务器中调用多个recv,反之亦然。

对于你的具体问题,我很确定没有什么可读的,这就是为什么recv阻止你的程序。请确保您的客户端确实成功发送了一些内容。