单线程读取多TCP Socket的低延迟可行方案咨询
针对你单线程处理两个TCP连接、优先低延迟的需求,相比轮询ioctl(FIONREAD)的方案,以下几种IO多路复用方案更合适,既能保证极低延迟,又能避免不必要的CPU消耗:
select()系统调用
这是类UNIX系统里最经典的多路复用方式,把两个socket加入读事件集合后调用select(),线程会直接阻塞,直到任一socket有可读数据时被内核唤醒。返回后只需遍历集合,处理有数据的连接即可。完全没有轮询空转的问题,延迟由内核直接触发通知,比主动轮询快得多,CPU消耗几乎可以忽略。poll()系统调用
作为select()的替代方案,poll()摆脱了fd_set的大小限制,使用起来更灵活。为每个socket创建struct pollfd结构并设置POLLIN事件,调用poll()后同样阻塞等待事件触发,返回后检查revents字段判断哪个socket可读。同样是内核被动通知模式,低延迟+低CPU占用,适配你的场景毫无压力。epoll()(Linux专属)
如果是Linux系统,epoll是更高效的选择,哪怕只有两个连接也能发挥其低延迟优势。先通过epoll_create()创建实例,再用epoll_ctl()给两个socket注册EPOLLIN事件,之后循环调用epoll_wait()阻塞等待就绪事件。可以选择水平触发(LT)模式(逻辑简单,适合少量连接)或边缘触发(ET)模式(进一步优化延迟,需注意读取完所有数据),同样是内核直接唤醒线程,无空转开销。
对比你最初的轮询方案:主动调用ioctl(FIONREAD)会导致线程不断空转,要么轮询间隔大了延迟高,要么间隔小了CPU占用飙升。而上面的多路复用方案都是被动等待内核通知,只有当有数据到达时才会唤醒线程处理,完美契合你"低延迟优先"的需求,同时CPU消耗也远低于轮询方式。
内容的提问来源于stack exchange,提问作者Pascal de Kloe

