在看TCP/IP 卷1和UNP的时候,对于这个TIME_WAIT状态了解的不透彻,这里记录一下个人对这个状态的理解,方便日后查阅。
一.简介
TIME_WAIT状态是TCP连接断开过程中的一种状态,TCP连接的主动关闭的一方会有这样的状态。其目的在后文介绍,先贴上一张TCP连接的状态转移图。
从上面的图中可以拿到,TCP连接的主动关闭一方,会在对最后一个FIN进行ACK后进入到这个状态。TCP连接主动关闭的一方处在这个状态时,TCP连接并没有关闭完成。通常,这个状态的时间为2MSL,MSL是IP数据报能在网络中生存的最长时间。
二. TIME_WAIT状态的目的
从TCP/IP协议详解卷一和UNP第一卷的介绍,该状态主要有两个目的:
1.确保主动关闭这一方最后发出的ACK能够顺利被被动关闭的一方收到。
2.在TIME_WAIT状态中,这个连接额插口不能再被使用/允许老的重复的分节在网络中消逝。
关于第一个目的,比较好理解,主动关闭的这一方,在发送完最后一个ACK后,如果被动关闭的一方没有收到,那么它会再次给主动关闭的一方发送一个对上一个分节的确认,告知没后收到ACK,这个时候处于TIME_WAIT的一方会再次发送ACK。对于第二个目的,理解起来比较不那么直观,本文将主要对这第二个目的进分析学习。
第二个目的:处于TIME_WAIT状态socket,不能再次被使用,也就说不能bind一个处于socket状态的端口到一个新socket,这样做是为了保证处于TIME_WAIT状态的socket以前发送在网络中的包能够正确的消逝。如果允许bind一个处于TIME_WAIT状态的socket再次启用一个新的连接,如果一个包到了这个新的连接,如何知道这个包不是以前处于TIME_WAIT那个socket应该收到的包呢?
三. TIME_WAIT的影响
本文写几个TCP连接的例子,来测试TIME_WAIT状态影响。
1.对client端的影响。
对于server端来说,确实是需要这个TIME_WAIT的,而且没什么影响,因为client端的端口号一般都是系统随机分配的,我们不会给client端的socket去bind一个端口号。内核一般也不会选择一个处于TIME_WAIT状态的端口号。TIME_WAIT的目的也正在这种情况下发挥了作用。
写一个简单的例子,来测试一下。为了测试,在client端bind一个端口。
client 端:
点击(此处)折叠或打开
-
#include <stdio.h>
-
#include <string.h>
-
#include <sys/socket.h>
-
#include <unistd.h>
-
#include <netdb.h>
-
#include <netinet/in.h>
-
#include <errno.h>
-
-
char * host_name = "192.168.204.134";
-
int port = 9888;
-
int localport = 51335;
-
int
-
main()
-
{
-
int sockfd;
-
int ret = 0;
-
struct sockaddr_in servaddr;
-
struct sockaddr_in cliaddr;
-
sockfd = socket(AF_INET, SOCK_STREAM, 0);
-
memset(&servaddr, 0, sizeof(servaddr));
-
servaddr.sin_family = AF_INET;
-
servaddr.sin_port = htons(port);
-
memset(&cliaddr, 0, sizeof(cliaddr));
-
cliaddr.sin_family = AF_INET;
-
cliaddr.sin_port = htons(localport);
-
ret = bind(sockfd, (struct sockaddr*)&cliaddr, sizeof(cliaddr));//给client绑定一个端口,测试TIME_WAIT
-
if(ret < 0)
-
{
-
printf(" bind error %s\n", strerror(errno));
-
return 0;
-
}
-
ret = inet_pton(AF_INET, host_name, &servaddr.sin_addr);
-
if(ret != 1)
-
{
-
printf(" inet_pton error:%s\n", strerror(errno));
-
return -1;
-
}
-
ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
-
if(ret < 0)
-
{
-
printf(" connect to server error %s\n", strerror(errno));
-
close(sockfd);
-
return -1;
-
}
-
printf(" connect successfully\n");
-
getchar();
-
close(sockfd);
-
return 0;
- }
server端:server在accept后fork一个子进程处理这个连接,后面测试TMME_WAIT用得到。
点击(此处)折叠或打开
-
#include <stdio.h>
-
#include <string.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
#include <sys/socket.h>
-
#include <netinet/in.h>
-
#include <netdb.h>
-
#include <sys/types.h>
-
#include <errno.h>
-
#include <sys/wait.h>
-
#define SERV_PORT 9888
-
#define LISTENQ 1024
-
void client_server(int sockfd)
-
{
-
char buf[100];
-
int ret = 0;
-
while(1)
-
{
-
ret = read(sockfd, buf, 100);
-
if(ret == 0)
-
{
-
printf(" connection shutdown by others \n");
-
ret = close(sockfd);
-
if(ret)
-
{
-
printf(" close error %s\n", strerror(errno));
-
}
-
exit(0) ;
-
}
-
}
-
return;
-
}
-
int main()
-
{
-
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
-
if(listenfd < 0)
-
{
-
printf(" socket error %s\n", strerror(errno));
-
return 0;
-
}
-
int sockfd = 0;
-
pid_t child;
-
if(listenfd == -1)
-
{
-
printf(" socket error\n");
-
return 0;
-
}
-
struct sockaddr_in serveraddr, clientaddr;
-
memset(&serveraddr, 0, sizeof(serveraddr));
-
memset(&clientaddr, 0, sizeof(clientaddr));
-
serveraddr.sin_family = AF_INET;
-
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
-
serveraddr.sin_port = htons(SERV_PORT);
-
int ret = 0;
-
ret = bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
-
if(ret < 0)
-
{
-
printf(" bind error %s\n", strerror(errno));
-
return -1;
-
}
-
ret =listen(listenfd, LISTENQ);
-
if(ret < 0)
-
{
-
printf(" listen error %s\n", strerror(errno));
-
return -1;
-
}
-
printf(" create server successfully\n");
-
while(1)
-
{
-
sockfd = accept(listenfd, NULL, NULL);
-
if(sockfd < 0)
-
{
-
printf(" accept error %s\n", strerror(sockfd));
-
return -1;
-
}
-
child = fork();
-
if(child == 0)
-
{
-
close(listenfd);
-
client_server(sockfd);
-
exit(0);
-
}
-
else if(child > 0)
-
{
-
int status;
-
ret = close(sockfd);
-
printf(" ret = %d\n", ret);
-
ret = wait(&status);
-
printf("ret = %d\n", ret);
-
continue;
-
}
-
-
}
- }
点击(此处)折叠或打开
-
int val = 1;
-
int len = sizeof(val);
-
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, len);
-
if(ret < 0)
-
{
-
printf(" setsockopt error: %s\n", strerror(errno));
-
return -1;
- }
点击(此处)折叠或打开
-
int ret = 0;
-
int val = 1;
-
int len = sizeof(val);
-
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &val, len);
-
if(ret < 0)
-
{
-
printf(" setsockopt error %s\n", strerror(errno));
-
return -1;
- }