利用select模型实现多个客户端和一个服务端的CS模型——多路IO复用技术
- select
同时监听多个文件描述符,将监控的操作交给内核去处理。
委托内核监控可读、可写、异常事件
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
struct timeval* timeout);
param:
nfds: 告诉内核要监控文件描述符的范围,一般取值为最大文件描述符+1
readfds: 告诉内核监控哪些文件描述符,并且内核告诉程序哪些文件描述有变化(变化的置1)
writefds: 同上,但一般监听读事件,写事件是主动发生的
exceptfds: 异常事件
timeout: 超时时间, NULL表示永久阻塞,直到有事件发生;
0表示不阻塞,不管有没有事件发生,都会立刻返回;
>0表示阻塞的时长
return:
成功返回发生变化的文件描述符的个数
- 相关宏函数
定义文件描述符集
fd_set set;
将fd从set集合中清除
void FD_CLR(int fd, fd_set* set);
判断fd是否在set集合中
void FD_ISSET(int fd, fd_set* set);
将fd添加到set集合中
void FD_SET(int fd, fd_set* set);
清空文件描述符集
void FD_ZERO(fd_set* set);
select优缺点
- 优点
一个进程可以支持多个客户端 - 缺点:
1、会设计到用户区到内核区的来会拷贝
2、当客户端有多个连接,但少数活跃的情况,select效率低。比如只有最后一个文件描述符在发数据
3、最大支持1024个客户端连接,是因为内核最大支持1024个文件描述符
服务端代码如下,客户端代码不变
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<netinet/in.h>
#include<errno.h>
int main()
{
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd < 0)
{
perror("socket error");
return -1;
}
struct sockaddr_in serv;
bzero(&serv, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
if(ret < 0)
{
perror("bind error");
return -1;
}
listen(lfd, 128);
fd_set readset;
fd_set tmpset;
FD_ZERO(&readset);
FD_SET(lfd, &readset);
int nready;
int maxfd = lfd;
int cfd;
int n;
char buf[1024];
while(1)
{
tmpset = readset;
nready = select(maxfd+1, &tmpset, NULL, NULL, NULL);
if(nready <= 0)
{
continue;
}
//有客户端连接请求到来
if(FD_ISSET(lfd, &tmpset))
{
cfd = accept(lfd, NULL, NULL);
FD_SET(cfd, &readset);
maxfd = cfd > maxfd? cfd : maxfd;
//说明只有监听描述符被置为1,后面可跳过
if(--nready == 0)
{
continue;
}
}
//有数据发来
//先有的lfd,后有的cfd
for(int i=lfd+1; i<=maxfd;i++)
{
if(FD_ISSET(i, &tmpset))
{
memset(buf, 0, sizeof(buf));
n = read(cfd, buf, sizeof(buf));
if(n <= 0)
{
close(i);
FD_CLR(i, &readset);
printf("read error or client close\n");
continue;
}
printf("n == [%d], buf == [%s]\n", n, buf);
for(int j=0; j<n; j++)
{
buf[j] = toupper(buf[j]);
}
write(i, buf, n);
}
}
}
close(lfd);
return 0;
}
【下一篇】:poll模型