TCP连接之状态改变探究

最近在读《UNIX网络编程1》,有了一些收获写出来供大家参考。

平台: Ubuntu 12.04.1 LTS 3.2.0-31-generic

还是先由一个问题引出吧。

背景:

    本打算观察TCP正常终止的socket状态转化的,结果却发现服务器的TIME_WAIT状态却始终不出现。

步骤如下:

    1、启动服务店,启动客户端

            使用netstat -natp 结果如下:

    2、 kill -9 已连接服务端进程

            使用netstat -natp结果如下:

    1,2分析:此时,结果一切正常,正如书上所说,active close端(在此是服务端)处于 FIN_WAIT2,而passive close端(客户端)是CLOSE_WAIT状态,至此我们完成了TCP终止序列的一半。

    3、这时在客户端敲入回车,客户端socket关闭,并可以看到客户端输出如下:

            使用netstat -natp结果如下:

    3分析:为什么服务端没有出现TIME_WAIT状态呢?呵呵,大牛们别笑我,先卖个关子,大家也可以顺便思考。

 

为了找出原因,我们可以仔细的分析上面的情况,相信1,2步的socket状态转换,大家看书都知道了,我们主要分析第3步的原因。

在第3步之前,两端的socket状态正常,可见肯定是因为第3步的代码导致了服务端的socket关闭流程异常终止。那到底是Write呢,还是Read呢?我们可以借助于gdb和tcpdump -i lo来查看,到底发生了什么情况,可以看见Write时,服务器回了RST分节,此时使用netstat -natp 查看,发现socket连接消失了,继续调用Read,返回0,客户端终止。

It is okay to write to a socket that has received a FIN, but it is an error to write to a socket that has received an RST.

    原因找到了,原来是客户端向服务端关闭的soket写,导致服务端返回RST,同时服务端强制关闭socket,而此时客户端因为收到了RST,也被强制关闭。导致我们看不到TIME_WAIT状态的发生。 至于为什么Read返回的是0,而不是ECONNRESET错误,书上的解析为Read时,RST分节还没有收到,因此读到的是FIN,所以返回0,但是我做过实验,在Read前,sleep(10),却发现仍然为0,不知道是什么原因?

    书上的解析:

What we have described also depends on the timing of the example. The client's call to readline may happen before the server's RST is received by the client, or it may happen after. If the readline happens before the RST is received, as we've shown in our example, the result is an unexpected EOF in the client. But if the RST arrives first, the result is an ECONNRESET ("Connection reset by peer") error return from readline.

    总结:

    1、向close socket写,会收到RST,并导致socket强制关闭

    2、向收到RST的socket写,会导致socket强制关闭

    3、为了得到服务端的TIME_WAIT状态,我们应该怎么办呢?相信大家都知道了,只要将第3步,改为在客户端键入CTRL+D就可以了。

    4、TCP终止流程只有在两边都正常退出的情况下才能看到,只要任何一方出现异常,连接都会终止。

另附上TCP状态转换图:

    TCP连接之状态改变探究_第1张图片

代码贴出来:

//server

void server_fuc(int conn_fd)
{
	char buf[MAX_STRING];
	int len = -1;

	while((len = Read(conn_fd, buf, sizeof(buf))) > 0)
	{
		Write(conn_fd, buf, len); 
	}

	printf("client close\n");
}

void sig_child(int signo)
{
	if(signo = SIGCHLD)
	{
		pid_t pid;

		printf("recv SIGCHLD\n");
		while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
		{
			printf("client pid = %ld\n", pid);
		}
	}
}

int main(int argc, char* argv[])
{
	int socket_fd = -1;

	if(argc != 3)
	{
		sys_quit("Usage: %s <hostname> <service>", argv[0]);
	}

	socket_fd = tcp_listen(strcmp(argv[1], "") == 0 ? NULL : argv[1], argv[2]);

	Signal(SIGCHLD, sig_child);

	while(1)
	{
		int conn_fd = Accept(socket_fd, NULL, NULL);
		pid_t pid;

		if(fork() == 0)
		{
			close(socket_fd);
			server_fuc(conn_fd);	
			close(conn_fd);
			exit(0);
		}

		close(conn_fd);
	}	

	return 0;
}

//client

 

void client_fuc(int socket_fd)
{
	char buf[MAX_STRING];	
	ssize_t len;

	while(fgets(buf, sizeof(buf), stdin) != NULL)
	{
		Write(socket_fd, buf, strlen(buf));
		
		len = Read(socket_fd, buf, sizeof(buf));
		if(len == 0)
		{
			printf("server terminate\n");
			break;
		}

		buf[len] = 0;
		printf("%s", buf);
	}
}

void sig_pipe(int signo)
{
	return;
}

int main(int argc, char* argv[])
{
	int socket_fd = -1;
//	struct linger ling;

	if(argc != 3)
	{
		sys_quit("Usage: %s <hostname> <service>", argv[0]);
	}

	socket_fd = tcp_connect(argv[1], argv[2]);

//	ling.l_onoff = 1;
//	ling.l_linger = 0;
//	setsockopt(socket_fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));

	Signal(SIGPIPE, sig_pipe);
	
	client_fuc(socket_fd);	

	close(socket_fd);

	return 0;
}

你可能感兴趣的:(tcp,状态转换)