由select实现的简单服务端实例

函数原型:
     

  #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>
       
       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

       #include <sys/select.h>

       int pselect(int nfds, fd_set *readfds, fd_set *writefds,
                   fd_set *exceptfds, const struct timespec *timeout,
                   const sigset_t *sigmask);

参数解释:

  1. nfds ---- 所有文件描述符的最大范围, 比最大的文件描述符大一

  2. fd_set *readfds ---- 结构体指针,此项中应该包含文件描述符,监视其可读状态,有一个文件可读就返回一个大于0的数,文件描述符到文件末尾也是可以读的,都不可读根据timeout参数进行等待,判断超时则返回0, 发生错误返回负数,可写入NULL,不关注可读状态

  3. fd_set *writefds ---- 监视集合内的文件描述符是否有空间可写入,其他同上

  4. fd_set *exceptfds ---- 同上,监视文件错误异常

  5. struct timeval *timeout :超时时间,会使select 进入三种状态

    (1)传入值为NULL,select 进入阻塞态,直到监视的文件描述符发生状态变化(ready)(实例: timeout == NULL )

    (2)若将两个时间值设为0秒和0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值 ( timeout->tv_sec == 0 &&timeout->tv_usec == 0不等待)

    (3)timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,时间内发生变化会返回正值,否则返回0

  6. 结构体 struct fd_set :集合中存放的是文件描述符(file descriptor),即文件句柄,可根据宏来人为操作,fd_set类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组

  7.  

    (1)void FD_CLR(int fd, fd_set *set); ---- 将一个给定的文件描述符从集合中删除

    (2)int FD_ISSET(int fd, fd_set *set); ---- 检查集合中指定的文件描述符是否可以读写

    (3) void FD_SET(int fd, fd_set *set); ---- 将一个给定的文件描述符加入集合之中

    (4)void FD_ZERO(fd_set *set); ---- 清空所有的描述符

  8. 结构体 struct timeval : 包含秒和微秒两个变量,long tv_sec; 与 long tv_usec;

    代码实例:

#include <iostream>
#include "common_n.h"
#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>

using namespace std;

#define MAX_CLIENT 200
#define PORT 8888

int client_cnt;
int client_socketfd[MAX_CLIENT + 5];
int max_fd;


void do_accept(int listen_socket) {
    int new_socket;
    new_socket = accept(listen_socket, NULL, NULL);
    if (new_socket < 0) {
        perror("accept : new_socket");
        return ;
    }
    cout << "----加入成功----" << endl;
    for (int i = 0; i < MAX_CLIENT; i++) {
        if (client_socketfd[i] != -1) continue;
        client_socketfd[i] = new_socket;
        client_cnt ++;
        break;
    }
}

void do_recv(fd_set rfds) {
    int client_fd;
    int leave = 0;
    for (int i = 0; i < client_cnt; i++) {
        if (client_socketfd[i] == -1) continue; 
        client_fd = client_socketfd[i];
        if (FD_ISSET(client_fd, &rfds)) {
            char buff[1024] = {0};
            char ip[20] = {0};
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            getpeername(client_fd, (struct sockaddr*)&addr, &len);
            int t = recv(client_fd, buff, 1024, 0);
            if (t <= 0) {
                client_socketfd[i] = -1;
                leave++;
                cout << "someone has leave" << endl;
                continue;
            } else {
                cout << inet_ntoa(addr.sin_addr) << "-->" << buff << endl;
                t = send(client_fd, buff, strlen(buff), 0);
                if (t > 0) cout << "send : " << buff << endl;
                else cout << "send failed" << endl;
            }   
        }
    }
    client_cnt -= leave;
}

int main() {
    int listen_socket;
    listen_socket = socket_create(PORT);
    if (listen_socket < 0) {
        perror("socket_create : listen_socket");
        exit(0);
    }
    for (int i = 0; i < MAX_CLIENT; i++) client_socketfd[i] = -1;
    struct timeval tm;
    fd_set rfds;
    max_fd = listen_socket;
    while (1) {
        FD_ZERO(&rfds);
        FD_SET(listen_socket, &rfds);
        tm.tv_sec = 10;
        tm.tv_usec = 0;
        for (int i = 0; i < client_cnt; i++) {
            if (client_socketfd[i] == -1) continue;
            FD_SET(client_socketfd[i], &rfds);
            if (max_fd < client_socketfd[i]) max_fd = client_socketfd[i];
        }

        int reval = select(max_fd + 1, &rfds, NULL, NULL, &tm);
        if (reval < 0) {
            perror("select");
            break;
        } else if (reval == 0) {
            cout << "----本轮超时结束----\n" << endl;
            continue;
        }
        
        if (FD_ISSET(listen_socket, &rfds)) {
            do_accept(listen_socket);    
        } else {
            do_recv(rfds);
        }
    }
    close(listen_socket);
    return 0;
}

利用select实现I/O复用,实现accept的非阻塞运行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值