基于select的回声服务器
程序员文章站
2024-03-23 10:14:40
...
一:函数理解
(1)
要想通过select函数处理多个客户端的连接,我们得需要一个函数把这些文件描述符集中起来吧,不然怎么处理多个客户端的链接呢?先来看select函数的第2,3,4参数,这几个参数,都是fd_set类型的变量,用来把文件描述符集中起来,第2个参数是集中想读的文件描述符,第3个参数是集中想写的文件描述符,第4个参数是集中异常的文件描述符。后面会举例子,先记这么多吧。
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
(2)
我们怎么通过fd_set变量集中文件描述符?或者清除不感兴趣的文件描述符?有以下4个函数完成这些操作。
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set);用来清除描述词组set的全部位
当客户端连入时,我们怎么知道是那个文件描述符发生了变化?以select函数来说,如果我们关心读事件,那么当可读时,相应的变化就会记录在select函数的第二个实参里,但是,我们不知道这个变化了的文件描述符的值是多少,所以要遍历,这也是select函数的缺点之一,所以它的第一个参数总是记录着最大的文件描述符是多少,因为后面遍历需要。
二:例程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 100
void error_handling(char *buf);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
struct timeval timeout;
fd_set reads, cpy_reads;
socklen_t adr_sz;
int fd_max, str_len, fd_num, i;
char buf[BUF_SIZE];
if(argc!=2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
FD_ZERO(&reads);
FD_SET(serv_sock, &reads);
/*目前serv_sock文件描述符的数值是最大的*/
fd_max=serv_sock;
while(1)
{
/*这个动作要有,因为select函数返回时
cpy_reads都会发生变化,而后续有新的客户端连 入时,也需要通过reads记录相应的文件描述符,所以每次循环到select函数时,cpy_reads的值都会是所有的文件描述符的集合*/
cpy_reads=reads;
timeout.tv_sec=5;
timeout.tv_usec=5000;
if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1)
break;
if(fd_num==0)
continue;
for(i=0; i<fd_max+1; i++)
{
if(FD_ISSET(i, &cpy_reads))
{
if(i==serv_sock) // connection request!
{
adr_sz=sizeof(clnt_adr);
clnt_sock=
accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
FD_SET(clnt_sock, &reads);
if(fd_max<clnt_sock)
fd_max=clnt_sock;
printf("connected client: %d \n", clnt_sock);
}
else // read message!
{
str_len=read(i, buf, BUF_SIZE);
if(str_len==0) // close request!
{
FD_CLR(i, &reads);
close(i);
printf("closed client: %d \n", i);
}
else
{
write(i, buf, str_len); // echo!
}
}
}
}
}
close(serv_sock);
return 0;
}
void error_handling(char *buf)
{
fputs(buf, stderr);
fputc('\n', stderr);
exit(1);
}
推荐阅读
-
linux回声服务器系列(2)_select实现
-
基于select的回声服务器
-
Linux环境编程 基于UDP通信协议的回声服务器
-
Java网络编程一个基础案例分析,基于TCP协议的客户端上传文件给服务器端(单线程版)
-
【2019-2020春学期】数据库作业8:SQL练习5 - SELECT(嵌套查询EXISTS、集合查询、基于派生表的查询)
-
基于Socket编程实现一个简单的Web服务器
-
CentOS 6.5基于OpenVPN的VPN服务器构建 博客分类: VPN/OTP openvpnvpn动态口令
-
SPHiveDB: 基于 sqlite 的数据库服务器 博客分类: OpenSource项目 应用服务器SQLite数据结构MongoDBSQL
-
基于Lucene的Java搜索服务器Elasticsearch安装使用教程
-
基于Lucene的Java搜索服务器Elasticsearch安装使用教程