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