【问题标题】:Socket programming - function : bad file descriptor套接字编程 - 功能:错误的文件描述符
【发布时间】:2018-01-18 15:04:08
【问题描述】:

我在尝试创建以下代码的函数时遇到问题,因此我可以重用它来轻松创建新套接字:

if ( (socket_fd = socket(AF_INET, SOCK_DGRAM, 0) ) < 0 ) /* return 1 if okay */
  {
    printf("ERROR opening socket ... 1");
  }

  serv_addr.sin_family = AF_INET; /*Define the domain used*/
  serv_addr.sin_port = htons(PORT); /*Declare port #PORT to be used*/
  serv_addr.sin_addr.s_addr = inet_addr("INADDR_ANY"); /*Permit any incoming IP address by declaring INADDR_ANY*/

  setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv , sizeof (struct timeval)); 

// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(AF_INET, SERVER, &serv_addr.sin_addr)<=0) 
  {
    printf("\nInvalid address/ Address not supported \n");
    return -1;
  }

if (connect(socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
  {
    printf("\nConnection Failed  \n");
    return -1;
 }
else
{
  printf("\n Connected \n");
}

上面的代码对我来说很好用,但是当我尝试创建一个简单的函数来重新组合它时,当我尝试使用 sendto() 发送 cmd 时出现错误 bad file descriptor

这是我的功能:

void open_socket( int s_fd, struct sockaddr_in s_addr, struct timeval tv, char* ID_IP, int ID_Port)
{

    if ( (s_fd = socket(AF_INET, SOCK_DGRAM, 0) ) < 0 ) /* return 1 if okay */
      {
        printf("ERROR opening socket ");
      }

      s_addr.sin_family = AF_INET; /*Define the domain used*/
      s_addr.sin_port = htons(ID_Port); /*Declare port #PORT to be used*/
      s_addr.sin_addr.s_addr = inet_addr("INADDR_ANY"); /*Permit any incoming IP address by declaring INADDR_ANY*/

      setsockopt(s_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv , sizeof (struct timeval)); /*config du timeout*/

    // Convert IPv4 and IPv6 addresses from text to binary form
    if(inet_pton(AF_INET, ID_IP, &s_addr.sin_addr)<=0)
      {
        printf("\nInvalid address/ Address not supported \n");
      }

    if (connect(s_fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0)
      {
        printf("\nConnection Failed \n");
     }
    else
    {
        printf("Socket Connected\n" );
    }

}

我尝试用 :

来称呼它
 //SOCKET 1
  struct sockaddr_in serv_addr; /* Server Socket address structure*/
  int serv_len=sizeof(serv_addr);
  int socket_fd;

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

  //FUNCTION CALL
  open_socket( socket_fd, serv_addr, tv, SERVER, PORT);

错误出现在这里:

 //send the message
        if ( (sendto(socket_fd , cmd_final, BUFFLEN , 0 , (struct sockaddr *)&serv_addr, serv_len) ) < 0 )
        {
          perror("ERROR > send cmd failed  : ");
          return 0;
        }

我很确定我搞砸了一些关于指针的基本知识(我是初学者,几年没有用 C 编码)......我尝试了很多不同的东西,但找不到任何解决方案。我会很感激一些帮助或在哪里寻找!

非常感谢!

【问题讨论】:

    标签: c linux sockets udp


    【解决方案1】:

    您调用open_socket 的方式与您想的不一样。您将 socket_fd 作为参数传入,因此函数不会更新它。您确实应该将新创建的套接字作为返回值返回 - 这样您就可以通过返回 -1 来处理错误,您的调用代码可以检查并采取相应的行动。

    int open_socket( struct sockaddr_in s_addr, struct timeval tv, char* ID_IP, int ID_Port)
    {
        int s_fd;
        // Code goes here, returning -1 if it fails at any point
        return s_fd;
    }
    

    然后这样称呼它

    socket_fd = open_socket(serv_addr, tv, SERVER, PORT);
    if(socket_fd==-1)
    {
        // Handle failure to create socket
    }
    

    【讨论】:

      【解决方案2】:

      函数open_socket() 采用s_fd 按值。也就是说,当函数被调用时:

      open_socket(socket_fd, serv_addr, tv, SERVER, PORT);
      

      上面的函数调用获取socket_fd 的副本,作为参数传递给函数参数s_fd。在函数内部,这个副本是用socket() 获得的文件描述符而不是函数调用处的变量socket_fd 写入的。

      C 编程语言为您提供了两种机制来取回在open_socket() 中创建的文件描述符:

      • 通过引用传递参数:调用者必须提供一个指向将存储文件描述符的变量的指针:

        void open_socket(int *s_fd, /* ... */);
        
      • 将文件描述符作为函数调用的返回值返回:

        int open_socket(/* ... */);
        

      我建议你坚持使用后者,因为界面更类似于系统调用socket() 提供的界面。当发生错误时,您可以简单地返回 -1 来表示错误,因为每个文件描述符都是非负数(即:>= 0)。

      由于您在 open_socket() 内部设置结构 sockaddr_in,因此也无需通过 s_addr 参数传递它。

      int open_socket(struct timeval tv, const char* ID_IP, int ID_Port)
      {
          int s_fd;
          struct sockaddr_in s_addr;
      
          if ((s_fd = socket(AF_INET, SOCK_DGRAM, 0) ) < 0 ) {
              printf("ERROR opening socket ");
              goto err;
          }
      
          s_addr.sin_family = AF_INET; /*Define the domain used*/
          s_addr.sin_port = htons(ID_Port); /*Declare port #PORT to be used*/
          s_addr.sin_addr.s_addr = inet_addr("INADDR_ANY"); /*Permit any incoming IP address by declaring INADDR_ANY*/
      
          if (setsockopt(s_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv , sizeof (struct timeval))) /*config du timeout*/
              goto err_close_socket;
      
          // Convert IPv4 and IPv6 addresses from text to binary form
          if (inet_pton(AF_INET, ID_IP, &s_addr.sin_addr) != 1) {
              printf("\nInvalid address/ Address not supported \n");
              goto err_close_socket;
          }
      
          if (connect(s_fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) {
              printf("\nConnection Failed \n");
              goto err_close_socket;
          } 
      
          printf("Socket Connected\n" );
      
          return s_fd; // return the socket
      
      err_close_socket:
          close(s_fd);
      
      err:
          return -1; // error
      }
      

      如果在创建套接字后发生错误(即:在成功调用socket() 之后)套接字必须是close()d,然后才能从函数open_socket() 返回,否则会有资源泄漏。这是使用goto 语句是个好主意的少数情况之一:跳转到通用代码进行错误处理。

      【讨论】:

      • 我理解socket_fd 的错误,非常清楚,谢谢!但是,由于我想创建多个套接字,所以我有://SOCK 1 struct sockaddr_in serv_addr; int serv_len=sizeof(serv_addr); //SOCK 2 struct sockaddr_in serv_addr_2; int serv_len2=sizeof(serv_addr_2); 所以我认为我需要将s_addr 作为参数传递,对吗?因为在更正函数时我仍然有“协议不支持的地址系列”错误。我没有提到但char *ID_IP 和 int ID_Port 是不变的。替换为`#define SERVER "192.168.0.10"` & #define PORT 3000
      • @HacHac 你不需要传递s_addr 因为你已经通过参数SERVERPORT 参数化了它的内容,传递给open_socket() 的参数@987654351 @ 和 ID_Port,分别。它适用于我的系统。您如何调用更正后的函数?
      • 我这样调用函数socket_fd_2 = open_socket( tv, IP_2, PORT_2); 这个调用工作正常但是当我sendto(socket_fd_2 , cmd_final, BUFFLEN , 0 , (struct sockaddr *)&amp;serv_addr_2, serv_len_2) 时我收到一个关于错误协议地址的错误。尝试很多不同的东西,论点....无法弄清楚...
      • @HacHac 如果你打算使用sendto() 而不是send(),为什么你实际上是“connect()ing”UDP 套接字?
      • 你是对的,send() 可以正常工作......但我的印象是为了达到我的目标而努力,而我认为这相对简单,但也许我混合了这些概念。因为如果替换 bind 而不是 connect,我得到一个绑定错误。我有一台计算机必须在 UDP 中发送(首先)和接收(发送订单后)5 台设备(具有自己的 IP、端口和大量订单......)的数据。如果我做错了,也许你可以给我一个简单的代码示例(只是原型或大纲)。非常感谢详细的帮助,真的让我明白了!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-28
      • 2021-12-20
      • 1970-01-01
      • 2011-10-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多