select多路IO复用的应用场景:
优点:
缺点:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds:监听的所有文件描述符中,最大文件描述符+1
readfds: 读文件描述符监听集合。 传入、传出参数
writefds: 写文件描述符监听集合。 传入、传出参数 (不常用) NULL
exceptfds: 常文件描述符监听集合 传入、传出参数 NULL
timeout: 设置监听超时时长 (NULL: 阻塞监听)(0: 非阻塞监听,轮询)
返回值:
> 0: 所有监听集合(3个)中, 满足对应事件的总数。
0: 没有满足监听条件的文件描述符
-1: errno
功能: 清空一个文件描述符集合
void FD_ZERO(fd_set *set);
功能:将待监听的文件描述符,添加到监听集合中
void FD_SET(int fd, fd_set *set);
功能: 将一个文件描述符从监听集合中 移除。
void FD_CLR(int fd, fd_set *set);
功能:判断一个文件描述符是否在监听集合中
int FD_ISSET(int fd, fd_set *set);
返回值: 在:1;不在:0
#include "cs_dev.h"
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#define SOCKPROT 8888
int main(void)
{
int sockfd = 0;
int c_fd = 0;
char dstIp[16] = {0};
int c_addr_len = 0;
char buf[1024] = {0};
struct sockaddr_in sockaddr;
struct sockaddr_in c_addr;
c_addr_len = sizeof(c_addr);
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
Setsockopt(sockfd); //设置端口复用
Bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
Listen(sockfd, 10);
int j = 0;
fd_set readfds, allfds;
int nread = 0;
int nready = 0;
int maxfd = sockfd;
int client[FD_SETSIZE]; //客户端连接文件描述符数组
int maxi = -1; //记录client数组最大下标位置
for (int i=0; i < FD_SETSIZE; i++) {
client[i] = -1;
}
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
allfds = readfds; //select返回 readfds修改为监听到的文件描述符集合; allfds作为备份
while (1) {
readfds = allfds;
nready = select(maxfd + 1, &readfds, NULL, NULL, NULL); //阻塞等待,成功返回 nready > 0
if (nread == -1)
perr_exit("select");
if (FD_ISSET(sockfd, &readfds)) { //新连接处理
c_fd = Accept(sockfd, (struct sockaddr*)&c_addr, &c_addr_len);
printf("IP:%s PROT:%d connect...\n", \
inet_ntop(AF_INET, &c_addr.sin_addr, dstIp, sizeof(dstIp)), \
ntohs(c_addr.sin_port));
for (j=0; j < FD_SETSIZE; j++) { //将新连接的客户端文件描述符存放入client数组
if (client[j] == -1) {
client[j] = c_fd;
break;
}
}
if (j == FD_SETSIZE) //超过最大连接数
fputs("too many clients connect...\n", stderr);
else if (c_fd > maxfd) //获取最大文件描述符
maxfd = c_fd;
FD_SET(c_fd, &allfds); //加入到备份集合
if (j > maxi) //更新客户端文件描述数组最大值下标
maxi = j;
if (--nready == 0) //事件处理完毕
continue;
}
for (int i=0; i <= maxi; i++) { //读数据
if ((client[i] != -1) && (FD_ISSET(client[i], &readfds))) {
memset(buf, 0, sizeof(buf));
nread = Read(client[i], buf, sizeof(buf));
if (nread == 0) { //客户端断开连接
Close(client[i]);
FD_CLR(client[i], &allfds);
client[i] = -1;
for (; i >= 0; i--) {//更新客户端文件描述数组最大值下标
if (client[i] != -1) {
maxi = i;
break;
}
}
} else if (nread > 0) { //处理动作
Write(client[i], buf, nread);
Write(0, buf, nread);
}
if (--nready == 0) //事件处理完毕
break;
}
}
}
Close(sockfd);
return 0;
}