read: Transport endpoint is not connected

调用 select() 相关函数编程时,委托内核对需要监测的文件描述符进行检测,检测的内容可以理解为两个方面:

  1. 用于的监听套接字文件描述符
  2. 用于通信的套接字文件描述符

不论哪种文件描述符对应的缓冲区发生改变,内核都会将文件描述符数组对应值置1。

问题来了
当只有监听文件描述符对应缓冲区发生变化,而通信文件描述符没变化时,调用 select() 会有返回值 1. 因为只有一个文件描述符发生变化。
而编写程序的逻辑是先判断是否是监听文件描述符发生变化,如果是,要通过其生成一个用于通信的文件描述符。此时其实我们这一轮的工作已经完成,直接下轮循环调用 select() 即可,也可对。但如果没有直接下轮循环,而是继续去进行遍历文件描述符数组进行读写操作的判断,并且遍历文件描述符数组是从 0 开始的,就会出问题:read: Transport endpoint is not connected

分析
这个问题是因为我们读取的是用于监听的文件描述符的缓冲区。因为我们这一轮循环只有一个文件描述符发生了变化,也进行了判断 发生变化的文件描述符就是监听文件描述符,接收连接 其实工作已经完成。结果继续遍历去读写数据,还是从文件描述符的头部 0 开始遍历,必然又会遍历到监听文件描述符为 1. 此时 read() 就会报错。

解决方案
方案1:
判断发生变化的文件描述符为 监听描述符 并完成连接业务后,进行一次判断,判断 select() 的返回值是否为 1,为 1 则说明这次委托检测只有客户端连接进来了,没有客户端往服务器端写数据。则和连接进来的客户端建立连接后将其加入下次监听行列就好了,就可以下次循环了。

if (FD_ISSET(lisfd, &tmp))
{
    // 找到连接进来的客户端对应的文件描述符,进行连接
    struct sockaddr_in cliaddr;
    int len = sizeof(cliaddr);
    int comfd = accept(lisfd, (struct sockaddr*)&cliaddr, &len);

    // 将用于通信的文件描述符 添加到需要委托内核检测的数组集合中
    FD_SET(comfd, &rdset);

    // 更新下次内核遍历时,需要遍历到的地方
    maxfd = comfd > maxfd ? comfd : maxfd;

    if (ret == 1)  // 加一个判断,满足直接下一轮循环
    {
        continue;
    } 
    
}

方案2:
如果不采用方案1,也就是不在 accept() 最后判断 select() 的返回值 是否等于1。则在读写遍历的范围上注意一下也可以。FD_SET 在设置监测文件描述符时每次都是将最小的没有被占用的文件描述符设置为1,所以监听文件描述符一定在通信文件描述符之前,我们只要将遍历范围的起始值设置为 lisfd + 1 即可。

 for (int i = lisfd + 1; i <= maxfd; i++)
 {
     if (FD_ISSET(i, &tmp))
     {
         // 说明这个文件描述符对应的客户端发来了数据
         // 读写数据
     }
     
 }

问题解决

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值