网络TCP编程

关于TCP主要结构

TCP针对解决的问题就是两个主机之间的进程间交互;他的模型分为服务器端和客户端:服务器端使用两种套接字,一种是监听套接字,用于监听客户端申请的连接;另外一种就是连接套接字,用于监听套接字监听到客户端连接套接字申请后创建连接之后用于与客户端通信的套接字。客户端的话只用连接套接字,连接服务器端的监听套接字并与服务器连接套接字进行通信。

关于listen接口和缓冲队列

调用listen接口会为监听套接字创建半连接队列和全连接队列,关于这两个队列的信息可以参考accept、连接队列 进行了解。

关于accept接口和三次握手

accept接口和三次握手过程并没有关系,三次握手在内核层完成的,完成三次握手的连接会存放在内核的全连接队列中,accept是从该队列中提取出来一项。因此没有使用accept客户端依旧可以正常连接,但是该情况下是无法进行通信的。

这个过程有兴趣的可以使用wireshark抓包测试。

单服务器多客户端模型:

1.多线程方式

对于每个accept获取到的连接都使用一个线程将其分配支持,以支持客户端并发。

优点:逻辑简单

缺点:就是系统的线程资源有限,内存资源也有限,每增加一个连接增加一个线程将导致系统内存大量消耗;不适合大量客户端情况。也就是C10K问题,多线程的方式可以并行支持客户端数量级大约在万级别。

2.IO多路复用

a.select

优点:IO的多路复用的效果

缺点:select本身的数据结构限制了最大监控数量,太多客户端连接就无法正常使用了。另外select在有条件满足时需要使用轮询方式检测状态,且在用户层和内核层之间要传递fd_set数据,增加了处理成本。

注意:select返回结果会将传入的fd_set修改,因此需要增加一个fd_set类型的返回变量,不需要监控的write直接设置NULL即可。

还有就是如果使用了select的写缓冲检测,他并不能在连接完成后就直接添加检测,而应该设置在需要发送的时候再进行检测,否则就会由于写资源一直可用导致进入程序死循环。

如果希望突破C10K,那么可以增加线程,然后在每个线程中进行select。另外套接字描述符在使用上也是个文件描述符,而文件描述符linux系统上的话在全局和每个进程方面都有限制,需要进行对应参数设置。

b.poll

poll的原理和select差不多,只不过将select的三个监控对象组装成一个结构体。当然,由于是使用了结构体,数组的大小就可以由用户来定义,因此也就突破了select的1024的限制。

原理上和select一样,也是在满足检测条件的情况下进行轮询,另外也需要进行数据的传入传出。

c.epoll

原理不一样,select和poll在内核中是轮询文件描述符状态,然后设置返回状态,并且返回给应用层之后应用层也需要轮询获得满足条件的描述符;而epoll通过回调函数的形式直接将就绪描述符插入在双向链表中,并且将就绪队列以链表的形式返回给应用层,省去了select和poll的轮询过程;

另外就是内核层和应用层之间的数据交互:select、poll需要将读写集传入传出;而epoll将设置数据直接存储在内核缓冲中,通过命令的方式对目标描述符进行检测设置,最终返回的数据是将双向链表中的就绪项复制到epoll_wait传入的数组中,另外这个数组不需要一次就复制完,一次没有复制完毕会在下一周期继续复制,然后处理。

另外就是epoll_event::data,这个里面的数据和触发epoll_wait没有关系,他是提供给用户层的一个接口,用于存储用户层对应于epoll_event的绑定数据。

关于TCP的通信与发送缓冲:

发送缓冲的话,TCP层,也就是内核本身实现的功能就是保证TCP流有序,安全到达目的端,因此如果中途发生类似丢包问题的话,那么TCP缓冲区资源就会一直被占用,直到对端安全的完成数据接收。

关于recv和read接口:

read / write和recv / send接口上表现为多一个参数 -- flag;

当recv / send的flag设置为0时,他们的表现和read / write没有区别;

对于recv : 

MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式

MSG_OOB:指明发送的是带外信息

MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失

MSG_WAITALL:通知内核直到读到请求的数据字节数时,才返回。

对于send : 

MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表

MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式

MSG_OOB:指明发送的是带外信息

另外,关于read使用上的一点注意点:

设置读取大小size为0,那么read也会返回0,不管是对应的文件描述符是不是阻塞的。

posted @ 2022-03-27 15:20  呵哈呵  阅读(18)  评论(0)    收藏  举报