0、实验环境
服务器端程序,只连接客户端,不read,即:不读缓冲区
运行服务器程序。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
int main()
{
int lfd= -1,clifd =-1;
fd_set rfd_set;
struct timeval tv;
struct sockaddr_in s_sockaddr,c_sockaddr;
int c_sockaddr_len = sizeof(c_sockaddr);//必须赋值
s_sockaddr.sin_family = AF_INET;
s_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
s_sockaddr.sin_port = htons(8888);
lfd = socket(AF_INET,SOCK_STREAM,0);
if(lfd<0)
{
perror("socket:");
exit(-1);
}
int opt=1;
setsockopt(lfd,SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(opt));
if((bind(lfd,(struct sockaddr*)&s_sockaddr,sizeof(s_sockaddr))<0))//
{
perror("bind:");
close(lfd);
exit(-1);
}
if(listen(lfd,2)<0)
{
perror("listen:");
close(lfd);
exit(-1);
}
int cfd[5];//5个客户端
int maxfd = lfd;
for(int i = 0;i<5;i++)
{
cfd[i]= -1;
}
while(1)
{
maxfd = lfd;
FD_ZERO(&rfd_set);//清空
FD_SET(lfd,&rfd_set);
for(int i = 0;i<5;i++)
{
if(cfd[i]== -1)
continue;
FD_SET(cfd[i],&rfd_set);
maxfd=(maxfd>=cfd[i])?maxfd:cfd[i];
}
tv = {5,0}; //设置超时时间
int ret = select(maxfd+1,&rfd_set,NULL,NULL,&tv);
if(ret<0)
{
perror("select");
}else if(ret ==0){
printf("select over time\n");
continue;
}else {
if(FD_ISSET(lfd,&rfd_set))
{
if((clifd = accept(lfd,(struct sockaddr*)&c_sockaddr,(socklen_t*)&c_sockaddr_len))<0)
{
perror("accept");
}
for(int i = 0;i<5;i++)
{
if(cfd[i]== -1)
{
cfd[i]= clifd;
int flags = fcntl(cfd[i], F_GETFL, 0); //获取文件的flags值。
fcntl(cfd[i], F_SETFL, flags | O_NONBLOCK); //设置成非阻塞模式;
break;
}
}
printf("client link,ip=%s,port= %d,clifd =%d\n",inet_ntoa(c_sockaddr.sin_addr),
ntohs(c_sockaddr.sin_port),clifd);
}
// for(int i=0;i<5;i++)
// {
// if(cfd[i]==-1)continue;
// if(FD_ISSET(cfd[i],&rfd_set))
// {
// char buf[1024];
// int nread = 0;
// nread = read(cfd[i],buf,1024);
// if(nread <=0)
// {
// printf("read:%s,errno=%d\n",strerror(errno),errno);
// close(cfd[i]);
// cfd[i] = -1;
// }else{
// printf("buf=%s,cfd=%d\n",buf,cfd[i]);
// }
// printf("before read ,cfd=%d\n",cfd[i]);
// read(cfd[i],buf,1024);
// }
// }
}
}
return 0;
}
1、默认的阻塞客户端。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/syscall.h>
#include <fcntl.h>
int main()
{
int cfd;
int nwrite;
char sendbuf[1000*1024]={1};
sockaddr_in s_sockaddr;
s_sockaddr.sin_family = AF_INET;
//字符类型转化为网络序(大端序)
inet_aton("127.0.0.1",&s_sockaddr.sin_addr);
s_sockaddr.sin_port = htons(8888);
cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd <0)
{
perror("socket:");
exit(-1);
}
if(connect(cfd,(struct sockaddr*)&s_sockaddr,sizeof(s_sockaddr))<0)
{
perror("connect:");
close(cfd);
exit(-1);
}
//int flags = fcntl(cfd, F_GETFL, 0); //获取文件的flags值。
// if(flags<0)
//{
// printf("get fail\n");
// }
// if(fcntl(cfd, F_SETFL, flags | O_NONBLOCK)<0)
// {
// printf("set fail\n");
// }//设置成非阻塞模式;
while(1)
{
nwrite = write(cfd,sendbuf,sizeof(sendbuf));
if(nwrite<=0)
{
perror("write:");
}else{
printf("nwrite=%d\n",nwrite);
}
sleep(1);
}
return 0;
}
实验结果:
阻塞的客户端程序,当缓冲区空间不足的时候,会阻塞等待。
2、非阻塞客户端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/syscall.h>
#include <fcntl.h>
int main()
{
int cfd;
int nwrite;
char sendbuf[1000*1024]={1};
sockaddr_in s_sockaddr;
s_sockaddr.sin_family = AF_INET;
//字符类型转化为网络序(大端序)
inet_aton("127.0.0.1",&s_sockaddr.sin_addr);
s_sockaddr.sin_port = htons(8888);
cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd <0)
{
perror("socket:");
exit(-1);
}
if(connect(cfd,(struct sockaddr*)&s_sockaddr,sizeof(s_sockaddr))<0)
{
perror("connect:");
close(cfd);
exit(-1);
}
int flags = fcntl(cfd, F_GETFL, 0); //获取文件的flags值。
if(flags<0)
{
printf("get fail\n");
}
if(fcntl(cfd, F_SETFL, flags | O_NONBLOCK)<0)
{
printf("set fail\n");
}//设置成非阻塞模式;
while(1)
{
nwrite = write(cfd,sendbuf,sizeof(sendbuf));
if(nwrite<=0)
{
perror("write:");
}else{
printf("nwrite=%d\n",nwrite);
}
sleep(1);
}
return 0;
}
注意:我们将设置非阻塞代码放在connect的后面了。若放在connect之前会出现如下错误:
实验结果:
我们看到发送缓冲区满了,非阻塞的客户端,会立即返回。
4、总结:
实验现象跟该文描述的情况一致。
即:当阻塞的write(或send)时,发送缓冲区空间不够的时(剩余空间比要发送的字节小),函数就会阻塞等待。
非阻塞的write(或send)时,write函数会尽可能的向发送缓冲区里copy数据,当缓冲区满时,才返回错误。