一场Socket四次握手引发的血案
前奏
Hello Everybody,原谅我是一个标题党。事情是这样的,周末,同事在微信上抛来一个问题,原文如下:
服务端某个服务timewait过多,网上说端口会耗尽,我怎么感觉是fd会耗尽呢,因为一个服务通常只使用一个端口
在记忆的某个角落里,翻出了四次握手的状态图,虽然模模糊糊,但是还是有点印象。
首先,明确一点,主动关闭socket的一端进入timewait阶段,一个socket连接由五元组(目的IP,目的Port,源IP,源Port,协议)唯一标识。对于采用TCP协议的服务端,目的IP、目的Port、协议这三个元素固定不变,对于同一个客户端来说,其源IP固定不变,也就是说唯一能变的就是源Port。而源Port最大为65535,因此有耗尽的可能,第一次回答如下:
五元组(目的IP,目的Port,源IP,源Port,协议),同一个客户端,目的IP、目的Port、源IP、协议不变,端口有数量限制,因此应该是客户端端口会耗尽。
同事紧接着发难:
先只考虑服务端,timewait过多会导致啥情况?
回忆了一下timewait的作用,隐隐约约记得,防止客户端的FIN包积压在链路中,导致socket错乱。即客户端关闭连接后,重用本次五元组;迟来的FIN包影响新连接。使用timewait,锁定五元组(客户端不能重用本次五元组),使得本次会话的所有数据包在链路中消亡。因而,猜想timewait要维护连接状态,fd不会被释放。第二次回答:
timewait 时候fd被霸占,量大后,fd肯定会被耗尽。
作答后,久久不能平静,因为后半截回答主要靠猜想,没有实际论证,总感觉没有把握,于是开始了一系列的论证过程。
论证
Socket 代码
这里仅列出主要代码逻辑,完整代码参加GitHub。
/* server code */
while (1) {
int len = sizeof(struct sockaddr);
fd = accept(sfd, &remote, &len);
res = read(fd, buf, sizeof(buf));
printf("user data : %s\n", buf);
strcpy(buf, "Hello Client");
res = write(fd, buf, sizeof(buf));
printf("send data : %s\n", buf);