一、相关函数
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类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组,如下图所示:
操作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;
}