【问题标题】:Socket "Bad file desc." after one time successful write()/read()套接字“错误的文件描述。”一次成功后 write()/read()
【发布时间】:2015-03-01 15:41:31
【问题描述】:

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

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

我重新检查了服务器,但他没有关闭它。我怀疑问题出在 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

【问题讨论】:

  • 这一行:'extern int errno;'在errno.h文件中,这里不需要再定义了。
  • 您必须关闭套接字。这是真正的代码吗?你确定关闭不在循环内吗?
  • 您需要(最终)关闭原始套接字和由 accept() 返回的套接字。这一行:'while(/*user doesn't close client*/)' 应该是:while(1) ?*user doesn't close client*/
  • 我强烈建议您在通过 TCP/IP 套接字进行通信时使用 send() 和 recv(),而不是使用 write() 和 read()
  • @user3629249 你强烈建议为什么?

标签: c sockets client server


【解决方案1】:
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 写入时失败,显然这不是真正的代码,而且很明显,正如它的缩进所暗示的那样,在循环内部,close 高出一行。

【讨论】:

  • 是的,这不是真正的代码,关于您对 close(socket) 的观察:在循环中使用 close() 将在第一次发送接收后关闭客户端和服务器之间的连接......或我的意图是在客户端退出之前保持打开状态,而不是 close() - 在每次迭代时连接。
  • 我不知道这条评论是什么意思,除非你只是同意我的观点。
【解决方案2】:

服务器和客户端的代码似乎都缺少设置“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
}

【讨论】:

  • 所有好的建议,但似乎与实际问题无关。
  • SO_KEEPALIVEEBADF 无关。 -1
猜你喜欢
  • 2015-05-20
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 2019-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多