浅析tcp中select使用(思路源于工作)

本文探讨了在TCP服务端使用select进行连接管理和数据收发的流程,特别是在Route模块中的应用。通常流程是socket->bind->listen->select->accept->read/write,但Route模块采取先accept再select的方式,确保在接收数据前检查描述符状态。当连接数增加时,select效率降低,解决方案是启动多个Route进程并级联,但这种方式受限于最大连接数和线程资源。
摘要由CSDN通过智能技术生成

        虽然说poll和epoll要比select好很多,但是还是有很多地方select稳健运行着,而且select相关的资料也多。接下来就简单阐述下工作中route模块对这些的使用。

         在服务端使用tcp的一般流程是:socket->bind->listen->select->accept->read/write

         listen函数的第二个参数backlog值是表示期待连接的描述符的个数,假设传5,实际放列队的个数会稍微大于5,不同操作系统有不同的实现。放到三次握手流程里说,就是client发了个SYN过来和client发SYN+ACK,对应的server端状态是SYN_RECV和ESTABLISED。如果队列满了,client端调用connect请求就会返回-1。如果backlog不知道用什么值,可以用系统函数getenv("LISTENQ")取环境变量中设置的值。这个函数没有超时设置,不是阻塞,所以监控是否有连接请求是内核做的事情,存放描述符到队列也是内核做的,这个函数也就是触发内核要做的事情。

        接下来用select函数来获得可用的描述符。在收到connect的请求时,驱动通知内核,内核完成3次握手后把描述符放入数组中,select遍历数组就能拿到该描述符了(linux系统环境变量一般设置这个数组的大小是1024)。然后调用accept接收一个套接字中已建立的连接。这个连接是双全工管道,既可以读也可以写。但是,工作中有些模块不是这么用的。以工作中的Route模块为例,它是先accept,再select,总觉得哪里不对,为啥?因为跟教科书中不符。其实,仔细分析下,也是对的。理由如下:

       1.在listen之后,内核就在收集客户端连接请求。accept去接收一个已建立的连接,如果连接建立成功后,即长连接了,就创建一个新线程用于收发数据。每次收数据之前都会使用select来校验下描述符是否可用,如果是断开状态,就不收数据。

       2.如果select->accept后建一个线程去处理收发数据,那么recv数据时候就不知道描述符是否还是可用的了。如果接着再调用selec也是可以的。

       这块业务看似是一个线程一个链接,其实是一个线程多个tcp连接,还是用到了I/O复用。主线程接收N个客户端的连接,然后每个连接建立一个线程去处理收发数据。

      缺点是这个Route模块是单进程的,linux下最多可以接受1024个连接。如果修改环境变量把值变大,在连接数很大的时候,select会比较慢,影响效率。现在的方案都是在环境用中起好几个route程序,并做级联对应。而且处理收发数据的线程相当于是阻塞的。还有个缺点是一个系统所能创建的线程数量有限,而且线程在cpu中频繁切换也是耗费资源,达到某个阀值后性能就大大降低了。所以后来又做了个升级版。

     Route模块大致结构图如下:

                                     

主要就是解析数据然后转发。

# select的一个简单用法
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include<netinet/in.h>
#include <iostream>
using namespace std;

const int PORT = 9000;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值