select函数
多路IO转接服务器
select函数参数
select相关函数
select实现多路IO转接服务器
/*************************************************************************
> File Name: server.c
> Author: shaozheming
> Mail: 957510530@qq.com
> Created Time: 2022年03月05日 星期六 10时48分07秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <ctype.h>
#include <arpa/inet.h>
#include "wrap.h"
#define SERV_PORT 6666
int main(int argc, char* argv[])
{
int i, j, n, nready;
int maxfd = 0; //最大的文件描述符
int listenfd, connfd;
char buf[BUFSIZ]; //数据缓冲区
struct sockaddr_in clie_addr, serv_addr; //服务端和客户端socket
socklen_t clie_addr_len;
listenfd = Socket(AF_INET, SOCK_STREAM, 0); //设置tcp
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //设置端口复用
bzero(&serv_addr, sizeof(serv_addr)); //清0
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //bind绑定
Listen(listenfd, 128); //设置监听数量
fd_set rset, allset; /* 读事件文件描述符集合,allset用来暂时存放 */
maxfd = listenfd;//当socket之后,返回的是当前可用的文件描述符最小的值比如3, 3之后的就没人用了,最大就是3
FD_ZERO(&allset);
FD_SET(listenfd, &allset); //将监听的文件描述符放到读文件集合中
while(1) {
rset = allset; //通过修改allset这个临时变量来修改rset
//并且rset是传入传出,参数,每次出来都会被修改,所以不能再下面直接修改rset
nready = select(maxfd + 1, &rset, NULL, NULL, NULL); //阻塞监听
if(nready < 0)
perr_exit("select error!\r\n");
if(FD_ISSET(listenfd, &rset)) { //说明有新的客户端连接请求
/* 建立客户端连接 */
clie_addr_len = sizeof(clie_addr);
connfd = Accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len); //connfd通信
FD_SET(connfd, &allset); //因为新建立客户端了,需要再次添加到监听集合里
if(maxfd < connfd) maxfd = connfd;
if(0 == --nready)
continue; //当减少到0了,也就是nready是1,说明只有listenfd有响应,无客户端响应,直接回到while
}
for(i = listenfd + 1; i <= maxfd; ++i) {
/* 检测哪个客户端有数据 */
if(FD_ISSET(i, &rset)) {
if((n = Read(i, buf, sizeof(buf))) == 0) {
//当服务器关闭链接时
Close(i);
FD_CLR(i, &allset); //解除监控
} else if(n > 0) {
for(j = 0; j < n; ++j) {
buf[j] = toupper(buf[j]);
}
Write(i, buf, n);
}
}
}
}
Close(listenfd);
return 0;
}
select 优缺点
缺点: 监听上限受文件描述符限制,最大1024
检测满足条件的fd,自己添加业务逻辑提高小,提高了编码难度
优点:唯一一个可以跨平台
自定义数组提升效率
/*************************************************************************
> File Name: server.c
> Author: shaozheming
> Mail: 957510530@qq.com
> Created Time: 2022年03月05日 星期六 10时48分07秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <ctype.h>
#include <arpa/inet.h>
#include "wrap.h"
#define INET_ADDRSTRLEN 16
#define SERV_PORT 6666
int main(int argc, char* argv[])
{
int i, j, n, nready;
int maxi, client[FD_SETSIZE]; //自定义数组,防止遍历1024个文件描述符
int maxfd = 0; //最大的文件描述符
int listenfd, connfd, sockfd;
char buf[BUFSIZ], str[INET_ADDRSTRLEN]; //数据缓冲区
struct sockaddr_in clie_addr, serv_addr; //服务端和客户端socket
socklen_t clie_addr_len;
listenfd = Socket(AF_INET, SOCK_STREAM, 0); //设置tcp
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //设置端口复用
bzero(&serv_addr, sizeof(serv_addr)); //清0
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //bind绑定
Listen(listenfd, 128); //设置监听数量
fd_set rset, allset; /* 读事件文件描述符集合,allset用来暂时存放 */
maxfd = listenfd;//当socket之后,返回的是当前可用的文件描述符最小的值比如3, 3之后的就没人用了,最大就是3
maxi = -1;
for(i = 0; i < FD_SETSIZE; ++i)
client[i] = -1;
FD_ZERO(&allset);
FD_SET(listenfd, &allset); //将监听的文件描述符放到读文件集合中
while(1) {
rset = allset; //通过修改allset这个临时变量来修改rset
//并且rset是传入传出,参数,每次出来都会被修改,所以不能再下面直接修改rset
nready = select(maxfd + 1, &rset, NULL, NULL, NULL); //阻塞监听
if(nready < 0)
perr_exit("select error!\r\n");
if(FD_ISSET(listenfd, &rset)) { //说明有新的客户端连接请求
/* 建立客户端连接 */
clie_addr_len = sizeof(clie_addr);
connfd = Accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len); //connfd通信
printf("received from %s at PORT %d \n",
inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
ntohs(clie_addr.sin_port));
/* 找client中没有使用的位置 */
for(i = 0; i < FD_SETSIZE; ++i) {
if (client[i] < 0) {
client[i] = connfd;
break;
}
}
/* 如果达到了监听上限 */
if(i == FD_SETSIZE) {
fputs("too many clients\n", stderr);
exit(1);
}
FD_SET(connfd, &allset); //因为新建立客户端了,需要再次添加到监听集合里
if(maxfd < connfd) maxfd = connfd;
/* 保证maxi存的是client[]最后一个下标 */
if(i > maxi) maxi = i;
if(0 == --nready)
continue; //当减少到0了,也就是nready是1,说明只有listenfd有响应,无客户端响应,直接回到while
}
for(i = 0; i <= maxi; ++i) {
if((sockfd = client[i]) < 0)
continue;
/* 检测哪个客户端有数据 */
if(FD_ISSET(sockfd, &rset)) {
if((n = Read(sockfd, buf, sizeof(buf))) == 0) {
//当服务器关闭链接时
Close(sockfd);
FD_CLR(sockfd, &allset); //解除监控
client[i] = -1;
} else if(n > 0) {
for(j = 0; j < n; ++j) {
buf[j] = toupper(buf[j]);
}
Write(sockfd, buf, n);
Write(STDOUT_FILENO, buf, n);
}
if(--nready == 0) break;
}
}
}
Close(listenfd);
return 0;
}
主要是给自己看的,所以肯定会出现很多错误哈哈哈哈哈
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律