socket编程select函数处理多个I/O并发

//本程序仅对客户端进行处理多个并发的I/O //服务器端 #include #include #include #include #include #include //使用signal函数 #include //使用wait函数#include #include #include #include #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0)//读确定大小的包 //ssize_t有符号数,size_t为无符号数 ssize_t readn(int fd, void *buf, size_t count) { size_t nleft=count; //剩余字节数 ssize_t nread; //已经接受的字节数 char *bufp = (char*)buf; // while(nleft>0) { if((nread=read(fd, bufp, nleft))<0) { if(errno==EINTR)//信号中断 { continue; } return -1; //否则出错 } else if(nread==0)//表示对方关闭传送 { return count-nleft; //返回已读字节数 } bufp+=nread; //进行指针偏移 nleft -= nread; } return count; } ssize_t writen(int fd, const void *buf, size_t count) { size_t nleft=count; //剩余字节数 ssize_t nwritten; //已经写入的字节数 char *bufp = (char*)buf; // while(nleft>0) { if((nwritten=write(fd, bufp, nleft))<0) { if(errno==EINTR)//信号中断 { continue; } return -1; //否则出错 } else if(nwritten==0) { continue; } bufp+=nwritten; //进行指针偏移 nleft -= nwritten; } return count; } ssize_t recv_peek(int sockfd, void *buf,size_t len) { while(1) { //recv函数只用于套接口 //recv函数读取后,不将数据在缓冲区清除 int ret= recv(sockfd, buf, len, MSG_PEEK); if(ret == -1 && errno == EINTR) continue; return ret; } }ssize_t readline(int sockfd, void *buf, size_t maxline) { int ret; //设置窥探返回值 int nread; //设置已窥探字符数 char *bufp = (char*)buf; //缓存buf int nleft = maxline; //设置maxline为包最大长度,nleft为剩余需读取字符数 while(1) { ret=recv_peek(sockfd, bufp, nleft); if(ret<0) { return ret; } else if(ret=0) { return ret; //对方终止了传送 }nread=ret; int i; //检测有没有‘\n’字符,有则读取 for(i=0; i0) }int main(void) { //当客户端结束时,服务器还维持一个僵尸进程 /*signal(SIGCHLD, SIG_IGH);//使用signal忽略SIGCHLD信号(第一种方法)*/ signal(SIGCHLD, handle_sigchld); //(第二种方法) int listenfd; //建立套接字 if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) /* if((listenfd=socket(PF_INET,SOCK_STREAM, 0))<0)*/ ERR_EXIT("socket"); //建立服务器地址 struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); */ /*inet_aton("127.0.0.1",&servaddr.sin_addr); */int on = 1; //为了使服务器处于TIME_WATTING状态,能再次绑定地址 if(setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))<0) ERR_EXIT("setsockopt"); //绑定地址 if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0) ERR_EXIT("bind"); //监听套接字 if(listen(listenfd,SOMAXCONN)<0)//套接字变为被动套接字 ERR_EXIT("listen"); //存放客户端地址 struct sockaddr_in peeraddr; socklen_t peerlen =sizeof(peeraddr); int conn; //接受accept的套接字 //建立子进程,以便于多个连接进入服务器 pid_t pid; while(1) { if((conn=accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen))<0)//套接字变为主动,且以后使用accept返回的套接字; ERR_EXIT("accept"); printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); pid=fork(); if(pid==-1) ERR_EXIT("fork"); if(pid==0) { close(listenfd); //子进程不再监听套接字 ehco_srv(conn); exit(EXIT_SUCCESS); } else close(conn); } return 0; } //客户端 #include #include #include #include #include #include #include #include #include #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0)//读确定大小的包 //ssize_t有符号数,size_t为无符号数 ssize_t readn(int fd, void *buf, size_t count) { size_t nleft=count; //剩余字节数 ssize_t nread; //已经接受的字节数 char *bufp = (char*)buf; // while(nleft>0) { if((nread=read(fd, bufp, nleft))<0) { if(errno==EINTR)//信号中断 { continue; } return -1; //否则出错 } else if(nread==0)//表示对方关闭传送 { return count-nleft; //返回已读字节数 } bufp+=nread; //进行指针偏移 nleft -= nread; } return count; } ssize_t writen(int fd, const void *buf, size_t count) { size_t nleft=count; //剩余字节数 ssize_t nwritten; //已经写入的字节数 char *bufp = (char*)buf; // while(nleft>0) { if((nwritten=write(fd, bufp, nleft))<0) { if(errno==EINTR)//信号中断 { continue; } return -1; //否则出错 } else if(nwritten==0) { continue; } bufp+=nwritten; //进行指针偏移 nleft -= nwritten; } return count; } ssize_t recv_peek(int sockfd, void *buf,size_t len) { while(1) { //recv函数只用于套接口 //recv函数读取后,不将数据在缓冲区清除 int ret= recv(sockfd, buf, len, MSG_PEEK); if(ret == -1 && errno == EINTR) continue; return ret; } }ssize_t readline(int sockfd, void *buf, size_t maxline) { int ret; int nread; char *bufp = (char*)buf; int nleft = maxline; while(1) { ret=recv_peek(sockfd, bufp, nleft); if(ret<0) { return ret; } else if(ret=0) { return ret; //对方终止了传送 }nread=ret; int i; for(i=0; i sock) maxfd =fd_stdin; else maxfd = sock; while(1) { //因为select的参数既为输入参数,也为输出参数,即rset一直在变化 //所以可以用FD_SET不能放在循环外; FD_SET(fd_stdin, &rset); FD_SET(sock, &rset); nready = select(maxfd+1, &rset, NULL, NULL, NULL); if(nready == -1) ERR_EXIT("select"); if(nready == 0) continue; //因为select的参数既为输入参数,也为输出参数 //所以可以用FD_ISSET判断事件是否发生 if(FD_ISSET(sock, &rset)) { int ret = readline(sock, recvbuf,sizeof(recvbuf)); //先接受包头 if(ret==-1)//读取失败则退出 ERR_EXIT("readn"); else if(ret==0)//服务器关闭 { printf("server close\n"); break; } fputs(recvbuf,stdout); //打印标准输出//memset(&sendbuf,0, sizeof(sendbuf)); //该段只执行接受服务器数据 memset(&recvbuf, 0, sizeof(recvbuf)); } if(FD_ISSET(fd_stdin, &rset)) { if(fgets(sendbuf, sizeof(sendbuf), stdin) ==NULL) break; writen(sock, sendbuf, strlen(sendbuf)); memset(&sendbuf,0, sizeof(sendbuf)); } } close(sock); } int main(void) { int sock[5]; int i; for(i=0; i<5; i++)//处理某个客户端并发多个聊天程序 { if((sock[i]=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) /* if((listenfd=socket(PF_INET,SOCK_STREAM<0))<0)*/ ERR_EXIT("socket"); struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); /*servaddr.sin_addr.s_addr = htonl(INADDR_ANY); */ servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); /*inet_aton("127.0.0.1",&servaddr.sin_addr); */ //发起连接 if(connect(sock[i],(struct sockaddr*)&servaddr,sizeof(servaddr))<0) ERR_EXIT("connect"); } ehco_cli(sock); return 0; }

    推荐阅读