标签:
如果TCP客户同时处理两个输入: 标准输入和TCP套接字. 那么如果客户阻塞于标准输入期间(例如fgets()), 套接字收到的FIN或者RST信息就不会及时得到处理. 所以这里需要使用I/O复用, 是由select和poll这两个函数支持的.
为了更好地理解I/O复用, 这里总结一下UNIX下的5种I/O模型的基本区别:
阻塞式I/O : 默认情况下, 所有的套接字都是阻塞的. 一些慢系统调用也是阻塞的.
非阻塞式I/O : 当所请求的I/O操作非得把本进程投入睡眠才能完成时, 不要把本进程投入睡眠, 而是返回一个错误.
I/O复用: 用select的优势在于我们可以等待多个描述符
信号驱动式I/O: 让内核在描述符就绪时发送SIGIO信号通知我们, 这种模式的优势在于等待数据报到达期间进程是不被阻塞的.
异步I/O: 告知内核启动某个操作, 并让内核在整个操作完成后通知我们.
为了实现I/O复用, 这里使用select函数, 该函数允许进程指示内核等待多个事件中的任何一个发生, 并只在一个或多个事件发生或者经历过一段指定的时间再唤醒它.
#include <sys/select.h>#include <sys/time.h>int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
select函数的返回: 若有就绪描述符则为其数目, 若超时则为0, 若出错则为-1
#include "unp.h"#include <time.h>intmain(int argc, char **argv){int i, maxi, maxfd, listenfd, connfd, sockfd;int nready, client[FD_SETSIZE];ssize_t n;fd_set rset, allset;char buf[MAXLINE];socklen_t clilen;struct sockaddr_in cliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM,0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=htonl(INADDR_ANY);servaddr.sin_port=htons(1234);Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);maxfd=listenfd;maxi=-1;for(i=0;i<FD_SETSIZE;i++)client[i]=-1;FD_ZERO(&allset);FD_SET(listenfd, &allset);for(;;){rset=allset;nready=Select(maxfd+1, &rset, NULL, NULL, NULL);if(FD_ISSET(listenfd, &rset)){ // new client connectionclilen=sizeof(cliaddr);connfd=Accept(listenfd, (SA*) &cliaddr, &clilen);for(i=0;i<FD_SETSIZE;i++){if(client[i]<0){client[i]=connfd;break;}}if(i==FD_SETSIZE)err_quit("too many clients");FD_SET(connfd, &allset);if(connfd>maxfd)maxfd=connfd;if(i>maxi)maxi=i;if(--nready<=0)continue;}for(i=0;i<=maxi;i++){ //check all clients for dataif( (sockfd=client[i])<0)continue;if(FD_ISSET(sockfd, &rset)){if((n=Read(sockfd, buf, MAXLINE))==0){//connection closed by clientClose(sockfd);FD_CLR(sockfd, &allset);client[i]=-1;}elseWriten(sockfd, buf, n);if(--nready<=0)break; //no more readable descriptors}}}return 0;}
#include "unp.h"void cli_echo(FILE *fp, int sockfd){int maxfdp1, stdineof;fd_set rset;char buf[1000];int n;stdineof=0;FD_ZERO(&rset);for(;;){if(stdineof==0)FD_SET(fileno(fp),&rset);FD_SET(sockfd,&rset);maxfdp1=max(fileno(fp), sockfd)+1;Select(maxfdp1, &rset, NULL, NULL, NULL);if(FD_ISSET(sockfd, &rset)) {//socket is readableif( (n=Read(sockfd, buf, 1000))==0){if(stdineof==1)return;//normal terminationelseerr_quit("cli_echo: server terminated prematurely");}Write(fileno(stdout),buf,n);}if(FD_ISSET(fileno(fp), &rset)){//input is readableif( (n=Read(fileno(fp),buf,1000))==0){stdineof=1;Shutdown(sockfd, SHUT_WR);FD_CLR(fileno(fp), &rset);continue;}Writen(sockfd, buf, n);}}}intmain(int argc, char **argv){int sockfd;struct sockaddr_in servaddr;if(argc!=2)err_quit("usage: tcpcli <IPaddress>");sockfd=Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(1234);Inet_pton(AF_INET,argv[1], &servaddr.sin_addr);Connect(sockfd, (SA*) &servaddr, sizeof(servaddr));cli_echo(stdin,sockfd);exit(0);}
标签:
原文地址:http://www.cnblogs.com/gremount/p/5785566.html