网络编程(23)—— socket客户端连接上服务端是在listen之后而非在accept之时

 我们知道在TCPIP中客户端同服务端建立连接,需要三次握手:

   第一次:客户端向服务端请求发起连接,发送SYN

   第二次:服务端收到客户端的请求后,向客户端回送ACKSYN

   第三次:客户端接收到服务端可以连接的信息,再向服务端发送ACK,表示收到服务端信息完成连接。

   在刚刚接触网络编程时,很长一段时间都以为只有服务端调用accept后,客户端才会connect成功,但是实际上只要服务端开启listen,客户端就会连接成功,并可以发送数据,下面就通过试验验证这一点:

    以下的服务端的代码:

#include<stdio.h>
#inlude<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<time.h>
#define BUF_SIZE 100 

void print_time();
void error_handling(char* message);

int main(int argc,char* argv[])
{
	int serv_sock,clnt_sock;
	struct sockaddr_in serv_addr,clnt_addr;
	int clnt_addr_sz;
	int str_len,i,j;
	char buf[BUF_SIZE];

	if(argc!=2)
	{
		printf("Usage %s <port>\n",argv[0]);
		exit(1);
	}
	//创建socket
	serv_sock=socket(AF_INET,SOCK_STREAM,0);
	if(serv_sock==-1)
		error_handling("socket error");

	//填充地址信息
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_addr.sin_port=htons(atoi(argv[1]));

	//socket和ip地址的绑定
	if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
		error_handling("bind error");

	//开启监听
	if(listen(serv_sock,5)==-1)
		error_handling(" listen error");
    	sleep(10);
	fputs("end sleep:",stdout);
	print_time();
	for(i=0;i<5;i++)
	{
		clnt_addr_sz=sizeof(clnt_addr);
		clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_sz);
		if(clnt_sock==-1)
			error_handling("accept error");
		else
			printf("clnt:%s connected\n",inet_ntoa(clnt_addr.sin_addr));
		//接受数据
		while(1)
		{
			str_len=read(clnt_sock,buf,BUF_SIZE);
				write(clnt_sock,buf,str_len);
			memset(buf,0,sizeof(buf));
			if(str_len<=0)
				break;
		}
		close(clnt_sock);
	}
	close(serv_sock);
	return 0;
}
void print_time()
{
    time_t now=time(0);
	struct tm* ptm=localtime(&now);
	char buf[256]={0};
	sprintf(buf,"time now:[%02d-%02d-%02d %02d:%02d:%02d]",
			ptm->tm_year+1900,
			ptm->tm_mon+1,
			ptm->tm_mday,
			ptm->tm_hour,
			ptm->tm_min,
			ptm->tm_sec);
	puts(buf);
}



void error_handling(char* message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

  请留意在调用 listen()开启监听后的sleep(10),我们让服务器睡了10s中,以此来验证客户端程序依然可以正常的connect,同时sleep结束后调用了一下print_time(),打印了一下当前的时间。接下来是客户端的代码:

  

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<time.h>
#define BUF_SIZE 100

void print_time();
void error_handling(const char* message);

int main(int argc,char* argv[])
{
	int sock;
	struct sockaddr_in serv_addr;
	int str_len;
	char buf[BUF_SIZE];
	int recv_len=0;
	//创建socket
	sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock==-1)
		error_handling("socket error");
	//准备地址
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
	serv_addr.sin_port=htons(atoi(argv[2]));

	//链接
	if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
		error_handling("connect error");
	else
	        puts("connect succeed");
	print_time();
	while(1)
	{
		memset(buf,0,BUF_SIZE);
		fputs("请输入数据:",stdout);
		fgets(buf,BUF_SIZE,stdin);
		if(!strcmp(buf,"q\n")||!strcmp(buf,"Q/n"))
			break;
		print_time();
		str_len=write(sock,buf,strlen(buf));
		puts("writed");
		print_time();
		sizeof(buf,0,sizeof(buf));
		while(recv_len<str_len)
			recv_len+=read(sock,buf,BUF_SIZE-1);
		buf[str_len]=0;
		printf("服务器传回信息:%s\n",buf);
	}
	close(sock);
	return 0;
}


void print_time()
{
    time_t now=time(0);
	struct tm* ptm=localtime(&now);
	char buf[256]={0};
	sprintf(buf,"time now:[%02d-%02d-%02d %02d:%02d:%02d]",
			ptm->tm_year+1900,
			ptm->tm_mon+1,
			ptm->tm_mday,
			ptm->tm_hour,
			ptm->tm_min,
			ptm->tm_sec);
	puts(buf);
}


void error_handling(const char* message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}	

        我们在进行connect服务端后,如果connect成功,就会打印出来一个“connected succeed”的字符串,同时利用print_time打印出当前的时间,只要打印出来的时间早于服务器中打印出来的时间就证明了我们的猜想,那么请看打印的结果:


客户端中:

[Hyman@Hyman-PC echoSever]$ ./clnt 127.0.0.1 9190
connect succeed
time now:[2016-10-08 11:31:53]
请输入数据:

服务器中:
[Hyman@Hyman-PC echoSever]$ ./serv 9190
end sleep:time now:[2016-10-08 11:32:02]
clnt:127.0.0.1 connected

        从上面的结果中,很明显晚于服务器中打印的时间明显晚于客户端中的时间,也就是说其实只要服务器开启listen,客户端就可以connect并写入数据,服务器端会将客户端连接信息和发送信息保存在输入缓冲区中,等sleep结束就会进行读取。


        也可以用netstat查看当前的端口监听的情况:

[Hyman@Hyman-PC echoSever]$ netstat -an >> sock.txt



把netstat查看的监听情况放到sock.txt中是因为信息过多,终端无法容纳,打开该文件可以看到9190端口的socket已经是established





Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL23






































































































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值