Welcome 微信登录

首页 / 操作系统 / Linux / 利用select实现IO多路复用TCP服务端

一、相关函数 1.  int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout); int maxfdp: 该参数是指集合中所有文件描述符的范围, 即所有文件描述符的最大值加1; fd_set *readset: 该参数是我们所关心的文件是否可读的文件描述符的集合, 如果这个集合中有个文件可读了,那select返回一个大于0的数,表示有文件可读了 fd_set *writeset:......可写...... fd_set *exceptset: ......异常发生...... timeval *timeout:该参数是select的超时参数,这个参数使select处于三种状态:(1)timeout传入NULL,则select一直等到文件状态有变化时才返回,这段时间一直处于阻塞状态。(2):timeout 传入0,则select会立即返回(非阻塞),如果文件状态有变化则返回一个大于0的值没有变化则返回0;(3)timeout传入一个大于0的数,则select在timeout时间内阻塞,一旦文件状态有变化就会返回,超时后不管怎样都会返回值同样是文件状态右边话就返回一个大于0的值,无变化则返回0; timeval的结构:struct timeval{long tv_sec; /*秒 */long tv_usec;/*微秒 */ } select返回一个小于0的数则说明出错。 2.fd_set fd_set类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组,如下图所示: 操作fd_set的宏有:#include <sys/select.h> int FD_ZERO(int fd, fd_set *fdset); int FD_CLR(int fd, fd_set *fdset); int FD_SET(int fd, fd_set *fd_set); int FD_ISSET(int fd, fd_set *fdset); FD_ZERO宏将一个 fd_set类型变量的所有位都设为 0,使用FD_SET将变量的某个位置位。清除某个位时可以使用 FD_CLR,我们可以使用FD_ISSET来测试某个位是否被置位(即状态是否发生了变化)。 当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位二、TCP服务端代码#include <string.h>#include <stdio.h>#include <stdlib.h>#include <netinet/in.h>#include <arpa/inet.h>#include <ctype.h>#include "wrap.h"#define MAXLINE 80#define SERV_PORT 8001int main(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];char str[INET_ADDRSTRLEN];socklen_t cliaddr_len;struct sockaddr_incliaddr, 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(SERV_PORT);Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));Listen(listenfd, 20);maxfd = listenfd;maxi = -1;for (i = 0; i < FD_SETSIZE; i++)client[i] = -1; /* -1 indicates available entry */FD_ZERO(&allset);FD_SET(listenfd, &allset);for ( ; ; ) {rset = allset; /* structure assignment */nready = select(maxfd+1, &rset, NULL, NULL, NULL);if (nready < 0)perr_exit("select error");if (FD_ISSET(listenfd, &rset)) { /* new client connection */cliaddr_len = sizeof(cliaddr);connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);printf("received from %s at PORT %d ",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));for (i = 0; i < FD_SETSIZE; i++)if (client[i] < 0) {client[i] = connfd; /* save descriptor */break;}if (i == FD_SETSIZE) {fputs("too many clients ", stderr);exit(1);}FD_SET(connfd, &allset); /* add new descriptor to set */if (connfd > maxfd)maxfd = connfd; /* for select */if (i > maxi)maxi = i; /* max index in client[] array */if (--nready == 0)continue; /* no more readable descriptors */}for (i = 0; i <= maxi; i++) {/* check all clients 714 for data */if ( (sockfd = client[i]) < 0)continue;if (FD_ISSET(sockfd, &rset)) {if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {Close(sockfd);FD_CLR(sockfd, &allset);client[i] = -1;} else {int j;for (j = 0; j < n; j++)buf[j] = toupper(buf[j]);Write(sockfd, buf, n);}if (--nready == 0)break; /* no more readable descriptors */}}}} 备:封装原始linux函数wrap.c:#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <sys/socket.h>#include <netinet/in.h>#include <string.h>#include <unistd.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;}void Bind(int fd, struct sockaddr *sa, socklen_t salen){if (bind(fd, sa, salen) < 0)perr_exit("bind error");}void Connect(int fd, const struct sockaddr *sa, socklen_t salen){if (connect(fd, sa, salen) < 0)perr_exit("connent error");}void Listen(int fd, int backlog){if (listen(fd, backlog) < 0) perr_exit("listen error");}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;again:if ((n = write(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;else return -1;}return n;}void Close(int fd){if (close(fd) == -1)perr_exit("close error");}ssize_t Readn(int fd, void *vptr, size_t n){size_t nleft;ssize_t nread;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 - nleft;}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;}ssize_t Readline(int fd, void *vptr, size_t maxlen){ssize_t n, rc;char c, *ptr;ptr = vptr;for (n = 1; n < maxlen; n++) {if ((rc = my_read(fd, &c)) == 1) {*ptr++ = c;if (c == " ") break;} else if (rc == 0) {*ptr = 0;return n - 1;} else {return -1;}}*ptr = 0;return n;}本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-01/139222.htm