赞
踩
原文:https://blog.csdn.net/hyman_c/article/details/52979317
刚刚接触网络编程时,很容易被客户端和服务器之间的交互搞晕,例如各种函数的阻塞和非阻塞就让人头疼。例如在我的印象中 linux中用于用于对文件描述符进行读写的write()函数和read()函数是非阻塞函数,但是在一次网络通信的试验中发现它们又是阻 塞的,后来man了一下write和read,发现其文档中都有下面一句话:
ERRORS
EAGAIN The file descriptor fd refers to a file other than a socket and has been
marked non-blocking (O_NONBLOCK), and the write would block.
翻译过来就是:
如果文件描述符不是socket的话,该函数是非阻塞的,否则该函数是阻塞的。 为了验证这个问题,进行如下实验,主要验证read函数的阻塞特性(验证write函数需要填充满输出缓冲区,我不会!!!)
首先编写socket服务器端程序:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<sys/socket.h> #include<arpa/inet.h> #define BUF_SIZE 100 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); 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 error_handling(char* message) { fputs(message,stderr); fputc('\n',stderr); exit(1); }
请留意在调用 listen()开启监听后的sleep(10),我们让服务器睡了10s中,以此来验证客户端程序在read时发生阻塞,服务器 睡醒后才结束阻塞状态
客户端的代码:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/socket.h> #include<arpa/inet.h> #include<time.h> #define BUF_SIZE 10000000 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"); while(1) { memset(buf,0,BUF_SIZE); fputs("请输入数据:",stdout); fgets(buf,BUF_SIZE,stdin); if(!strcmp(buf,"q\n")||!strcmp(buf,"Q/n")) break; str_len=write(sock,buf,strlen(buf)); puts("writed,begin block"); print_time(); sizeof(buf,0,sizeof(buf)); while(recv_len<str_len) recv_len+=read(sock,buf,BUF_SIZE-1); puts("end block"); print_time(); 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); }
客户端在write之后,打印出"writed,begin block"的信息,然后read函数开始阻塞,只有在服务端sleep完,返回数据后才会结束阻塞,结果如下:
服务端-
[Hyman@Hyman-PC echoSever]$ ./serv 9190
time now:[2016-10-09 09:28:04]
clnt:127.0.0.1 connected
客户端-
[Hyman@Hyman-PC echoSever]$ ./clnt 127.0.0.1 9190
connect succeed
请输入数据:helo
writed,begin block
time now:[2016-10-09 09:28:01]
end block
time now:[2016-10-09 09:28:04]
服务器传回信息:helo
以上代码,足以证明了read函数的阻塞特性。。。。。
Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL24
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。