继上一章介绍了应用程序调用socket的连线流程後,我们将透过协议栈内部的TCP/UDP消息处理来探讨作业系统是如何使用socket消息进行连线以及通讯双方如何使用TCP头部控制消息来互相确认连线状态
我们都知道应用程序会使用socket标识符代表一个本地端与服务端的沟通通道,当使用socket标示符进行操作时,就相当於将本地端与目标服务器的IP与端口号等控制消息交给下层,待操作系统要处理消息时,只要看一下该socket标识符对应的结构体中储存的消息就知道对方的连线资讯
有使用社交聊天软件吗?把socket当成一个聊天视窗,视窗中有双方的沟通讯息,例如姓名、位置、状态等... 其实socket就相当於聊天双方需要的连线消息,也可以说本地端socket就代表一个我跟某某某的聊天室窗,当然有些应用程序可以同时与多个应用程序**"聊天"**
我们可以利用windows cmd下的netstat指令来查看本机端的socket内容,毕竟没有实际操作案例其实还蛮难搞懂这种抽象观念,可以透过在cmd中下达netstat -ano得到类似下图的结果
我们看看PID编号为1580的程序的沟通状况,它使用TCP通讯协定,本机IP与端口号为192.168.0.14与8430,且已经和远端服务器完成连线ESTABLISHED,对方服务器IP与端口号分别为52.159.49.199与443
接着来看看PID编号为13348的程序,它使用UDP通讯协定,本机端显示0.0.0.0表示不绑定IP地址,远端服务器地址显示*:*,也是不绑定IP地址的状态,连线状态更是省略掉了,这里可以清楚看到TCP与UDP的差异
当应用程序调用socket()创建套接字时,作业系统会为套接字分配一个结构体空间好存放初始化连线消息,等到connect操作时将会把储存沟通消息的结构体做为参数供作业系统连线使用
客户端与服务器进行连线实际上是双方在交换连线控制消息。connect()操作的目的在於使用本地端socket告诉TCP连线资讯,而沟通双方在进行连线操作时是透过更改与查看TCP头部消息来确认每个步骤是否成功,因此接下来我们将大致介绍TCP头部格式
TCP头部主要储存两种消息,(1)客户端与服务器通讯流程需要的控制消息 (2)套接字给的连线消息
长度为16个bits,代表本地端应用程序端口号
长度为16个bits,代表对方服务器应用程序端口号
长度为32个bits,代表发送数据的封包序号。封包序号的意思是发送方告知接收方该封包的起始位元是占整个封包的第几个bit,要注意起始封包序号不次从1开始,而是在建立连接时乱数产生的。下一次的封包序号就是该次封包序号加上封包的长度(几个bits)
长度为32个bits,又称为ACK号,它代表接收方告知发送方下一次发送封包要从第几个bits开始
长度为4个bits,它代表TCP内嵌的资料从第几个bytes开始,也可以表示TCP头部消息的总长,使用的单位为4bytes,例如资料偏移位填入6则代表TCP头部总长为6 x 4 = 24 bytes
长度为4个bits,用来当作扩充用,实际应用时必须先设为0
长度为8个bits,这8个bits分别代表不同的控制消息
比特位 | 涵意 |
---|---|
CWR | CWR是使用於IP头部ECN的标志位,用於壅塞视窗控制 |
ECE | ECE也是使用於IP头部ECN的标志位,用来通知网路是否壅塞 |
URG | 紧急指标,若URG为1则表示该包资料需要紧急处理 |
ACK | 向对方表示接收到消息,除了客户端一开始的连接请求外,都设定成1 |
PSH | 是否马上传给上层应用,1表示立刻马上,0表示可以先传递给缓冲区,不用那麽急 |
RST | 设为1时代表TCP连线异常,需要强制切断连线 |
SYN | 设定为1时用来建立连线,由客户端发起连线操作 |
FIN | 通讯结束後若没有数据要传输,将FIN设为1断开连线 |
长度为16个bits,接收方用於告知发送方,无需等待确认可以一次发送的数据大小(後面关於视窗控制时会介绍)
长度为16个bits,用来检查封包在传递过程中是否发生错误
长度为16个bits,若控制位中的URG设置成1,则表示TCP资料从开头到紧急指标所指向的位置都是需要立刻处理的部分
长度可变,通常用来提升TCP通讯性能,是额外的可选字段
用来补足选项字段的byte数
上图显示两种不同的封包格式,这里主要探讨调用socket connect时的封包格式。我们可以把连接阶段的TCP头部看成是一个寻找服务器应用程序的地图。
应用程序透过connect参数将socket储存的消息传递给传输层,TCP模块会对这些消息进行处理并写进头部消息中,在连接初期TCP不会写入资料,所以整个资料封包仅只有头部消息
TCP模块完成对消息的初步封装後便会通知IP模块对该封包进行处理,然後再委托更下层进行封包的封装以及传送
TCP是面向连接的通讯方式,在进行连接请求前会先准备齐连线所需要的资料。通常连线请求由客户端发出,客户端必须确认服务端的IP地址与端口号,对於TCP头部消息,首先会随机产生一个乱数的发送数据序号,然後把SYN控制位设为1。紧接者客户端发出连接请求,并把状态改为SYN-SEND
当接收到客户端的连接请求後,服务器会解析TCP头部并找到等待中的服务器应用程序,并将双方的沟通消息写入服务器上的socket,与此同时服务器产生一个随机的发送数据序列号,并将客户端的发送数据封包序号加上1并且田进自己的接收数据封包序号中,最後将SYN与ACK都设置成1遂返回响应。服务端这时将状态改成SYN-RCVD且该封包一样不含资料区段
客户端需要再发送一个封包来完成连线的最後步骤。客户端会将服务器响应的发送数据封包序号加1并填入自己的接收数据封包序号中,同时将ACK设置成1然後发送出去。一旦服务器接收到三次握手的消息,双方将会处於ESTABLISHED状态,也就是我们在netstat视窗中看到的连线状态
<<: Day16 - 在 Next.js 做 JWT 验证,使用既有的 Backend API - PART 2
>>: D30-(9/30)-远雄港(5607)-贸易港区冷链
关於资料治理中的安全性,适用於 欧盟的《通用数据保护条例》(GDPR) 和《 金融工具市场指令II》...
咦?To-Do-List 怎麽突然结束了!? 恩…主要是最近接到了不少新的任务,而我想,To Do ...
今天 google 了一下,发现之前很多前辈大多不是有充分的腹案,就是早早准备好了文件库。不管是信手...
Drawer 这个组件其实就是我们常用的 sidebar,继前一天的章节结合,就可以完成一个完整的应...
图片来源 随着数位科技的进步与运用, 现在电子礼券盛行, 从超商寄杯的条码, 到公司年节发送的大卖...