Tcp连接建立
上图为Tcp连接建立过程:
1)客户端给服务器发送了一条将其SYN标志位置1的请求连接建立报文,然后其状态由closed转变为SYN-SENT(同步已发送)。
2)服务器收到该报文后如果想要与客户端建立连接其给客户端发送一条将ACK和SYN都置1的报文。此时服务器的状态由的listed状态转化为SYN-RECV(同步已接收)状态。
3)客户端收到该报文后,给服务器发送一条将ACK置为1的确认报文,之后就进入established状态(已建立连接)。
4)服务器收到上述报文后也进入established状态,之后就可以你来我往的通信了。
代码语言:javascript复制客户端:
fd = socket()
// 调用该方法时由closed转化为syn-sent,该方法是一个阻塞方法,当获得返回值时转回established状态
connect(fd, address port)
服务器:
fd = socket();
bind();
listed(); // 进入listen状态
// 对于阻塞socket,调用accept()之前进入SYN-RECV 当前获得返回值后进入established状态。
accept();
Tcp连接释放
连接释放过程如上图所示.
1)客户端对服务器发送连接释放报文段将其FIN标志位置1,并由之前的established状态转化为finwait-1(终止等待1)状态。此时其已经不能再发送了,只能接收。
2)服务器收到连接释放报文后,对客户端发送一条将ACK置1的响应报文,并转化为closewait(关闭等待)状态,之后其继续可以向客户端发送信息。
3)客户端收到该报文后,转化为finwait-2(终止等待2)状态。
4)当服务器信息也发送完了,其会给客户端发送一天将FIN和ACK都置为1的报文,自己进入lastack状态(最后一个应答)。
5)客户端收到最后一条应答报文后将对服务器发送一条应答报文ACK=1,并进入timewait(时间等待)状态,并在timewait状态等待两个MSL后再将自己关闭。
6)服务器端收到应答报文后关闭该连接。
代码语言:javascript复制客户端:
close()
服务器:
// 返回0
read()
close()
为何要等待两个MSL?
1)为了保证客户端的最后一条应答报文到达服务器,当该报文丢失后,服务器端会在2msl时间之内重传一条FIN=1,ACK=1报文,客户端收到后会重发一条应答报文,并将等待计时器清0重新计时。如此是为了避免客户端自己关了,服务器端的就无法收到最后一个ACK=1报文,无法按照正常步骤进入closed状态。
2)为了防止已失效的连接请求报文出现在本连接中。
document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });