TCP四次分手及对应的TCP状态学习笔记

转载请注明原文链接哦


做网络开发,经常会遇到以下”投诉“——

”你的软件网络怎么做得那么不稳定!“

”怎么了?“

”经常有设备无故下线了。“

”啊。。。“

”赶紧解决啊,现场急。“

”能给个日志来么。。。顺便(用wireshark)抓个包呗。。。“

。。。。。


反正遇到这个问题,我首先会检查soket是网络不稳定断开的,还是正常断开,正常断开的话是谁主动断开的。我们就来讨论一下怎么是正常的关闭,又怎样进行判断是哪边提出断开的。


TCP正常断开需要进行四次分手(哎呀嘛呀~怎么和人一样墨迹啊~)。翠花上图:

所谓四次分组就是客户端和服务端相互了四次分组交换:

“我们分手吧!”  ——  啪!一个(FIN)打过来

“哎呦!不要啊~还记得大明湖畔%^&*(#$……”  ——  回了个ACT,

“好吧,竟然无法挽留,我答应你” —— 把以前的礼物的还了(也发了个FIN)

“BYE-BYE!” —— 也回了个ACT

嗯嗯,其实TCP四次分手其实也是和人一样,既然分手了就分得干净点,免得有一天有了新的对象(新的连接),还因为和前任没断干净而出现混乱。


为了进一步验证,我们用代码做做实验吧:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int service(int listenfd)
{
	int connfd = -1;
	
	while(1)
	{
		printf("accept befor....\n");
		connfd = accept(listenfd, NULL, NULL);
		printf("accept new socket\n");
		
		getchar();
		printf("close connfd\n");
		close(connfd);
		connfd = -1;
	}
	return 0;
}

int main(int argv, char *argc[])
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in addr;
	char ip[64] = {0};
	int client = 0, port;
	int ret;

	if(argv < 4 || sockfd < 0)
	{
		perror("socket fail");
        close(sockfd);
		return -1;
	}

	if(0 == strcmp(argc[1], "client"))
	{
		printf("start -- client\n");
		client = 1;
	}
	else
	{
		printf("start -- %s\n", argc[1]);
	}
	
	strcpy(ip, argc[2]);
	printf("ip:%s\n", ip);
	port = atoi(argc[3]);
	printf("port:%d\n", port);

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);

	if(client)
	{
		ret = inet_pton(AF_INET, ip, &addr.sin_addr);
		if(ret != 1)
		{
			perror("inet_pton fail");
			close(sockfd);
			return -1;
		}

		ret = connect(sockfd, (struct sockaddr *)&addr, (socklen_t)sizeof(addr));
		if(ret != 0)
		{
			perror("connect fail");
			close(sockfd);
			return -1;
		}

		printf("connect %s:%d success\n", ip, port);

		getchar();
#if 0
		printf("abort without close socker\n");
		abort();
#else
		printf("close socker\n");
		close(sockfd);
#endif
		return 0;
	}
	else
	{
		addr.sin_addr.s_addr = htonl(INADDR_ANY);
		
		ret = bind(sockfd, (struct sockaddr *)&addr,sizeof(addr));
		if(ret < 0)
		{
			perror("bind fail");
            close(sockfd);
			return -1;
		}
		
        if(listen(sockfd,5) < 0)
        {
			perror("listen Faild");
            close(sockfd);
            return -1;
        }

        service(sockfd);
	}
	return 0;
}

为了方便,上面同时实现了客户端和服务端,通过运行选项来区别是运行客户端的代码还是服务端的代码。

建立TCP连接

编译成功后,先运行服务端

到服务端口已启用

运行客户


连接成功,查看TCP状态

当前状态都为“ESTABLISHED


客户端先主动断开,断开后查看TCP状态

可以看到:

  • 主动断开的一方状态变为FIN_WAIT2
  • 被动断开的一方状态为CLOSE_WAIT

服务端也断开,断开后查看TCP状态

可以看到:
  • 主动断开的一方已经没有状态描述了
  • 被动断开的一方还存在状态为TIME_WAIT
过一段时间后,TIME_WAIT也消失了

下面是TCP四次分手分组交换图



?!怎么刚才没看 FIN_WAIT_1的状态呢!!那是因为发出去的FIN和收到的ACK之前的时间很快,所以通常FIN_WAIT_1很难出现。但也不是不可能,只要主动断开之前对方把网线拔了,你的分手信(FIN)就无法送给对方了:


我们分手吧!”
“喂~你说什么我听不见!大声点!喂!。。。嘟嘟嘟。。。”
“( 0_0||| )"

(完)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值