select 版 高并发服务器

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值