实现一个简单的select模型

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<unistd.h>
#include<arpa/inet.h>
#include<cctype>
#include<sys/select.h>

using namespace std;

const int PORT = 7777;

int main()
{
    int server_socket,client_socket;
    //用来存储需要监听的文件描述符
    int client_fd[FD_SETSIZE];
    //最大的文件描述符,select的返回结果
    int max_fd,select_num;
    //每次监听的集合,总的集合(因为select会改变参数,所以需要备份)
    fd_set res_set,all_set;
    sockaddr_in server_addr,client_addr;
    socklen_t server_addrlen;
    //maxi为监听集合中最后一个描述符的下标
    int i,maxi=-1;
    char client_ip[BUFSIZ],str[BUFSIZ];
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == server_socket)
    {
        perror("create server socket error:");
        exit(1);
    }
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if(-1 == bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)))
    {
        perror("bind error:");
        exit(1);
    }
    if(-1 == listen(server_socket, 32))
    {
        perror("listen error:");
        exit(1);
    }
    max_fd = server_socket;
    for(int k=0;k<FD_SETSIZE;k++)
    {
        client_fd[k] = -1;
    }
    FD_ZERO(&all_set);
    FD_SET(server_socket, &all_set);
    cout<<"waiting for clients\n";
    while(1)
    {
        res_set = all_set;
        select_num = select(max_fd+1, &res_set, NULL, NULL, NULL);
        if(select_num < 0)
        {
            perror("select error:");
            exit(1);            
        }
        /*
            当server_socket出现在了res_set中表示,有新的客户端请求连接
            把新的客户端的文件描述符放到监听集合中
        */
        if(FD_ISSET(server_socket, &res_set))
        {
            client_socket = accept(server_socket,(struct sockaddr *)&client_addr,&server_addrlen);
            cout<<"received from:"<<inet_ntop(AF_INET, &client_addr.sin_family, client_ip, BUFSIZ)<<" port:"
                <<ntohs(client_addr.sin_port)<<endl;
            for(i=0;i<FD_SETSIZE;i++)
            {
                if(client_fd[i] < 0)
                {
                    client_fd[i] = client_socket;
                    break;
                }
            } 
            if(FD_SETSIZE == i)
            {
                cout<<"too mant clients";
                exit(1);
            }
            FD_SET(client_socket, &all_set);
            if(client_socket > max_fd)
                max_fd = client_socket;
            if(i > maxi)
                maxi = i;
            if(--select_num == 0)
                continue;
        }
        /*
            下面这个循环用来查看在client_fd中保存的文件描述符,是否有读事件发生
            如果有的话,这里简单处理,转为大写再发回去
        */
        for(i=0;i<=maxi;i++)
        {
            if(client_fd[i] < 0)
                continue;
            if(FD_ISSET(client_fd[i], &res_set))
            {
                int read_len;
                if((read_len = read(client_fd[i], str, sizeof(str))) == 0)
                {
                    cout<<"client close\n";
                    close(client_fd[i]);
                    FD_CLR(client_fd[i], &all_set);
                    client_fd[i] = -1;
                }else if(read_len > 0)
                {
                    cout<<"have read sth from client\n";
                    for(int j=0;j<read_len;j++)
                    {
                        str[j] = toupper(str[j]);
                    }
                    int wr_ret = write(client_fd[i], str, read_len);
                    if(-1 == wr_ret)
                    {
                        perror("write error:");
                        exit(1);
                    }
                }
                if(--select_num == 0)
                    break;
            }
        }
    }
    close(server_socket); 
    return 0;
}

这里引出一些问题

1.如果客户端发送的数据长度,大于read函数中的第三个参数怎么办?

经过测试,假设用户发的数据长度为4,read的第三个参数为2,所以会每次从缓冲区中读取2个字节,然后由于缓冲区中的数据没有被完全消化掉,所以这个客户端对应的文件描述符还是会有读事件产生,整个操作执行三次,知道清空读缓冲区,才不会有读事件发生

2.总的思想就是,把服务器的socket和客户端链接进来的socket放在res_set中,看是否有读事件发生,如果服务器端的socket有读事件发生,则表示有新的客户端链接进来了,加入到all_set中,然后看链接进来的客户端socket是否有读事件发生,这些socket存储在client_fd中,如果有的话,进行一下简单操作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巴塞罗那的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值