UNP学习_I/O复用之select函数实现回射服务器
1、I/O复用模型
??I/O复用是阻塞在select系统调用之上,而不是阻塞在真正的I/O系统调用之上。
文章图片
2、函数声明
#/select.h>
#/time.h>int select(int maxfdpl,
fd_set* readset, //读事件集合
fd_set* writeset, //写事件集合
fd_set* exceptset, //异常事件集合
const struct timeval *timeout
);
struct timeval {
long tv_sec;
//秒
long tv_usec;
//微秒
};
//返回值:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
//具体函数介绍参考UNP-P127
3、select实现的tcp回射服务器
// File Name: select_server.c
// Author: AlexanderGan
// Created Time: Mon 20 Jul 2020 08:32:13 PM CST#include
#include
#include
#include
#include
#include
#include#include
#include
#include
#include
#includeint main(int argc, char* argv[]){
if(argc < 2)
{
printf("eg: ./a.out IP port\n");
}
int port = atoi(argv[1]);
struct sockaddr_in serv_addr, client_addr;
socklen_t serv_len = sizeof(serv_addr);
socklen_t cli_len = sizeof(client_addr);
//创建套接字
int lfd = socket(AF_INET,SOCK_STREAM,0);
printf("lfd = %d\n",lfd);
//初始化服务器
memset(&serv_addr,0,serv_len);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
//端口复用(防止在一端处于TIME_WAIT状态还未释放端口导致端口被占用的问题)
int flag = 1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
//绑定IP和端口
if(bind(lfd,(struct sockaddr*)&serv_addr,serv_len) < 0){
perror("bind error");
exit(1);
}//设置同时监听的最大个数
listen(lfd,36);
printf("Start accept !\n");
//设定最大文件描述符
int maxfd = lfd;
//文件描述符读集合
fd_set reads,temp;
//初始化读fd_set
FD_ZERO(&reads);
FD_SET(lfd,&reads);
while(1){
temp = reads;
int ret = select(maxfd+1,&temp,NULL,NULL,NULL);
if(ret == -1)
{
perror("select error");
exit(1);
}
//客户端发起了新的连接
if(FD_ISSET(lfd,&temp)){
//接受连接,accept不阻塞
int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
if(cfd == -1){
perror("accept error");
exit(1);
}//接受成功后将客户端地址和端口打印出来
char ip[64];
//存放ip表达式
printf("new client IP:%s, Port: %d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
ntohs(client_addr.sin_port));
//将cfd加入到待检测的读集合
FD_SET(cfd,&reads);
//更新最大文件描述符
maxfd = maxfd < cfd ? cfd:maxfd;
}//已经连接的客户端有数据到达
for(int t = lfd+1;
t <= maxfd;
++t){
if(FD_ISSET(t,&temp)){
char buf[1024] = {0};
int len = recv(t,buf,sizeof(buf),0);
if(len == -1){
perror("recv error");
exit(1);
}
else if(len == 0)
{
printf("客户端已经断开了连接\n");
close(t);
FD_CLR(t,&reads);
}
else{
//正常接收数据
printf("recv buf is : %s\n",buf);
send(t,buf,strlen(buf)+1,0);
}
}
}
}
close(lfd);
return 0;
}
4、select函数的优缺点
select优点:
select的最大优势是可以等待多个描述符就绪。
如:在调用recv函数之前,先调用select函数,如果系统没有可读数据那么select函数就会阻塞在这里。当系统存在可读或可写数据时,select函数返回,就可以调用recv函数接 收数据了。
可以看出使用select模型,需要两次调用函数。第一次调用select函数第二次accept函数。使用该模式的好处是:可以等待多个套接字。
【UNP学习_I/O复用之select函数实现回射服务器】select缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
(3)select函数支持的文件描述符数量由FD_SETSIZE设置,默认值是1024。
推荐阅读
- linux网络编程|linux名称解析函数简介——gethostbyname与getaddrinfo
- guagga入门-我们对guagga的修改
- Linux网络编程|UNP学习_UDP广播服务器和客户端的实现
- Linux网络编程|UNP学习_组播服务器与客户端的实现
- Linux网络编程|UNP学习_I/0复用之epoll函数实现回射服务器
- UNP学习_I/O复用之poll函数实现回射服务器