#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/select.h>
int main()
{
// 1 创建套接字
int lfd = socket(AF_INET, SOCK_STREAM, 0);
// 2 绑定
// 2.1 复用端口
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 2.2 设置IP和端口的结构体
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr)); // 清空结构体
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 2.3 绑定
bind(lfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 3 设置同时监听上限
listen(lfd, 128);
// 4 自定义的文件描述符数组,需要监听的文件描述符就放进client数组,用于select后遍历判断事件
int client[FD_SETSIZE]; // FD_SETSIZE = 1024
int i; // 循环因子
for(i = 0; i< FD_SETSIZE; i++)
client[i] = -1; // 将文件描述符数组元素都初始化为-1,代表各个位置可用
// 5 准备select集合
fd_set set, orgset; // orgset是原始集合,set用于保存传出集合
FD_ZERO(&orgset);
FD_SET(lfd, &orgset); // 在原始集合中监听lfd,准备select参2
// 6 监听读事件
int nfds = lfd; // 记录最大文件描述符编号,准备select参1
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr); // 用于接收客户端信息
char buf[BUFSIZ] = {0}; // 读写缓存区
while(1)
{
set = orgset; // select中参2是传入传出属性,因为不希望改变原始监听集合orgset,所以使用set
int nready = select(nfds+1, &set, NULL, NULL, NULL);
if(nready < 0)
{
perror("select err");
exit(1);
}
// 6.1 如果是新连接
if(FD_ISSET(lfd, &set))
{
int cfd = accept(lfd, (struct sockaddr*)&client_addr, &client_len); // 获得连接描述符
char ip[16] = {0};
printf("conn from %s at %d\n",inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)),ntohs(client_addr.sin_port)); // 打印客户端信息
for(i = 0; i< FD_SETSIZE; i++) // 更新遍历client数组,将cfd添加进去
{
if(client[i] < 0)
{
client[i] = cfd; // 给到数组中可用的最小位置
break; // 退出for循环
}
}
if(i == FD_SETSIZE) // 如果数组中没有位置
{
printf("client too many\n");
break; // 退出while循环,执行returm
}
FD_SET(cfd, &orgset); // 监听cfd , 更新select参数2
if(cfd > nfds) // 增加判断条件是因为,一旦nfds前有可用位置,nfds不需要更新
nfds = cfd; // 更新select参数1
if(--nready == 0)
continue; // 就一个事件,而且是新连接,没必要往下执行了
}
// 6.2 ,如果不是以上情况,那就是普通读事件
for(i = 0; i < FD_SETSIZE ; i++) // 遍历client数组,看哪个文件描述符变化
{
if(client[i] < 0)
continue; // 如果是-1,代表空位,没必要向下走了,继续for循环
if(FD_ISSET(client[i], &set)) //判断传出set中是否有client[i],如果有,代表有读事件发生
{
int n = read(client[i], buf, sizeof(buf));
if(n < 0)
{
perror("read err");
close(client[i]); // 该文件描述符无用了,关闭
FD_CLR(client[i],&orgset); // 更新select参数2,注意:nfds不能动
client[i] = -1; // 将client数组元素置为初始态
}
if(n == 0)
{
printf("client[%d] closed\n", i);
close(client[i]);
FD_CLR(client[i],&orgset);
client[i] = -1;
}
if(n > 0)
{
write(client[i], buf, n);
write(STDOUT_FILENO, buf, n);
}
if(--nready == 0)
break; // 为了防止循环1024
}
}
}
close(lfd);
return 0;
}
select 版 高并发服务器
最新推荐文章于 2024-03-20 21:39:46 发布