相关网络编程函数:http://blog.csdn.net/somehow1002/article/details/72648743
以前各个版本套接字的默认状态是阻塞的,这样带来的时间消耗是巨大的。我们可以修改其为非阻塞版本。
非阻塞式IO方式重写str_cli函数
void str_cli(FILE *fp,int sockfd){
int maxfdp1,val,stdineof;
ssize_t n,nwritten;
fd_set rset,wset;
char to[MAXLINE],fr[MAXLINE];
char *toiptr,*tooptr,*friptr,*froptr;
val=Fcntl(sockfd,F_GETFL,0);
Fcntl(sockfd,F_SETFL,val|O_NONBLOCK);
val=Fcntl(STDIN_FILENO,F_GETFL,0);
Fcntl(STDIN_FILENO,F_SETFL,val|O_NONBLOCK);
val=Fcntl(STDOUT_FILENO,F_GETFL,0);
Fcntl(STDOUT_FILENO,F_SETFL,val|O_NONBLOCK);
toiptr=tooptr=to;
friptr=froptr=fr;
stdineof=0;
maxfdp1=max(max(STDIN_FILENO,STDOUT_FILENO),sockfd)+1;
for(;;){
FD_ZERO(&rset);
FD_ZERO($wset);
if(stdineof==0&&toiptr<&to[MAXLINE])
FD_SET(STDIN_FILENO,&rset);
if(friptr<&fr[MAXLINE])
FD_SET(sockfd,&rset);
if(tooptr!=toiptr)
FD_SET(sockfd,&wset);
if(froptr!=friptr)
FD_SET(STDOUT_FILENO,&wset);
select(maxfdp1,&rset,&wset,NULL,NULL);
if(FD_ISSET(STDIN_FILENO,&rset)){
if((n=read(STDIN_FILENO,toiptr,&to[MAXLINE]-toiptr))<0){
if(errno!=EWOULDBLOCK)
err_sys("read error on stdin");
}else if(n==0){
fprintf(stderr,"EOF on stdin\n");
stdineof=1; //all done with stdin
if(tooptr==toiptr)
shutdown(sockfd,SHUT_WR); //send FIN
}else{
fprintf(stderr,"read %d bytes from stdin\n",n);
toiptr+=n; //just read
FD_SET(sockfd,&wset);
}
}
if(FD_ISSET(sockfd,&rset)){
if((n=read(sockfd,friptr,&fr[MAXLINE]-friptr))<0){
if(errno!=EWOULDBLOCK)
err_sys("read error on socket");
}else if(n==0){
fprintf(stderr,"EOF on socket");
if(stdineof)
return ; //normal termination
else
err_quit("str_cli:server terminated prematurely");
}else{
fprintf(stderr,"read %d bytes from socket\n",n);
friptr+=n;
FD_SET(STDOUT_FILENO,&wset);
}
}
if(FD_ISSET(STDOUT_FILENO,&wset)&&((n=friptr-froptr)>0)){
if((nwritten=write(STDOUT_FILENO,froptr,n))<0){
if(errno!=EWOULDBLOCK)
err_sys("write error to stdout");
}else{
fprintf(stderr,"wrote %d bytes to stdout\n",nwritten);
froptr+=nwritten;
if(froptr==friptr)
froptr=friptr=fr;
}
}
if(FD_ISSET(sockfd,&wset)&&((n=toiptr-tooptr)>0)){
if((nwritten=write(sockfd,tooptr,n))<0){
if(errno!=EWOULDBLOCK)
err_sys("write error to socket");
}else{
fprintf(stderr,"wrote %d bytes to socket\n",nwritten);
tooptr+=nwritten;
if(tooptr==toiptr){
toiptr=tooptr=to;
if(stdineof)
shutdown(sockfd,SHUT_WR); //send FIN
}
}
}
}
}
Fcntl函数如下
int Fcntl(int fd, int cmd, int arg)
{
int n;
if ( (n = fcntl(fd, cmd, arg)) == -1)
perror("fcntl error");
return(n);
}
一个更精简版本的非阻塞的str_cli
这也是第一个超越单进程单线程设计范畴的程序。
父进程处理从客户端到服务器的数据,子进程处理从服务器到客户端的数据
void str_cli(FILE *fp,int sockfd)
{
pid_t pid;
char sendline[MAXLINE],recvline[MAXLINE];
//child:server -> stdout
if((pid=fork())==0){
while(readline(sockfd,recvline,MAXLINE)>0)
fputs(recvline,stdout);
kill(getppid(),SIGTREM); //in case parent still running when server terminated
exit(0);
}
//parent:stdin -> server
while(fgets(sendline,MAXLINE,fp)!=NULL)
Writen(sockfd,sendline,strlen(sendline));
shutdown(sockfd,SHUT_WR); //EOF on stdin, send FIN
pause();
return ;
}
关于执行时间
unix网络编程作者测试str_cli得出时间如下:
最初的停-等版本:354.0s
select+阻塞式IO版本:12.3s
非阻塞式IO版本:6.9s
非阻塞fork版本:8.7s
线程化版本:8.5s(http://)
从结果上看,非阻塞IO版本时间更快,但是鉴于其过高的复杂性,使用非阻塞fork版本是值得的