套接字 "Bad file desc." 一次成功后 write()/read()

Socket "Bad file desc." after one time successful write()/read()

这是一个带有 select ()` 的服务器实现和一个简单的客户端。它不是主应用程序,而是一个可能无法运行的示例。重要的是来自服务器的无限循环。

问题出现在这里:客户端发送一条(正确的)消息,从服务器接收到一个(正确的)应答,然后,当他发送另一条消息时,它收到错误:write():错误的文件描述符。

我检查了服务器,他没有关闭它。我怀疑问题出在 select () 或服务器架构中。

编辑: 如果相关,我会在 127.0.0.1 上测试我的应用程序。

服务器

        #include <sys/types.h>
        #include <sys/socket.h>
        #include <sys/time.h>
        #include <netinet/in.h>
        #include <unistd.h>
        #include <errno.h>
        #include <stdio.h>
        #include <arpa/inet.h>
        #include <string.h>

        #define PORT 2728

        extern int errno;       

        /* converting client address int char*  */
        char * conv_addr (struct sockaddr_in address)
        {
          static char str[25];
          char port[7];

          strcpy (str, inet_ntoa (address.sin_addr));   

          bzero (port, 7);
          sprintf (port, ":%d", ntohs (address.sin_port));  
          strcat (str, port);
          return (str);
        }


        int main ()
        {
          struct sockaddr_in server;    
          struct sockaddr_in from;
          fd_set readfds;   //clients which are ready to write  
          fd_set actfds;    //active clients    
          struct timeval tv;        
          int sd, client;       
          int optval=1;             
          int fd;           
          int nfds;      //max number of descriptors        
          int len;          


          if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
            {
              perror ("[server] Error:socket().\n");
              return errno;
            }

          /*SO_REUSEADDR  option*/ 
          setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,&optval,sizeof(optval));


          bzero (&server, sizeof (server));


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


          if (bind (sd, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
            {
              perror ("[server] Error:bind().\n");
              return errno;
            }


          if (listen (sd, 5) == -1)
            {
              perror ("[server] Error:listen().\n");
              return errno;
            }


          FD_ZERO (&actfds);        
          FD_SET (sd, &actfds);     

          tv.tv_sec = 1;        
          tv.tv_usec = 0;


          nfds = sd;

          printf ("[server] Waiting at port %d...\n", PORT);
          fflush (stdout);

          /* serving clients... */
          while (1)
            {

              bcopy ((char *) &actfds, (char *) &readfds, sizeof (readfds));


              if (select (nfds+1, &readfds, NULL, NULL, &tv) < 0)
            {
              perror ("[server] Error:select().\n");
              return errno;
            }


              if (FD_ISSET (sd, &readfds))
            {
              /* preparing client structure */
              len = sizeof (from);
              bzero (&from, sizeof (from));


              client = accept (sd, (struct sockaddr *) &from, &len);


              if (client < 0)
                {
                  perror ("[server] Error:accept()\n");
                  continue;
                }

                  if (nfds < client) /* adjusting max value */
                    nfds = client;


              FD_SET (client, &actfds);

              printf("[server] Client with %d descriptor and %s address connected.\n",client, conv_addr (from));
              fflush (stdout);
            }

              /* checking if any client is ready so 'speak' */
              for (fd = 0; fd <= nfds; fd++)    
            {

              if (fd != sd && FD_ISSET (fd, &readfds))
                {   

                       //receive message from client
                       //do something 
                       //sent back result

                }
            }
                            /* for */
            }               /* while */
        }               /* main */

客户端

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <errno.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <netdb.h>
    #include <string.h>

    extern int errno;


    int port;

    int main (int argc, char *argv[])
    {
      int sd;           
      struct sockaddr_in server; 
      char msg[100];    

      if (argc != 3)
        {
          printf ("[client] Sintax: %s <server_address> <port>\n", argv[0]);
          return -1;
        }

      port = atoi (argv[2]);

      if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
        {
          perror ("[client] Error:socket().\n");
          return errno;
        }


      server.sin_family = AF_INET;
      server.sin_addr.s_addr = inet_addr(argv[1]);
      server.sin_port = htons (port);


      if (connect (sd, (struct sockaddr *) &server,sizeof (struct sockaddr)) == -1)
        {
          perror ("[client]Error: connect().\n");
          return errno;
        }


    while(1)/*meaning untill user doesn't close the client*/)
     {

        //here are the functions used to communicate with server. Same in server.

      if (write (sd, msg, 100) <= 0)
        {
          perror ("[client]Error:write().\n");
          return errno;
        }


      if (read (sd, msg, 100) < 0)
        {
          perror ("[client]Error:read().\n");
          return errno;
        }

     }//while

      close (sd);

  }//main

Source

服务器和客户端的代码似乎都缺少设置 'SO_KEEPALIVE' 选项。 建议使用类似的东西:

int keepAlive = 1;


if( 0 > setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE,
               &keepAlive, (socklen_t)sizeof(keepAlive)) )
{ // then, setsockopt failed
    // process error
}

当前代码通过以下方式创建套接字:

if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{ // then socket failed
    // process error
}

建议使用:

if ((sd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{ // then socket failed
    // process error
}
while(/*user doesn't close client*/)
 {
    //here are the functions used to communicate with server. Same in server.

  if (write (sd, msg, 100) <= 0)
    {
      perror ("[client]Error:write().\n");
      return errno;
    }
  if (read (sd, msg, 100) < 0)
    {
      perror ("[client]Error:read().\n");
      return errno;
    }
 }
  close (

如果此循环在使用 EBADF 的第二次写入中失败,显然这不是真正的代码,并且显然收盘价高了一行,在循环内部,正如其缩进所示。