linux http服务器 io复用,利用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的结构:

structtimeval{long tv_sec; /*秒*/

long tv_usec; /*微秒*/}

select返回一个小于0的数则说明出错。

2.fd_set

fd_set类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组,如下图所示:

5d53f828991479a1956962bac88acb99.png

操作fd_set的宏有:

#include

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 #include#include#include#include#include#include"wrap.h"

#define MAXLINE 80

#define SERV_PORT 8001

int main(int argc, char **argv)

{inti, maxi, maxfd, listenfd, connfd, sockfd;intnready, client[FD_SETSIZE];

ssize_t n;

fd_set rset, allset;charbuf[MAXLINE];charstr[INET_ADDRSTRLEN];

socklen_t cliaddr_len;structsockaddr_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(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\n",

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\n", 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{intj;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 #include#include#include#include#include#include

void perr_exit(const char *s)

{

perror(s);

exit(1);

}int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)

{intn;

again:if ((n = accept(fd, sa, salenptr)) < 0) {if ((errno == ECONNABORTED) || (errno ==EINTR))gotoagain;elseperr_exit("accept error");

}returnn;

}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, intbacklog)

{if (listen(fd, backlog) < 0)

perr_exit("listen error");

}int Socket(int family, int type, intprotocol)

{intn;if ((n = socket(family, type, protocol)) < 0)

perr_exit("socket error");returnn;

}

ssize_t Read(int fd, void *ptr, size_t nbytes)

{

ssize_t n;

again:if ((n = read(fd, ptr, nbytes)) < 0) {if (errno ==EINTR)gotoagain;else

return -1;

}returnn;

}

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)gotoagain;else

return -1;

}returnn;

}void Close(intfd)

{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;

}returnn;

}static ssize_t my_read(int fd, char *ptr)

{static intread_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)gotoagain;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 == '\n')break;

}else if (rc == 0) {*ptr = 0;return n - 1;

}else{return -1;

}

}*ptr = 0;returnn;

}

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值