socket - close(2) 发送 RST 数据包而不是 FIN 数据包
socket - close(2) send RST packet instead of FIN packet
我写了一个简单的客户端和服务器,基本上是客户端先连接到服务器然后断开连接close(2)
,看起来像:
/* setting server address and other stuff */
...
connect();
close();
调用 connect()
和 close()
之间没有其他操作。
服务器接受连接并使用epoll(7)
监视EPOLLOUT事件。 epoll 报 EPOLLOUT 时,server 写 1 byte 给 client。
nev = epoll_wait(ep, events, 10, -1);
for (int i=0; i<nev; i++) {
std::cout << "ready, events: " << std::hex
<< events[i].events << std::dec << std::endl;
if (events[i].data.fd == sockfd) {
int connfd = accept(sockfd, (struct sockaddr*)&client, &len);
if (connfd < 0) {
std::cout << strerror(errno) << std::endl;
exit(0);
}
std::cout << "accpeted\n";
struct epoll_event ev;
ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLHUP;
ev.data.fd = connfd;
epoll_ctl(ep, EPOLL_CTL_ADD, connfd, &ev);
} else {
if (events[i].events & EPOLLOUT) {
write(events[i].data.fd, "1", 1);
sleep(1);
}
}
}
这里是tcpdump,9999是服务器:
11:52:11.411988 IP localhost.37776 > localhost.9999: Flags [S], seq 2786125487, win 43690, options [mss 65495,sackOK,TS val 34912846 ecr 0,nop,wscale 7], length 0
11:52:11.412013 IP localhost.9999 > localhost.37776: Flags [S.], seq 1338547838, ack 2786125488, win 43690, options [mss 65495,sackOK,TS val 34912846 ecr 34912846,nop,wscale 7], length 0
11:52:11.412035 IP localhost.37776 > localhost.9999: Flags [.], ack 1, win 342, options [nop,nop,TS val 34912846 ecr 34912846], length 0
11:52:11.413476 IP localhost.9999 > localhost.37776: Flags [P.], seq 1:2, ack 1, win 342, options [nop,nop,TS val 34912847 ecr 34912846], length 1
11:52:11.414869 IP localhost.37776 > localhost.9999: Flags [.], ack 2, win 342, options [nop,nop,TS val 34912847 ecr 34912847], length 0
11:52:11.415882 IP localhost.37776 > localhost.9999: Flags [R.], seq 1, ack 2, win 342, options [nop,nop,TS val 34912847 ecr 34912847], length 0
最后一个数据包建议客户端发送一个RST数据包来关闭连接。我没有设置 SO_LINGER 选项,为什么会这样?
tcp_close
在内核上的实现,在文件 net/ipv4/tcp.c
中。
内核解释如下:
/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (unlikely(tcp_sk(sk)->repair)) {
sk->sk_prot->disconnect(sk, 0);
} else if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk->sk_allocation);
}
我写了一个简单的客户端和服务器,基本上是客户端先连接到服务器然后断开连接close(2)
,看起来像:
/* setting server address and other stuff */
...
connect();
close();
调用 connect()
和 close()
之间没有其他操作。
服务器接受连接并使用epoll(7)
监视EPOLLOUT事件。 epoll 报 EPOLLOUT 时,server 写 1 byte 给 client。
nev = epoll_wait(ep, events, 10, -1);
for (int i=0; i<nev; i++) {
std::cout << "ready, events: " << std::hex
<< events[i].events << std::dec << std::endl;
if (events[i].data.fd == sockfd) {
int connfd = accept(sockfd, (struct sockaddr*)&client, &len);
if (connfd < 0) {
std::cout << strerror(errno) << std::endl;
exit(0);
}
std::cout << "accpeted\n";
struct epoll_event ev;
ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLHUP;
ev.data.fd = connfd;
epoll_ctl(ep, EPOLL_CTL_ADD, connfd, &ev);
} else {
if (events[i].events & EPOLLOUT) {
write(events[i].data.fd, "1", 1);
sleep(1);
}
}
}
这里是tcpdump,9999是服务器:
11:52:11.411988 IP localhost.37776 > localhost.9999: Flags [S], seq 2786125487, win 43690, options [mss 65495,sackOK,TS val 34912846 ecr 0,nop,wscale 7], length 0
11:52:11.412013 IP localhost.9999 > localhost.37776: Flags [S.], seq 1338547838, ack 2786125488, win 43690, options [mss 65495,sackOK,TS val 34912846 ecr 34912846,nop,wscale 7], length 0
11:52:11.412035 IP localhost.37776 > localhost.9999: Flags [.], ack 1, win 342, options [nop,nop,TS val 34912846 ecr 34912846], length 0
11:52:11.413476 IP localhost.9999 > localhost.37776: Flags [P.], seq 1:2, ack 1, win 342, options [nop,nop,TS val 34912847 ecr 34912846], length 1
11:52:11.414869 IP localhost.37776 > localhost.9999: Flags [.], ack 2, win 342, options [nop,nop,TS val 34912847 ecr 34912847], length 0
11:52:11.415882 IP localhost.37776 > localhost.9999: Flags [R.], seq 1, ack 2, win 342, options [nop,nop,TS val 34912847 ecr 34912847], length 0
最后一个数据包建议客户端发送一个RST数据包来关闭连接。我没有设置 SO_LINGER 选项,为什么会这样?
tcp_close
在内核上的实现,在文件 net/ipv4/tcp.c
中。
内核解释如下:
/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (unlikely(tcp_sk(sk)->repair)) {
sk->sk_prot->disconnect(sk, 0);
} else if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk->sk_allocation);
}