以上准备工作完成之后,我们终于要开始真正发送HTTP请求了,HTTP请求建立在TCP连接之上。所以我们需要首先建立TCP连接。
进行收发数据操作之前, 双方需要先建立起这条管道才行。建立管道的关键在于管道两端的数据出入口,这些出入口称为套接字。需要先创建套接字,然后再将套接字连接起来形成管道。具体会在下面介绍操作系统内核上体现的时候说明。
创建套接字
调用Socket库的socket创建套接字,协议栈会返回一个描述符,应用程序会讲描述符存放在内存中。当创建套接字后,我们就可以使用这个套接字来执行收发数据的操作。
TCP三次握手
通过Socket库的connect完成连接。指定描述符、服务器IP和端口号。服务端通过Socket库的accept接受请求。
客户端调用connect时,触发了连接请求,向服务器发送了第一次握手,这时connect进入阻塞状态;
服务器监听到连接请求,即收到SYN包,调用accept函数接收请求向客户端发送确认同步,这时accept进入阻塞状态;
客户端收到服务器的确认同步之后,这时connect返回,并对确认同步进行确认;
服务器收到客户端的ACK之后,accept返回,至此三次握手完毕,连接建立。
服务器监听,客户端创建
一般在握手之前,服务器已经处于监听状态。客户端需要创建一个数据结构里面包含源端口、目的端口、目的IP、序号、应答号、对方窗口大小、自己窗口大小、缓冲区等。
第一次握手建立连接 控制位(SYN) + 序列号
第一次握手从TCP客户端开始,设置SYN(SYNCHRONIZATION)标志位代表建立第一次握手,并提供一个初始序列号(初始序号是随机的)。
发送后,客户端状态为SYN-SENT。
第二次握手确认同步 控制位(SYN + ACK) + 序列号 + 确认号(客户端序列号+1)
服务端收到后,会在本地创建一个数据结构(request_sock结构),包含源端口、目的端口、目的IP、序号、应答号、对方窗口大小、自己窗口大小、缓冲区等。
第二次握手,服务端会回应SYN + ACK(ACKNOWLEDGMENT),和第一次握手一样只有头部没有数据,服务器也生成自己的序列号。到客户端一直解封装,收到SYN + ACK,并且将确认号-1判断和自己发送的序列号一样,就知道连接建立了。
发送后,服务端状态从LISTEN变为SYN-RECEIVED。客户端接收状态为ESTABLISHED。
第三次握手 控制位(ACK)+ 序号 + 确认号 确认确认同步
为了通知服务器确认同步消息被接受了,发送ACK,并且发送序列号+确认号,序列号为步骤一的序列号+1,也就是步骤二的确认号。确认号为步骤二的序列号+1。
握手之后就建立了连接,这时候客户端就可以发送HTTP请求了。
服务端接收后,状态为ESTABLISHED。
其他
DDoS攻击
但如果每次服务器都要记住自己的序列号,那么就需要挂起非常多资源。那么可能出现有客户端不断发送SYN却不进行下一步操作,服务器就会崩溃,这就是DDoS攻击。
因此服务器根据IP地址和端口号等私有信息进行算法运算得到序号,不进行保存。
TCP报文格式
- 源端口一般随机生成
- [S]SYN置为1代表是请求报文;[S.]代表ACK同步消息;[.]代表ACK。
- win 代表本机窗口大小
- 握手的时候序列号+1,传输数据的时候不会。
TCP字节流协议
TCP会把应用层数据看成一连串无结构的字节流。TCP会将报文切成一个个Segment段进行传输。这个段多大呢?默认536byte,查看MSS:Max Segment Size可知,但会根据网络状况以及客户端和服务器的窗口大小动态调整。(网络差,会缩减段的大小,这也是拥塞控制)需要根据序列号,对报文进行排序。报文如果丢失了会进行重传、去重。
- 不限制数据大小
- 打包成报文段
- 保证有序接收
- 重复报文自动丢弃
TCP流量缓冲
双方处理能力不匹配,各自都会有一个缓冲区。
TCP三次握手在操作系统内核上体现
内核会对某个端口进行监听,当收到客户端发送SYN请求之后,请求会进入SYN队列。服务端返回SYN/ACK响应报文给客户端,等待客户端给服务器发送ACK。服务端接收到ACK后会把SYN队列中的报文出队,放到ACCEPT队列中,这时候监听accept的应用程序就可以从中拿到连接。
- SYN报文到达服务端
- 入队,进入SYN队列
- 响应SYN/ACK
- ACK
- SYN队列出队
- 进入ACCEPT队列,连接已经建立了
- 这时候通常会有一个应用程序拿到这个accept,进行连接
-
SYN队列也叫半连接队列
由服务器进行维护,为每个客户端的SYN包开设一个条目。服务端在接收到SYN包的时候,就已经创建了request_sock结构了,存储在半连接队列中,该条目代表服务器已经收到SYN,等待客户发出确认,等待客户端确认包。这个条目标识服务器处于SYN_RECEIVED状态。
-
ACCEPT队列也叫完全连接队列
服务端接收到ACK之后,进入ACCEPT队列。
TCP可靠性传输
-
- 停止等待协议 死等ACK
-
-
重传机制
-
ACK报文丢失,会尝试超时后重新请求
-
请求报文丢失,同样也会在超时后重新请求
-
-
- 滑动窗口协议与累计确认
挨个确认超时重传效率很低,因此可以设置一个发送窗口大小,一次性发送该窗口大小的报文,服务器只需要确认最后一个报文。但如果丢包了怎么办呢?丢包了的话从最后一个发送的报文之后进行重传。
- 保证可达
- 丢包时,通过重发机制实现可靠性
TCP三次握手目的
- 同步通信双方初始序列号(ISN,Initial sequence number)
- 协商TCP通信参数(MSS,窗口信息,指定校验和算法)