返回:Linux网络编程学习笔记
TCP状态时序图如下:
TCP建立连接时,三次握手时序如下: | TCP数据报格式: |
第一次握手:SYN, 1000(0),
//控制位SYN置1,32位序列号为1000,该段不携带有效载荷(数据字节数为0),mss(Maximum Segment Size,最大报文长度)选项值为1460;
第二次握手:SYN, 8000(0), ACK 1001,
//控制位SYN置1,32位序列号为8000,该段不携带有效载荷,ACK置1,32位确认序列号为1001(代表client端1001之前的包,server端都收到了),mss选项值为1024;
第三次握手:ACK, 8001
//控制位ACK置1,32位确认序列号为8001(代表server端8001之前的包,client端都收到了)。
三次握手发生在内核空间,用户层的主要体现是,connect()和accept()在两端生成网络连接套接字。
使用命令查看端口状态:netstat -apn|grep 9527
root@wang-virtual-machine:/home/wang/nfs# netstat -apn|grep 9527
tcp 0 0 0.0.0.0:9527 0.0.0.0:* LISTEN 4087/./service
tcp 0 0 127.0.0.1:9527 127.0.0.1:47286 ESTABLISHED 4087/./service
tcp 0 0 127.0.0.1:47286 127.0.0.1:9527 ESTABLISHED 4088/nc
主动发起连接请求端: CLOSE -- 发送SYN -- SEND_SYN -- 接收 ACK、SYN -- SEND_SYN -- 发送 ACK -- ESTABLISHED(数据通信态)
被动接收连接请求端: CLOSE -- LISTEN -- 接收 SYN -- LISTEN -- 发送 ACK、SYN -- SYN_RCVD -- 接收ACK -- ESTABLISHED(数据通信态)
主动发起连接 被动接受连接 |
滑动窗口:允许多发单回或多收单确认
第1至第3为建立连接阶段,sender端的滑动窗口缓冲区大小为4k,receiver端滑动窗口的缓冲区大小为6k字节;
第4至第9步,sender向receiver端发送了6次1k的数据;
第10至11步,receiver端告诉sender,缓冲区2k的数据已经处理掉了,空闲大小为4k。
第12至13步,sender向receiver再发2k的数据,并关闭了发送(半关闭);
第14至15步,为ACK应答;
第17步:receive端关闭了发送;
第18步:sender端回复了receive端关闭。
主动关闭连接请求端: ESTABLISHED(数据通信态) -- 发送 FIN -- FIN_WAIT_1 -- 接收ACK -- FIN_WAIT_2(半关闭)
-- 接收对端发送 FIN -- FIN_WAIT_2(半关闭)-- 回发ACK -- TIME_WAIT(只有主动关闭连接方,会经历该状态)
-- 等 2MSL时长 -- CLOSE
被动关闭连接请求端: ESTABLISHED(数据通信态) -- 接收 FIN -- ESTABLISHED(数据通信态) -- 发送ACK
-- CLOSE_WAIT (说明对端【主动关闭连接端】处于半关闭状态) -- 发送FIN -- LAST_ACK -- 接收ACK -- CLOSE
主动发起关闭 被动接受关闭 |
client与server建立TCP socket连接后,如果client端主动关闭连接,会进入FIN_WAIT2状态
root@wang-virtual-machine:/home/wang/nfs# netstat -apn|grep 9527
tcp 0 0 0.0.0.0:9527 0.0.0.0:* LISTEN 4087/./service
tcp 0 0 127.0.0.1:9527 127.0.0.1:47286 CLOSE_WAIT 4087/./service
tcp 0 0 127.0.0.1:47286 127.0.0.1:9527 FIN_WAIT2 -
如果server端主动关闭,其状态会从上方的CLOSE_WAIT变为FIN_WAIT2状态
root@wang-virtual-machine:/home/wang/nfs# netstat -apn|grep 9527
tcp 0 0 127.0.0.1:9527 127.0.0.1:47288 FIN_WAIT2 -
tcp 0 0 127.0.0.1:47288 127.0.0.1:9527 CLOSE_WAIT 4143/nc
在server端主动关闭的情况下,立马重启server端,会出现以下报错信息,主要原因是FIN_WAIT2状态会持续40s,9527这个端口还在被占用。
root@wang-virtual-machine:/home/wang/nfs# ./service
error bind!
: Address already in use
在server的TCP连接没有完全断开之前不允许重新监听是不合理的。因为,TCP连接没有完全断开指的是connfd(127.0.0.1:47288)没有完全断开,而我们重新监听的是lis-tenfd(0.0.0.0:9527),虽然是占用同一个端口,但IP地址不同,connfd对应的是与某个客户端通讯的一个具体的IP地址,而listenfd对应的是wildcard address。解决这个问题的方法是使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。
在server代码的socket()和bind()调用之间插入如下代码:
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
有关setsockopt可以设置的其它选项请参考UNP第7章。