linux相关的IO编程,进程,线程,网络编程等;方法及代码解析:https://pan.baidu.com/s/1IVn3xfCaRPnQklOrcvsQ_g 密码:vi76
IO多路转接技术之select
IO多路转接技术select/poll/epool:解决服务器一次只能处理一个客户端的问题,及并发处理client端的请求。IO多路则可以间接实现并行处理多个客户端的请求,及同时处理来自client的请求。
IO多路转接技术的实现:
构建一张存储客户端套接字(文件)描述符的列表
调用具有阻塞功能的监听函数对文件描述符表进行监听,一旦有对应的描述符所代表的client有请求时,立即将该描述符返回。
返回时,会告诉进程有多少描述符要进行IO操作(即client请求处理)。
IO多路转接-select函数原型;
int select(int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
参数分析:
int nfds://第1024(最高位)个文件描述符,即第1023个
fd_set *readfds://读取描述符流指针
fd_set *writefds://写描述符流指针
fd_set *exceptfds://异常描述符流指针
struct timeval timeout://设值或NULL,NULL时为永久阻塞,检测到fd变化时返回
结构体参数:
struct timeval {
long tv_sec; / seconds /
long tv_usec; / microseconds */
};
文件描述符操作函数及类型;
select函数的优点:跨平台
缺点:
2.select 工作过程分析(文件描述符0,1,2分别默认为标准输入,标准输出,标准出错)
多个客户端:A B C D E ...连接到服务器,分别对应文件描述符3,4,100,101,102,103...
1)变量的定义:
fd_set reads,temp;//用户空间
FD_SET(3,&reads);//
2)初始化select(103+1,&reads,NULL,NULL,NULL);
103+1为内核拿到的初始化文件描述符表的大小,从3...103均制值为1
3)当内核遍历描述符表时,内核检测对应描述符的客户端有数据发来时,对应描述符的内容不变乃为1,否则置为0
.如下图示:fd_set表的变化,左边为初始化得到的表;右边为内核遍历检测完成后,修改之后的表,该表将会印射到用户空间的reads所指的表。然后服务器就遍历新的reads表,对应的描述符为1则表明该描述符对应的客户端发来了数据,为0则没有发来数据。
3,select多路转接代码
1)伪码
int main()
{
//socket
int lfd =socket();
//bind
bind();
//listen
listen();
//create socket list
fd_set reads,temp;
//init
fd_zero(&reads);
//listen lfd add to reads
fd_set(lfd,&reads);
int maxfd=lfd;
while(1)
{
//select委托检测
temp=reads;
int ret=select(maxfd+1,&reads,NULL,NULL,NULL);
//检测lfd是否在以temp为高位地址的监听表中
if(fd_isset(lfd,&temp))
{
//在则,接受新连接
int cfd =accept();
//cfd加入读集合
fd_set(cfd,&reads);
//更新maxfd
maxfd=(maxfd
2)实现多客户端连接的server代码:
建立select_server.c文件;内容如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_LISTEN 100
int main()
{
int server_fd,client_fd;
struct sockaddr_in server,client;
int client_len=sizeof(client);
//initial socket object
server.sin_family = AF_INET;
server.sin_addr.s_addr =htonl(INADDR_ANY);
server.sin_port =htons(8888);
//socket
server_fd=socket(AF_INET,SOCK_STREAM,0);
if(server_fd == -1)
{
printf("socket error\n");
exit(1);
}
printf("server_fd=%d\n",server_fd);
//bind
if(bind(server_fd,(struct sockaddr *)&server,sizeof(server))==-1)
{
printf("bind error\n");
close(server_fd);
exit(1);
}
//listen
if(listen(server_fd,MAX_LISTEN)==-1)
{
printf("listen error\n");
close(server_fd);
exit(1);
}
printf("listening...........\n");
//max fd
int maxfd=server_fd;
//create socket list
fd_set reads,temp;
//init
FD_ZERO(&reads);
//listen server add to reads
FD_SET(server_fd,&reads);
while(1)
{
//select
temp=reads;
int ret=select(maxfd+1,&temp,NULL,NULL,NULL);
if(ret ==-1)
{
printf("select error\n");
close(server_fd);
exit(1);
}
//verify server_fd whether listening
if(FD_ISSET(server_fd,&temp))
{
//accept
client_fd=accept(server_fd,(struct sockaddr *)&client,&client_len);
if(client_fd==-1)
{
printf("accept error\n");
close(server_fd);
exit(1);
}
char ip[64];
printf("new client IP:%s Port: %d\n",inet_ntop(AF_INET,&client.sin_addr.s_addr,ip,sizeof(ip)),ntohs(client.sin_port));
//add client_fd into reads
FD_SET(client_fd,&reads);
//update maxfd
maxfd = (maxfd <=client_fd ? client_fd : maxfd);
}
//operation send to client
for(int i=server_fd+1;i<=maxfd;++i)
{
if(FD_ISSET(i,&temp))
{
char read_buf[1024] ={0};
int len=recv(i,read_buf,sizeof(read_buf),0);
if(len==-1)
{
printf("read error\n");
exit(1);
}
else if(len==0)
{
printf("client exit\n");
close(i);
//delete client_fd from reads
FD_CLR(i,&reads);
}
else
{
printf("client says :%s\n",read_buf);
send(i,"hello client",12,0);
printf("send to client\n");
}
}
}
}
close(server_fd);
return 0;
}