目录
tcp模块如何处理数据包:此时应用程序已经处于read阻塞阶段
服务端等待连接模块
socket建立连接的过程是交换控制信息。建立了套接字之后并不能马上使用,需要录入控制信息,并且建立连接,形成一个完整的套接字,才可以收发数据,收发数据也是通过Socket组件,委托给协议栈去执行的。
1.服务端进程创建套接字-创建套接字阶段
描述符=socket(使用ipv4,使用tcp,...) //此过程,开辟了一个内存空间,返回的是这块空间的文件描述符,既分配用于套接字的内存空间
2.将套接字设置为等待连接状态-等待连接状态
bind(描述符1,端口号等,...) //把端口号写入套接字中,因为客户端在发起连接的时候,需要指定服务端的端口号
listen(描述符1,...) //向套接字写入等待连接状态这一控制信息
向套接字写入bind所指定的端口号,并等待该端口的连接
3.接受连接-接受连接状态
描述符2=accept(描述符1,...)
/**
等待包到达的状态,客户端一旦到达,会给描述符1对应的等待连接的套接字复制一个副本,然后把连接对象等控制信息写入新的套接字中。当accept结束之后,程序员会使用新的socket与客户端通信,由这个模块执行与客户端的通信操作。原来的那个套接字会继续等待连接的状态存在。连接的过程表面上看是控制流转移给内核,不仅仅是三次握手,同时还建立了收发数据的缓冲空间。
端口号是识别套接字的,如果一个端口号对应多个套接字,协议栈看到tcp头部的接收方端口号,无法判断是把包交给哪个套接字,所以要是用4个信息判断,srcip port,descip port。为何有了端口号区分套接字,还需要描述符呢,那是因为一个端口号可以对应一群套接字,而一个描述符只对应一个socket。
*/
...
调用客户端通信模块(描述符2)
控制流转移到与客户端通信的模块(描述符2)
返回accept;
...
服务端与客户端通信的模块(描述符2)
...
接收数据长度=read(描述符2,接收缓冲区,接收缓冲区长度)
...
根据请求消息的内容做进一步处理
...
write(描述符2,发送数据,发送数据长度)
...
close(描述符2)
结束
服务端的收发操作之如何建立连接
服务端处于accept的阻塞状态,以下步骤用于解救这种状态:
1.网卡把接收的信号转换成数字信息:网卡的mac模块和FCS模块
到达服务器的网络包本质还是光信号或者电信号
1.1 根据信号还原数字信息
1.2 根据包末尾的侦校验序列FCS来校验错误,错误的直接丢掉,不用担心,tcp会发现并让重传
1.3 还原后的数字信息被保存在网卡内部的缓冲区
2.网卡通过中断将网络包到达的事件通知给cpu
3.cpu暂停当前的工作,切换到网卡的任务
4.网卡驱动开始运行,从网卡的缓冲区中将接受的包读取出来,并根据mac头部的以太类型字段判断协议的类型,并调用负责处理该协议的软件,以太类型的值一般是ip协议,因此会调用tcpip协议栈,并把包转嫁给它。
5.网卡驱动切回os,由os调用协议栈,协议栈继续执行接收工作。
6.IP模块的接收工作
6.1 检查ip头部,格式是否规范,检查ip地址,看是否发给自己的,如果服务器启用了类似路由器的包转发功能,对于不是自己的包,会像路由器一样,根据路由表对包进行转发。
6.2 确认包是发给自己的以后,检查包有没有被分片,检查ip头部的内容就可以知道。
6.3 如果是分片的包,则把包暂时存放在内存中,等所有分片都到达之后再组装起来还原成原始包。如果没有分片,则直接保留接收时的样子,不需要重组。到这里就完成了包的接收。
6.3 检查ip头部的协议号字段,把包装给相应的模块,tcp还是udp.
7 TCP模块如何处理连接包
7.1 检查tcp头部的控制位SYN=1表示这是一个发起连接的包,tcp模块会执行接受连接的操作
7.2 检查接收方的端口,确认在该端口上有咩有与接收方端口号相同且处于等待连接的套接字,没有则向客户端返回错误通知的包。ICMP消息。
7.3 存在等待连接的套接字,为这个套接字复制一个新的副本,并且把src的ip 端口 序号初始值,窗口大小等必要参数写入这个套接字。
7.4 分配这个套接字使用的,用于发送缓冲区和接收缓冲区的内存空间
7.5 生成代表确认接收的ack号,用于从服务端向客户端发送数据的序号初始值,表示接收缓冲区剩余容量的窗口大小,并用这些信息生成tcp头部,委托ip模块发送给客户端。
7.6 客户端返回表示接收确认的ack号,当这个ack号返回服务端之后,连接操作就完成了。
7.8 服务端程序这时还是accept的暂停状态,当把新的套接字的描述符转交给服务器程序以后,服务器程序会恢复运行
tcp模块如何处理数据包:此时应用程序已经处于read阻塞阶段
1.tcp 收到额包SYN!=1,那么这是一个已经连接好的包
2.找到对应哪一个套接字:
根据四个信息匹配到套接字
3.确定数据收发是否正常:tcp模块会对比该套接字中保存的数据收发状态和tcp头部的信息是否匹配
根据套接字中保存的上一个序号和数据长度计算下一个序号,并检查与收到包的tcp头部序号是否一致,如果一致,说明包正常到达了服务器,没有丢失。
4.没有丢失,tcp模块会从包中提取出来数据,保存在接收缓冲区,与上次收到的数据块连接起来,这样数据就会被还原成分包之前的状态了。
拼合操作在每次包收到时候都会进行,而不是等待所有数据包全部接收完毕之后再统一拼合的。
5.数据进入到接收缓冲区后,tcp模块会生成确认应答的tcp头部,并根据接收包的序号和数据长度计算出来ack号,委托ip模块发送给客户端
6.收到的数据块进入到接收缓冲区,表示数据包的接收操作告一段落。
7.应用程序会调用socket库的read获取到收到的数据,也就是说,数据被转交给应用程序
8.应用程序根据应用层的协议如mysql http ftp 来处理数据
我的疑问:粘包和差错控制,如何确定是一个新连接接还是一个已建立的?分片是ip做的,发送的过程呢?
TCP模块的断开操作:
1.断开操作可以由任何一端发起,比如从服务器端发起,应用程序调用Socket库的close组件
2.tcp模块生成一个控制位FIN=1的tCP头部,并委托ip模块发送给客户端
3.客户端收到之后,返回一个ack号
3.客户端调用close组件,生成一个FIN=1 的tcp头部发送给服务端
4 服务端返回ack号,这时断开连接操作就完成了
断开连接后,套接字会在一段时间后被删除。
-
客户端发送连接过程:
都是委托协议栈来干,外在体现是调用socket库的程序组件
前提是服务端一方已经创建好了套接字等待客户端向该套接字连接管道,当服务器进入等待状态,客户端就可以连接管道了。
描述符=Socket(使用ipv4,流模式,...)//返回一个描述符并且分配随机的端口
connect(描述符,服务器ip+port,...)//服务器的端口号可以确定服务器的socket,因为端口号是tcp和tcp之间标识套接字的对话,描述符是tcp和应用程序标识套接字的语言。描述符是内部识别socket的,端口号是让通信的另一方识别套接字的机制。//连接成功,携带者哪个随机端口,委托协议栈把本地套接字和服务器套接字连起来,也就是说把ip port信息都保存在套接字中,这个简历连接的过程,参考tcp三次握手
//ip地址只是指定了网络接入点
//描述符只是和委托创建套接字的应用程序交互时候使用的,并不是用来告诉网络连接的另一端的,端口号是为了让通信的另一端识别套接字的机制。
//套接字已经保存了已连接的通信对象的相关信息,所以只要通过描述符指定套接字,就可以识别出来通信对象,向其收发数据。
...
write(描述符,发送数据,发送数据的长度)//委托协议栈写
...
接收数据长度=read(描述符,接收缓冲区,...)//委托协议栈读,当消息放在接收缓冲区,等于已经转交给应用程序
//read操作需要指定用于存放接收到的响应消息的内存地址,这一地址又叫接收缓冲区
//接收缓冲区是位于应用程序内部的内存空间,因为只要把消息放到接收缓冲区,就等于已经转交给应用程序
close(描述符)//参考四次挥手 通过close组件调用协议栈进入断开阶段
...
1.客户端应用程序调用Socket库的组件创建套接字,控制流会转移到socket内部并执行创建套接字的操作,完成之后控制流会已移交回应用程序,标志就是返回一个描述符,应用程序会把这个描述符放在内存里,描述符是用来识别不同套接字的呦~~~
2.客户端管道连接到服务端管道上
3.数据送入套接字就可以完成数据收发//协议栈可以判断使用哪个描述符进行数据收发
4.断开管道并删除套接字
其实我认为关闭操作也是回收描述符,哈哈~
tcp调优参数:滑动窗口大小
1.发送时候会确认是MTU满了再发还是来了就发,这个时机属于tcp参数调优
2.ack的等待市场,也是tcp根据网络状态自己动态调整的,多久确认ack
3.更新滑动窗口大小的时机,是协议栈把数据拷贝到用户态内存之后
4.滑动窗口大小默认是接收缓冲区的大小
5.注意控制流转移的时机,什么时候io通道阻塞,什么时候控制流转移,哈哈
6.无论tcp委托ip干的事握手挥手的控制流交换还是发送的数据流,对于ip,无差别,ip就是把委托的东西打包送给对端,或者接受东西
7.如果没有网关,即路由器(上有地址转换表,路由表,mac-ip对应表),我们无法上外网,网关把内网地址和端口,转换成了网关的地址+随机端口,去访问外网
ARP:把包装发给同一个LAN的所有设备,属于IP层。路由器也是通过这个协议查询下一个转发目标的mac..路由器的作用就是包转发,交换机也是,路由器的工作是通过委托给交换机进行的。一个基于ip设计,一个基于以太网的局域网技术设计。
8.我觉得互联网就是一个个lan即局域网组成的,在局域网之上,加了路由,所以能够通过ip与网卡通信。网卡只是对计算机网络接入点的代表。对于被访问的服务器来说,他看到的源访问者就是网关。
话公网,看了刘超的趣谈网络协议,解惑了一点点,关于包从路由到另一个路由是怎样过去的,一个路由器就是一个县长,这个县长有自己管辖的网段,一个县,然后还会连接另外一个网段,即会switch到另外一个县,通过ARP就可以找到这个县的县长,因为根据ip目标就是县长,另外一个县的县长管辖着这个县的网段内的主机,到达他们县长手里,同样再switch到另外一个县境内,再ARP一下,啊哈~~~
"局域网的包如果发给外网,先到达路由器,路由器根据路由表决定下一跳的地址,因为路由器上装了多个网卡,会通过局域网2的网内ARP获取下一个路由的地址"
刚才说的是包在公网上的漂流过程,关于nat:
现在大家每家都有家用路由器,家里的网段都是192.168.1.x,所以你肯定访问不了你邻居家的这个私网的IP地址的。所以,当我们家里的包发出去的时候,都被家用路由器NAT成为了运营商的地址了
"很多办公室访问外网的时候,也是被NAT过的,因为不可能办公室里面的IP也是公网可见的,公网地址实在是太贵了,所以一般就是整个办公室共用一个到两个出口IP地址。你可以通过 https://www.whatismyip.com/ 查看自己的出口IP地址。"