TCP是一个字节流(byte-stream)协议,没有任何记录边界,这一点不同于UDP
TCP三次握手(结合程序):
准备:
服务器passive open
服务器准备好接受外来的链接
调用socket(); bind(); listen()这三个函数完成
- 客户端active open
调用connect()主动打开(发送一个SYN分节(没有数据,告诉发送数据的开始序列号)); - 服务器确认
服务器确认(ACK)客户端的(SYN)这里应该就用到accept()了吧;
然后发回去SYN,ACK - 客户端确认
客户端确认服务器的SYN( connect()函数产生返回值 ),先发送ACK,然后发送数据,
服务端收到accept()函数的返回值,然后调用read()接受数据
TCP四次挥手(结合程序):
- 客户端active close
调用close(),该端的TCP发送一个FIN分节,表示数据发送完毕。 - 服务器passsive close
收到FIN( read()函数返回0 )由TCP确认(发回一个ACK)。他的接受也作为与一个文件结束符(EOF)传递给应用进程 - 服务器FIN
接收到这个EOF的应用进程将调用close()关闭它的套接字,导致它的TCP也发出一个FIN信号 - 客户端确认
客户端确认服务器的FIN
四次挥手可能成为三次,是因为2,3的ACK和FIN可能一起发送;
发送FIN是因为应用进程调用close()而发生的
序列号?
看到这里的时候发现有点混乱;
首先SYN和ACK是报文首部的内容;
ACK告诉对端自己期待接收到的下一个包的序列号,通常为最近收到的包的序列号 + 1
三次握手的时候:
第一次客户端向服务端发送的报文里的SYN标志位为1(标识该报文用来建立连接),整个包的序列号为client_isn(客户端的一个初始序列号);
服务端收到之后返回一个包,报文首部里:SYN标志位为1(同理),ACK为client_isn + 1;整个包的序列号为server_isn(服务端的一个初始序列号);
第三次表示连接已经建立,所以发送的包里面SYN设置为0,ACK为server_isn + 1;整个包的序列号为client_isn + 1;
TCP选项:
每一个SYN可以含有多个TCP选项
1. MSS(Maximum Segment Size)最大分节大小:
接收端表示在本连接中的每个TCP分节中愿意接受的最大数据量,发送端TCP使用接收端的MSS值作为所发送分节的最大大小;
TCP_MAXSEG套接字选项可以提取和设置这个TCP选项;
2. 窗口规模选项:
原来是65535bit,现在由于网速变快接近1G;
在一个TCP连接上使用窗口规模的全体是它的两个端系统必须都支持这个选项。SO_RCVBUF套接字选项可以影响到这个选项
TIME_WAIT状态
执行主动关闭的端经历了这个状态,该断点停留在这个状态的持续时间是最长分节生命期(Maximum Segment Lifetime,MSL)的两倍,2MSL;
MSL是任何IP数据报能够在因特网中存活的最长时间。我们假设具有最大跳限(hop limit)的分组在网络中存在的时间不可能超过MSL。
在此可以查一下重复分组(lost duplicate)的概念
TIME_WAIT状态有两个存在的理由:
1. 可靠的实现TCP全双工连接接的终止
假设4次挥手的最后一个ACK丢失,服务器重新发送一个FIN,这时客户端需要足够的时间再去接收这个FIN并重新发送一个ACK,如果没有TIME_WAIT而是发送完最后一个ACK直接关闭了事,那么如果ACK丢失,那么状态信息则不完整,就不能可靠的实现TCP全双工连接接的终止。
2. 允许老的重复分组在网络中消逝
如果两个地址(IP+端口号)在断开连接之前出现错误,产生了重复分组;还是这两个地址重新连接的时候,有可能老的重复分组传到对端,与新连接里与老的重复分组序列号相同的新分组冲突;为了防止这种情况,断开连接之后,处于TIME_WAIT状态的地址不能建立连接,要等待老的重复分组消逝(TIME_WAIT的时间足够让它消逝)之后才能发起连接。
(一个例外)
如果新连接的SYN的序列号大于前一个连接的结束序列号(可能是最后一个ACK),处于TIME_WAIT的端可以建立连接。
端口号:
采用16位整数的端口号(port number)
well-known端口号:
约定俗成的端口号,如21是FTP
由IANA分配和控制,范围为0~1023
已登记的端口号:
范围1024~49151
临时端口号:
是由传输层协议自动赋予用户,传输层协议代码保证它的唯一性
范围49152~65535
Unix系统有保留端口(reserved port),任何well-known端口号都是保留端口,分配这些端口的服务器应用必须以SU启动。
套接字对(socket pair):
一个TCP连接的套接字对是一个定义该连接的连个断电的四元组:
本机IP地址,本机TCP端口号,外地IP地址,外地TCP端口号
TCP套接字对唯一标识一个网络上的每个TCP连接
标识每个端点的(IP地址和端口号)为套接字(socket)
TCP端口号和并发服务器:
并发服务器中主服务器循环通过派生一个 子进程 来处理新的连接
不同的客户连接到并发服务器的同一个端口的时候,先连接到父进程的监听套接字,然后父进程fork出一个子进程来处理(IP地址和端口号不变,但是不是监听套接字而是已连接套接字)
缓冲区大小及限制:
- IPv4数据报最大大小为65535Bytes,包括IPv4首部。(因为首部标识总长度的字段为16bit)
- IPv6数据报最大大小为65575Bytes,包括40Bytes的IPv6首部。(因为首部标识净荷的字段为16bit)
- 以太网的MTU(Maximum Transmission Unit)为1500Bytes
- IPv4要求最小链路的MTU为68Bytes,IPv6要求最小链路的MTU为1280Bytes
- 两个主机之间使用的是路径MTU(为两个主机之间的链路最小MTU)
- 分片(Fragmentation):大小超过链路的MTU
IPv4:主机对产生的数据包执行分片,路由器对转发的数据报执行分片
IPv6:主机对产生的数据包执行分片,路由器不对转发的数据报执行分片- IPv4首部DF标志位(Don`t Fragment)若被设置,无论主机还是路由器都不执行分片,IPv6隐含一个DF位
DF位可用作路径MTU发现。- 最小重组缓冲区大小(minimum reassembly buffer size), 它是IPv4和
IPv6的任何实现都必须保证支持的最小数据报大小。IPv4为576Bytes,IPv6为1500Bytes- TCP有一个MSS(Maximum Segment Size)最大分节大小,告诉对端每个分节中能够发送的最大TCP的数据量。经常被设置为MTU - IP首部长度 - TCP首部长度;
以太网:IPv4: 1500-20-20=1460; IPv6: 1500-40-20=1440
TCP输出:
应用进程调用write函数,然后将数据从应用进程缓冲区写入套接字发送缓冲区,如果数据很大,在I/O过程,应用进程投入睡眠;
write函数调用成功产生返回值,表示应用进程缓冲区可以重新使用,不表示对端收到数据;
TCP加上TCP首部之后,构成TCP分节传入数据链路的输出队列,如果输出队列已满,新到的数据将被丢弃,沿协议栈向上返回一个错误,TCP在相应的时刻重传;IP给每一个TCP分节加上IP数据报;
TCP必须为已发送的数据保留一个副本,知道它收到对端发回的确认为止;
UDP输出:
UDP是不可靠的,所以不需要保存已发送数据的副本;
UDP给用户数据加上8Bytes的UDP首部,发送给IP层,然后IP给每个UDP分段加上IP首部放到数据链路层的输出队列;
如果write函数成功返回,说明数据报或者其分段已经被加入输出队列;