栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux系统编程—select

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux系统编程—select

1. 函数原型
#include 
#include 
#include 
#include 

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • 保持阻塞,直到一个或多个文件描述符集合就绪、或超时。

  • 超时时返回 0,出错时返回 -1,否则返回就绪的文件描述符数,即 readfds、writefds 和 exceptfds 中被置位的比特数。

  • nfds 必须设为 readfds、writefds 和 exceptfds 中最大的文件描述符号加 1。

  • readfds、writefds 和 exceptfds 分别表示用来检测输入是否就绪、输出是否就绪和异常情况是否发生(如,收到带外数据)的文件描述符集合。使用以下宏来操作文件描述符集合:

    void FD_CLR(int fd, fd_set *set);		// 将 fd 从 set 中移除。 
    int  FD_ISSET(int fd, fd_set *set);		// fd 在 set 中时返回 1,否则返回 0.
    void FD_SET(int fd, fd_set *set);		// 将 fd 添加到 set 中。
    void FD_ZERO(fd_set *set);				// 清空 set.
    
  • timeout 指定超时时间,NULL 表示一直阻塞直到有事件就绪。

    struct timeval {
        long    tv_sec;         
        long    tv_usec;        
    };
    
  • 文件描述符集合有一个最大容量限制,由 FD_SETSIZE 指定,Linux 将其设为 1024。

  • 在调用 select() 之前,需要设置 readfds、writefds 和 exceptfds,以指定要监听哪些文件描述符和事件;select() 返回后,readfds、writefds 和 exceptfds 中被置位的文件描述符说明哪些文件描述符的哪些事件就绪了,即 select() 会修改 readfds、writefds 和 exceptfds。

  • 如果对某一类型的事件不感兴趣,可将相应的文件描述符集合设为 NULL。

  • 在 Linux 上,如果 timeout 不为空,则 select() 也会修改 timeout 参数,以表示剩余的超时时间;为了可移植性考虑,调用 select() 之前也应设置 timeout 参数。

2. 例子
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static uint16_t Port = 6666;
static const char* IP = "127.0.0.1";
static std::string Msg("Hello, World!");
static constexpr size_t MSG_LEN = 13;
static std::mutex ioLock;

class IOManager {
public:
    IOManager(int listenFd=-1);
    
    void run();
    void add_read(int fd);
    void add_write(int fd);
    void del_read(int fd);
    void del_write(int fd);

private:
    void initFdSets();

private:
    int m_nFds, m_listenFd;
    fd_set m_rSet, m_wSet;
    std::unordered_set m_rFds, m_wFds;
    std::unordered_set m_add_rSet, m_add_wSet, m_del_rSet, m_del_wSet;
};

IOManager::IOManager(int listenFd): m_nFds(0), m_listenFd(listenFd) {}

void IOManager::add_read(int fd) {
    m_add_rSet.insert(fd);
}

void IOManager::add_write(int fd) {
    m_add_wSet.insert(fd);
}

void IOManager::del_read(int fd) {
    m_del_rSet.insert(fd);
}

void IOManager::del_write(int fd) {
    m_del_wSet.insert(fd);
}

void IOManager::initFdSets() {
    FD_ZERO(&m_rSet);
    FD_ZERO(&m_wSet);

    m_nFds = m_listenFd;

    for (int fd : m_del_rSet) {
        m_rFds.erase(fd);
    }
    for (int fd : m_del_wSet) {
        m_wFds.erase(fd);
    }
    for (int fd : m_add_rSet) {
        m_rFds.insert(fd);
    }
    for (int fd : m_add_wSet) {
        m_wFds.insert(fd);
    }

    m_del_rSet.clear();
    m_del_wSet.clear();
    m_add_rSet.clear();
    m_add_wSet.clear();

    for (int fd : m_rFds) {
        FD_SET(fd, &m_rSet);
        if (fd > m_nFds) {
            m_nFds = fd;
        }
    }
    for (int fd : m_wFds) {
        FD_SET(fd, &m_wSet);
        if (fd > m_nFds) {
            m_nFds = fd;
        }
    }

    if (m_listenFd >= 0) {
        FD_SET(m_listenFd, &m_rSet);
    }

    m_nFds++;
}

void IOManager::run() {
    int nReady;
    char buf[MSG_LEN];
    ssize_t n;

    while (true) {
        initFdSets();

        nReady = select(m_nFds, &m_rSet, &m_wSet, NULL, NULL);
        if (nReady == -1) {
            perror("select()");
            exit(EXIT_FAILURE);
        }

        if (FD_ISSET(m_listenFd, &m_rSet)) {
            int fd = accept(m_listenFd, NULL, NULL);
            if (fd == -1) {
                perror("accept()");
                exit(EXIT_FAILURE);
            }

            add_read(fd);
        } else {
            for (int fd : m_rFds) {
                if (FD_ISSET(fd, &m_rSet)) {
                    del_read(fd);
                    n = read(fd, buf, MSG_LEN);
                    if (n < 0) {
                        perror("read()");
                        continue;
                    }

                    add_write(fd);
                }
            }

            for (int fd : m_wFds) {
                if (FD_ISSET(fd, &m_wSet)) {
                    del_write(fd);
                    n = write(fd, Msg.data(), MSG_LEN);
                    if (n < 0) {
                        perror("write()");
                    }
                }
            }
        }
    }
}

void server() {
    int listenSock = socket(AF_INET, SOCK_STREAM, 0);
    int value = 1;
    setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_port = htons(Port);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, IP, &addr.sin_addr);

    bind(listenSock, (const struct sockaddr*)&addr, sizeof(addr));
    listen(listenSock, 4);

    IOManager manager(listenSock);
    manager.run();
}

void client() {
    using namespace std::literals;

    std::this_thread::sleep_for(1s);

    int sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_port = htons(Port);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, IP, &addr.sin_addr);

    connect(sock, (const struct sockaddr*)&addr, sizeof(addr));

    char buf[MSG_LEN+1] = {0};
    ssize_t n = write(sock, Msg.c_str(), MSG_LEN);
    read(sock, buf, n);

    std::lock_guard lock(ioLock);
    std::cout << "client: " << buf << 'n';

    close(sock);
}

int main() {
    constexpr int N = 4;
    for (int i = 0; i < N; i++)
    {
        std::thread t(client);
        t.detach();
    }

    std::thread st(server);
    st.join();

    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/460971.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号