- 1、select函数介绍
- 2、select代码
- 3、socket常用函数出错处理封装
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 参数: nfds 监听所有文件描述符中,最大文件描述符+1 readfds:读 文件描述符监听集合(这三个参数都是传入传出参数) writefds:写 文件描述符监听集合 exceptfds 异常 文件描述符监听集合 timeout: >0 设置监听超时长 NULL 阻塞监听 0 非阻塞监听,轮询 返回值: >0 所有监听集合中, 满足对应事件的总数 0 没有满足监听条件的文件描述符 -1 设置errno void FD_ZERO(fd_set* set); // 清空一个文件描述符集合 fd_set rset; FD_ZERO(rset); void FD_SET(int fd,fd_set* set); // 将待监听的文件描述符,添加到监听集合中 FD_SET(3,&rset); void FD_CLR(int fd,fd_set* set); // 将一个文件描述符从监听中移除 FD_CLR(4,&rset); int FD_ISSET(int fd,fd_set* set); // 判断一个文件描述符是否在监听集合中 FD_ISSET(4,&rset); 返回值:在1,不在0
之前写的多线程和多进程服务器,都是通过,accept进行阻塞监听,来一个客户端(子进程、线程)就给他创建一个通信文件描述符,进行读写
要是客户端太多,就会创建太多线程或进程
2、select代码select:先将建立连接的文件描述符添加到集合中,监听select 的读事件,确定是建立连接,就调用accept,此时不会阻塞,将通信的文件描述符添加到数组中,并且也添加到集合中,要是select返回数大于1表示,还有其他读事件,则去一一处理
#include#include #include #include #include #include #include #include #include #include #include #include #include "socket_wrap.h" int main() { int listenfd,connfd,sockfd; char buf[BUFSIZ]; bzero(buf,sizeof(buf)); struct sockaddr_in clie_addr,serv_addr; socklen_t clie_addr_len = sizeof(clie_addr); listenfd = Socket(AF_INET,SOCK_STREAM,0); // 创建套接字 int opt = 1; setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); // 端口复用函数 bzero(&serv_addr,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(9999); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); Bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); // 绑定ip port Listen(listenfd,128); // 设置连接上限 fd_set rset,allset; // all监听的,rset是传出的 FD_ZERO(&allset); // 将allset集合全部置零 FD_SET(listenfd,&allset); // 将用于连接文件描述符添加到集合中 int ret,maxfd=listenfd; // maxfd表示当前集合中最大的文件描述符术 int n,i,j,maxi=-1; // maxi存储最后一个下标 char client[FD_SETSIZE],str[INET_ADDRSTRLEN]; // FD_SETSIZE 1024 INET_ADDRSTRLEN 16 for(i=0;i maxi) // 保证maxi中存储最后一个下标 maxi = i; FD_SET(connfd,&allset); // 将通信文件描述符添加到集合中 if(maxfd < connfd) // max中存储最大的文件描述符 maxfd = connfd; if(ret == 1) // 说明select 只返回了一个并且listenfd continue; } for(i=0;i<=maxi;i++) { if((sockfd=client[i])<0) continue; if(FD_ISSET(sockfd,&rset)) { if((n=Read(sockfd,buf,sizeof(buf)))==0) { Close(sockfd); FD_CLR(sockfd,&allset); client[i] = -1; printf("客户端:%s:%d已经断开n",inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,str,sizeof(str)), ntohs(clie_addr.sin_port)); } else if(n>0) { for(j=0;j 缺点:监听上限文件描述符限制,最大1024 检测满足条件fd,自己添加业务逻辑提高小,提高了编码难度 优点: 跨平台3、socket常用函数出错处理封装socket_wrap.h
#ifndef __WRAP_H_ #define __WRAP_H_ void perr_exit(const char* s); int Accept(int fd,struct sockaddr* sa,socklen_t* salenptr); int Bind(int fd,const struct sockaddr* sa,socklen_t salen); int Connect(int fd,const struct sockaddr* sa,socklen_t salen); int Listen(int fd,int backlog); int Socket(int family,int type,int protocol); ssize_t Read(int fd,void* ptr,size_t nbytes); ssize_t Write(int fd,const void* ptr,size_t nbytes); int Close(int fd); ssize_t Readn(int fd,void* vptr,size_t n); ssize_t Writen(int fd,const void* vptr,size_t n); static ssize_t my_read(int fd,char* ptr); ssize_t Readline(int fd,void* vptr,size_t maxlen); #endif // !__WRAP_H_socket_wrap.c
#include#include #include #include #include #include #include #include #include #include "socket_wrap.h" void perr_exit(const char* s) { perror(s); exit(1); } int Accept(int fd,struct sockaddr* sa,socklen_t* salenptr) { int n; again: if((n=accept(fd,sa,salenptr))<0) { if(errno == EConNABORTED || (errno==EINTR)) goto again; else perr_exit("accept error"); } return n; } int Bind(int fd,const struct sockaddr* sa,socklen_t salen) { int n; if((n=bind(fd,sa,salen))<0) { perr_exit("bind error"); } return n; } int Connect(int fd,const struct sockaddr* sa,socklen_t salen) { int n; if((n=connect(fd,sa,salen))<0) perr_exit("connect error"); return n; } int Listen(int fd,int backlog) { int n; if((n=listen(fd,n))<0) { perr_exit("listen error"); } return n; } int Socket(int family,int type,int protocol) { int n; if((n=socket(family,type,protocol))<0) perr_exit("socket error"); return n; } ssize_t Read(int fd,void* ptr,size_t nbytes) { ssize_t n; again: if((n=read(fd,ptr,nbytes))<0) { if(errno==EINTR) goto again; else return -1; } return n; } ssize_t Write(int fd,const void* ptr,size_t nbytes) { ssize_t n; if((n=write(fd,ptr,nbytes))<0) perr_exit("write error"); return n; } int Close(int fd) { int n; if((n=close(fd))<0) perr_exit("close error"); return n; } ssize_t Readn(int fd,void* vptr,size_t n) { size_t nleft; // unsigned int 剩余未读取字节数 ssize_t nread; // int 实际读到的字节数 char* ptr; ptr = vptr; nleft = n; while (nleft>0) { if((nread = read(fd,ptr,nleft))<0) { if(errno == EINTR) nread = 0; else return -1; } else if(nread == 0) break; nleft -= nread; ptr+=nread; } return n-nread; } ssize_t Writen(int fd,const void* vptr,size_t n) { size_t nleft; ssize_t nwritten; const char* ptr; ptr = vptr; nleft = n; while(nleft>0) { if((nwritten=write(fd,ptr,nleft))<=0) { if(nwritten<0 && errno==EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n; } static ssize_t my_read(int fd,char* ptr) { static int read_cnt; static char* read_ptr; static char read_buf[100]; if(read_cnt<=0) { again: if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0) { if(errno == EINTR) goto again; return -1; } else if(read_cnt == 0) return 0; read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return 1; } // 传出参数 vptr ssize_t Readline(int fd,void* vptr,size_t maxlen) { ssize_t n,rc; char c,*ptr; ptr = vptr; for(n=1;n 欢迎分享,转载请注明来源:内存溢出
评论列表(0条)