尝鲜select多路复用
问题:
如何增强服务端能力,同时支持多个客户端?
Linux的设计哲学
一切皆文件
- Linux中的文件是什么?
- 狭义:
- 文件系统中物理意义上的文件(逻辑上关联的数据集合)
- 广义
- 设备,管道,内存…
- Linux管理的一切对象
- 狭义:
- 理解文件描述符(File Descriptor)
- 文件描述符是一个非负整数值,本质上一个句柄
- 一切对用户(程序员)透明的资源标识都可以看做句柄
- 用户使用文件描述符(句柄)与内核交互
- 内核通过文件描述符操作对应资源的数据结构
- 一切皆文件的意义
- 统一各种设备的操作方式(open,read,write,close)
- 如:
- IO设备(命令行,显示器)
- 网络设备(网卡)
- …
Linux文件操作编程模式
- 编程实验——以文件类型操作命令行
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int iofd = 0;
int len = 0;
char str[] = "Titai,soft\n";
write(iofd, str, sizeof(str));
len = read(0, str, 5);
str[len] = '\0';
printf("%s\n",str);
return 0;
}
事件相关函数的分类
- 阻塞式函数
- 函数调用后需要等待某个事件发生后才会返回
- 非阻塞式函数
- 函数调用后能够及时返回(仅标记等待的事件)
- 事件发生后以回调方式传递
- 阻塞vs轮询
- 轮询指依序询问每一个相关设备是否需要服务的方式
- 轮询可用于解决函数导致程序无法继续执行的问题
神奇的selec()函数
- select()用于监视指定的文件描述符是否产生事件
- 可通过轮询的方式检测目标事件(事件产生则标记发生变化)
- 根据事件类型做出具体处理(如:读取数据)
int select(int maxfd,
fd_set* readset,
fd_set* writeset,
fd_set* exceotset,
const struct timeval* timeout);
select()函数的使用步骤
select()相关数据类型及操作
fd0 | fd1 | fd2 | fd3 | … |
---|---|---|---|---|
0 | 1 | 1 | 0 | … |
FD_ZERO(fd_set* fdset);//将fd_set变量的所有位设置为0
FD_SET(int fd,fd_set* fdset);//在fd_set中指定需要监听的fd
FD_CLR(int fd, fd_set* fdset);//在fd_set中剔除fd,不再监听
FD_ISSET(int fd, fd_set* fdset);//在fd_set查看是否包含fd
代码示例
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <malloc.h>
int main(void)
{
char s[] = "hello world\n";
fd_set reads = {0};
fd_set tmps = {0};
int iofd = 0;
struct timeval timeout = {0};
int r = -1;
int len = 0;
FD_ZERO(&reads);
FD_SET(iofd, &reads);
while( 1 )
{
tmps = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 10*1000;
r = select(1, &tmps, 0, 0, &timeout);
if(r > 0)
{
len = read(1, s, sizeof(s));
s[len] = 0;
printf("s[]:%s\n",s);
}
else if( r == 0)
{
//so something
static int count = 0;
usleep(10 * 1000);
count++;
if(count > 100)
{
printf("do something else\n");
count = 0;
}
}
else
{
break;
}
}
return 0;
}