IO多路复用
步骤:
1、首先我们需要创建一张文件描述符集合表
fd_set stFdr;//大小为1024字符
FD_ZERO(&stFdr);//初始化集合表将表全置为0
2、 然后将我们打开的文件的文件描述符添加到这张表里面
FD_SET(n, &stFdr);//将文件描述符加入列表
3、 用select()函数监听所有通道情况,集合表里面的所有通道没有响应的话就阻塞(在最后一个参数置为NULL的时候阻塞生效)有响应的话将该通道对应的集合表对应的bit位置1,然后返回有响应的通道个数。
//函数功能:将有响应的置为1,若没有响应的就阻塞
//参数1:n+1是最大监控多少个从0开始算
// 一般是文件描述符中最大的那个+1
//参数2:所有要读的文件文件描述符的集合
//参数3:所有要写的文件文件描述符的集合
//参数4:其他文件描述符的集合
//参数5:超时设置,若为NULL则表示阻塞。
// 置为0的话只检测文件描述符的状态
// 非0的话在指定时间内监听,若在规定时间内没有响应则超时返回
//返回值:返回响应的数量
int ret = select(n+1, &stFdr,NULL,NULL,NULL);
if(ret <= 0)
{
continue;//不跳出循环继续执行
}
4、 当有响应的时候通过FD_ISSET()函数来判断是哪些通道的响应,进而执行某些对应的数据操作;
//函数功能:判断某一个通道的状态是否有响应
//参数1:文件描述符,
//参数2:描述符集合表地址;
//返回值:若有响应则返回1,没有返回0
FD_ISSET(n, &stFdr);
5、执行对应文件操作;
多路复用案例:
tcp.c
#include "tcp.h" //服务器套接字初始化 //三个参数分别为IP、端口号、允许最大连接 int tcp_server_init(char *ip, int port, int backlog) { //创建套接字文件打通软件和硬件的关节 int skt = socket(AF_INET, SOCK_STREAM, 0); if(-1 == skt) { printf("socker error\n"); return -1; } //在一个端口号和IP绑定多个socket int on = 1; setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)); //将IP和端口号存入结构体 struct sockaddr_in sddr; sddr.sin_family = AF_INET; sddr.sin_port = htons(port); sddr.sin_addr.s_addr = inet_addr(ip); //将套接字和IP、端口号进行绑定 int ret = bind(skt, (struct sockaddr *)&sddr, sizeof(sddr)); if(-1 == ret) { printf("bind error\n"); return -1; } //监听套接字并设置最大链接数量 ret = listen(skt, backlog); if(-1 == ret) { printf("listen error\n"); return -1; } puts("listen...."); return skt; } //服务器连接函数 //参数是打开的套接字文件描述符 int tcp_server_connect(int skt) { struct sockaddr_in cddr; int len = sizeof(cddr); int nfd = accept(skt, (void *)&cddr, &len); /*if(-1 == nfd) { perror("accept error\n"); return -1; }*/ return nfd; } //客户端连接函数 //两个参数分别为IP、端口号 int tcp_app_connect(char *ip, int port) { //创建套接字 int skt = socket(AF_INET, SOCK_STREAM, 0); if(-1 == skt) { printf("socket error\n"); return -1; } //将IP、端口号存入结构体 struct sockaddr_in sddr; sddr.sin_family = AF_INET; sddr.sin_port = htons(port); sddr.sin_addr.s_addr = inet_addr(ip); //绑定 int ret = connect(skt, (struct sockaddr *)&sddr, sizeof(sddr)); if(-1 == ret) { printf("connect error\n"); return -1; } return skt; }
tcp.h
#ifndef _TCP_H #define _TCP_H #include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <pthread.h> #include <arpa/inet.h> #define N 10 //服务器套接字初始化 //三个参数分别为IP、端口号、允许最大连接 int tcp_server_init(char *ip, int port, int backlog); //服务器连接函数 //参数是打开的套接字文件描述符 int tcp_server_connect(int skt); //客户端连接函数 //两个参数分别为IP、端口号 int tcp_app_connect(char *ip, int port); #endif
server.c
#include "tcp.h" int main(void) { int skt = tcp_server_init("0.0.0.0", 8888, 10); //创建一张文件描述符集合表 fd_set table; //清空表 FD_ZERO(&table); //将skt加入表 FD_SET(skt, &table); //监听最大值 int max = skt; int nfd = 0; int ret = 0; int i; char buf[N] = {'\0'}; while(1) { fd_set tableTmp = table; ret = select(max+1, &tableTmp, NULL, NULL, NULL); if(ret <= 0) { continue; } for(i = 0; i < max+1; i++) { //判断是哪路响应 if(FD_ISSET(i, &tableTmp)) { printf("%d号通道响应\n",i); //判断是不是socket通道响应 if(i == skt) { //如果是,再开辟一条软通道传输数据 nfd = tcp_server_connect(skt); if(nfd < 0) { continue; } printf("%d号通道被开通!\n",nfd); //将开通的通道加到真实表中去 FD_SET(nfd, &table); if(max < nfd) { max = nfd; } } else { //若不是socket通道响应则读取数据 //ret = read(i,buf,N);//不能用~~ ret = recv(i,buf,N,0); if(ret > 0) { //对读取到的数据做处理 puts(buf); //write(nfd,buf,N);//不能用~~ send(i,buf,N,0); } else { printf("%d号通道关闭\n",i); //若读取到的数据为空则关闭通道 close(i); FD_CLR(i, &table); } } } } } close(skt); return 0; }
app.c
#include "tcp.h" int main(void) { int skt = tcp_app_connect("192.168.4.8", 8888); char buf[N] = {'\0'}; while(1) { fgets(buf,N,stdin); write(skt,buf,N); read(skt,buf,N); puts(buf); } close(skt); return 0; }