提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
最近回顾了一下 之前项目做到的关于网络通信的知识,关于一些网络IO复用的知识,在以下做了一个笔记
提示:以下是本篇文章正文内容,下面案例可供参考
IO即为网络I/O,多路即为多个TCP连接,复用即为共用一个线程或者进程,模型最大的优势是系统开销小,不必创建也不必维护过多的线程或进程
IO复用的方式select /poll/epoll 。
他们共同的特点是对整个网络套接字进行监听 。
*在系统调用里面如何做?
poll (fds,len,timeout(轮询)) 去从内核中处理的过程
1 fds 从用户空间copy到内核 copy的是n个fd 一个fd代表了客户端与服务端的链接套接字
2 for循环去遍历
3 内核空间再copy到用户空间,去应用。
! io数量不多的情况下 和多线程多进程的情况下用select
io数量不多的情况下 和多线程多进程的情况下用select
aio 异步:
Linux有用户空间和内核空间 之间要用0x80中断,是大量的不适合aio
Aio对磁盘文件比较使用。
1 dpdk抓包
2 arp icmp udp
三次握手 客户端和服务端的版本
网络传输
四次挥手 主动和被动
慢启动 滑动窗口 拥塞控制
定时器
Tcp相较而言比较稳定 可以保证顺序的传输,
延迟ACK:收到包时进行ack延迟 把包与包之间进行按照顺序传输
举一个例子
把客户端当成运送方,运输10箱货物,
服务端是接收站台,其中ACK就是接收站台所拟定的一个协议;
运送方 在传输时候
情况1
运送了 1号 2号 4号 10号的货物, 此时接收站根据ACK去判断 1 2号是连续的 4号和10号不符合规定,返回一个消息(1号和2号是符合的)返回一个2+1 =3 的值 。
运送方接收到后会重新从3号开始发送数据 直到全部发送完为止,保证了顺序性。
那么有人会问 4号和10号接收了么? 答 是没有的,不会接收不符合顺序的包。
就此上情节 根据ack延时的机制 ,接收方不能一直求接收,上班还要摸鱼才行,设置了一个时间 每过200ms 去取一次包 做检测。
也就有了下列的定义。
一、ACK定义
TCP协议中,接收方成功接收到数据后,会回复一个ACK数据包,表示已经确认接收到ACK确认号前面的所有数据。
ACK字段长度为32位,能表示0~2^32-1之间的值。
二、ACK作用
发送方在一定时间内没有收到服务端的ACK确认包后,就会重新发送TCP数据包。发送方收到了ACK,表明接收方已经接收到数据,保证了数据的可靠达到。
三、ACK机制
接收方在接收到数据后,不是立即会给发送方发送ACK的。这可能由以下原因导致:
1、收到数据包的序号前面还有需要接收的数据包。因为发送方发送数据时,并不是需要等上次发送数据被Ack就可以继续发送TCP包,而这些TCP数据包达到的顺序是不保证的,这样接收方可能先接收到后发送的TCP包(注意提交给应用层时是保证顺序的)。
2、为了降低网络流量,ACK有延迟确认机制。
3、ACK的值到达最大值后,又会从0开始。
四、ACK延迟确认机制
接收方在收到数据后,并不会立即回复ACK,而是延迟一定时间。一般ACK延迟发送的时间为200ms,但这个200ms并非收到数据后需要延迟的时间。系统有一个固定的定时器每隔200ms会来检查是否需要发送ACK包。这样做有两个目的。
1、这样做的目的是ACK是可以合并的,也就是指如果连续收到两个TCP包,并不一定需要ACK两次,只要回复最终的ACK就可以了,可以降低网络流量。
2、如果接收方有数据要发送,那么就会在发送数据的TCP数据包里,带上ACK信息。这样做,可以避免大量的ACK以一个单独的TCP包发送,减少了网络流量
我们知道tcp有三次握手,那么这三次握手的目的到底是什么?
有人说 要先确定看看是否能够正确的传输网络信息,也有人说三次握手很麻烦,甚至有人去模仿了三次握手。
通过三次握手可以计算 RTT (客户端和服务端之间传递的时间(网络的回传))。
RTT0 前面一次的时间
RTT1 下一次的值
RTT= 0.9RTT0+0.1RTT1
慢启动也就是在这期间 从1开始发送数据
1 2 4 8 16 32
查看RTT 是否满足要求 注:32 是顶峰值。
如果达到32巅峰的时候 没有回复超过了RTT的值 那就回到之前的中间值 24开始
如果还是没稳定的话,以此类推 重复二叉 达到一个稳态。
拥塞控制 就是计算网络稳态时间
epoll有什么作用?
我们假设一个场景
状态:多个客户端连接
条件: 只有一个客户端发送
问题:recv接收比较费时间
解决:recv之前用一个epoll进行过滤
等效 :
快递员取快递 是服务端
小区用户为客户端, 挨家挨户的取就是recv
建立一个丰巢类似的快递站(epoll)
epoll_create(bool) 创建快递站的队列大小 现在用链表代替就可以
epoll_ctl() 对于小区用户新增或者搬走或者修改楼层所用
epoll_wait(epoll的fd,buf,bufsize,多久取) 快递员对快递站多久取一次
epoll的代码过程
1. 当一个新的客户端用fd送连接服务端时候调用epoll_ctl()去为小区楼创建一个新的用户
2. 新的客户端发送 你好,相当于用户寄快递,把快递发送到蜂巢柜子种
也就把fd发送到epoll的就绪队列里面去 可以检测到有数据 让程序调用recv函数
单线程
无需加锁 无需抢占 多线程的问题无需解决
缺点:一些功能实现不了
比如:收到数据时要存入数据库 写入log中 这样的大量损耗时间都会影响线程的处理时间。
适用于纯内存的写法。
如何解决缺点
比如把存入数据库的操作放到队列中,主线程还是单线程 只不过开启了一些协助的线程来帮助主线程操作;
服务器宕机 重启之后 大量的客户端涌入进来重新连接?
问题:服务器的压力点 在于 大量的处理accept 接受连接。
解决办法:网络io多线程做法
单线程处理accept,多线程处理recv/send,recv的数据用队列开启线程处理(业务代码)
注意:多个线程不要共用一个fd 再A线程读的时候 B线程用到的时候有可能会出错(fd 内部为0);
仅仅作为自己 查看以往笔记使用