网络编程 accept

1,accept 

       从listen 监听队列中接受一个连接。

#include <sys/types.h>
#include <sys/socket.h>

int accept(int socked, struct socked *addr, sickle_t *addrlen);

      sockfd参数是执行过listen 系统调用的监听socket。

      addr参数用来获取被接受连接的远端socket 地址,该socket地址的长度由addrlen参数指出。

      accept成功时返回一个新的连接socket,该socket唯一地标识了被接受的这个连接,服务器可通过读写该socket来与被被接受连接对应的客户端通信。

     accept失败时返回-1并设置error。

注;监听 socket 和连接 socket 的区别:

      其实一个socket是一个五元组(协议,源IP,源端口,目的ip,目的端口)。

      监听socket 是服务器用来监听新的连接,状态是 listen。如下图,服务器监听5678的端口。

      连接socket表示服务器与客户端建立一次的连接,状态是 ESTABLISHED。如下图,client 连到服务器的5678 端口,accept返回的socket表示的就是这条连接。

 

2,如果监听队列中处于ESTABLISHED 状态的连接对应的客户端出现网络异常(比如掉线),或者提前退出,那么服务器对这个连接执行的accept调用是否成功???

3,模拟client提前退出,server 再accept的情况。

    server在listen后,立马sleep 30秒,此过程中,client connect服务端,建立三次握手,client和server都进入established状态。client连接后,又立马close,并退出程序。此时server还在sleep中,未进入accept,刚好模拟了client在server accept前提前退出的情形。

client 程序:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>

int main()
{
	int sockfd;
	int ret;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(sockfd != -1);

	struct sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(5678);
	serAddr.sin_addr.s_addr = inet_addr("127.0.0.1");


	ret = connect(sockfd, (struct sockaddr *)&serAddr, sizeof(serAddr));
	if(ret < 0)
	{
		close(sockfd);
		printf("connect error\n");
		return -1;
	}
	else
	{

		printf("connetc succ\n");	
	}

	sleep(5);
	printf("client quit\n");
	close(sockfd);

	//shutdown(sockfd, SHUT_RDWR);

	return 0;

}

服务端:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>

int main()
{
	int listenFd, clientFd;
	int ret;

	listenFd = socket(AF_INET, SOCK_STREAM, 0);
	assert(listenFd != -1);

	struct sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(5678);
	serAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	ret = bind(listenFd, (struct sockaddr *)&serAddr, sizeof(serAddr));
	assert(ret != -1);

	ret = listen(listenFd, 5);
	assert(ret != -1);

	printf("listen...\n");

	sleep(30);
	printf("sleep over\n");

	struct sockaddr_in cliAddr;
	socklen_t len = sizeof(cliAddr);

	clientFd = accept(listenFd, (struct sockaddr *)&cliAddr, &len);
	if(clientFd < 0)
	{
		close(listenFd);
		printf("accept error\n");
		return -1;
	}
	else
	{
		printf("client [fd: %d] [ip: %s] [port: %d] connect\n", clientFd, inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));

		printf("pause before\n");
		pause();
		printf("pause after\n");
		close(clientFd);
	}

	close(listenFd);

	return 0;

}

A,启动服务器监听,紧接着服务器进入sleep 30秒。

B,启动客户端,连接服务器,client进入sleep,close前。此时,已经完成了三次握手,建立了连接。

wireshark 抓包也能看出来此过程,client发起了syn连接。

C,client sleep时间到了,5秒后,close掉socket,客户端随之退出。(此时服务器还在sleep中,还未accept)

  此时,client的socket状态是FIN_WAIT_2,服务器的socket状态是 CLOSE_WAIT。是由于client close动作,向服务器单方面发起了fin请求,服务端回复了ack。wireshark抓包情况:

client打印:

D,当服务器的sleep 30秒到期后,进行accept,看下服务端此时的打印情况:

说明,accept成功返回了。再次查看服务端的状态:

由此可见,accept只是从监听队列中取出连接,而不论连接处于何种状态,如上面的CLOSE_WAIT状态。

E,ctrl+c强制退出服务端后,查看服务端的状态:

wireshark的抓包如下:

奇怪的是,此时client程序早已经退出了,ctrl+c服务端,server会发送fin请求,但client居然回复了ack,相应的状态也是TIME_WAIT???

查阅书籍发现,原来连接停留在FIN_WAIT_2状态的情况可能发生在:客户端执行半关闭后,未等服务器的关闭连接就强行退出了。(上面模拟情况,client connect建立连接后,server sleep 30秒后才accept,这期间client已经退出了,未等server最后的fin请求,这时客户端属于半关闭状态)

此时客户端连接由内核来接管,可称之为孤儿连接(和孤儿进程类似)。linux为了防止孤儿连接长时间存留在内核中,定义了两个内核变量:/proc/sys/net/ipv4/tcp_max_orphans和 /proc/sys/net/tcp_fin_timeout。前者指定内核能接管的孤儿连接数目,后者指定孤儿连接在内核中的生存时间。

如果,accept成功后,server 向该连接socket发送数据会怎么样呢???(即将上图中的ctrl+c操作变成send 数据)

wireshark抓包情况:

最后发现客户端回了一个rst复位报文段,终止了此次连接,只剩下listen socket。

上面蓝色对应的是client提前退出,服务端的状态(close_wait)。

红色,是当server accept后,继续send后的情况,此时client发送了rst,导致终止了此次连接。

 

4,模拟client 断开网络连接。

启动客户端程序后,立即断开该客户端的网络连接(连接和断开连接的过程要在服务器启动后的30秒内完成,此时server在sleep)。结果发现accept调用也能正常返回。

A,启动服务器,并相应启动客户端。

其实已经建立了连接,立马剥掉客户端的网线。

待服务器sleep时间到,并调用accept的时候,打印如下:表示accept正常返回。

如果此时服务器send数据会怎么样???

wireshar抓包情况:

由于网络是断开的,这包数据会一直重传,重传的过程中,服务器的状态是:

依然是建立完成态,如果不是send,而是close,表示发送的是fin,那此时应该是fin_wait_1,一直重传fin报文,未收到client的ack。当达到一定重传次数后,服务器发送rst报文,终止了此次连接。

而此时client的状态是:

如果在server重发data的过程中,client网络连接上了,会怎么办呢???

表明client连上后,在某次重传后收到了该数据,并进行啦ack回复。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值