簡體   English   中英

如何遍歷 fd_set

[英]How to iterate through a fd_set

我想知道是否有一種簡單的方法來遍歷 fd_set? 我想這樣做的原因是不必循環遍歷所有連接的 sockets,因為 select() 更改這些 fd_sets 以僅包括我感興趣的那些。 我也知道使用不打算直接訪問的類型的實現通常不是一個好主意,因為它可能因不同的系統而異。 但是,我需要一些方法來做到這一點,而且我的想法已經用完了。 所以,我的問題是:

如何遍歷 fd_set? 如果這是一個非常糟糕的做法,除了循環遍歷所有連接的 sockets 之外,還有其他方法可以解決我的“問題”嗎?

謝謝

你必須在調用select()之前填寫一個fd_set結構,你不能直接傳入你的原始std ::套接字。 select()然后相應地修改fd_set,刪除任何未“設置”的套接字,並返回剩余的套接字數。 你必須遍歷生成的fd_set,而不是你的std :: set。 無需調用FD_ISSET(),因為生成的fd_set只包含准備就緒的“set”套接字,例如:

fd_set read_fds;
FD_ZERO(&read_fds);

int max_fd = 0;

read_fds.fd_count = connected_sockets.size();
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    read_fds.fd_array[i] = connected_sockets[i];
    if (read_fds.fd_array[i] > max_fd)
      max_fd = read_fds.fd_array[i];
}

if (select(max_fd+1, &read_fds, NULL, NULL, NULL) > 0)
{ 
    for( int i = 0; i < read_fds.fd_count; ++i ) 
        do_socket_operation( read_fds.fd_array[i] ); 
} 

FD_ISSET()更常出現的地方是使用select()進行錯誤檢查,例如:

fd_set read_fds;
FD_ZERO(&read_fds);

fd_set error_fds;
FD_ZERO(&error_fds);

int max_fd = 0;

read_fds.fd_count = connected_sockets.size();
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    read_fds.fd_array[i] = connected_sockets[i];
    if (read_fds.fd_array[i] > max_fd)
      max_fd = read_fds.fd_array[i];
}

error_fds.fd_count = read_fds.fd_count;
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    error_fds.fd_array[i] = read_fds.fd_array[i];
}

if (select(max_fd+1, &read_fds, NULL, &error_fds, NULL) > 0)
{ 
    for( int i = 0; i < read_fds.fd_count; ++i ) 
    {
        if( !FD_ISSET(read_fds.fd_array[i], &error_fds) )
            do_socket_operation( read_fds.fd_array[i] ); 
    }

    for( int i = 0; i < error_fds.fd_count; ++i ) 
    {
        do_socket_error( error_fds.fd_array[i] ); 
    }
} 

選擇設置與集合中的文件描述符對應的位,因此,如果您只對少數幾個感興趣(並且可以忽略其他),則需要 - 不迭代所有fds,只測試您感興趣的那些文件描述符。

if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
   perror("select");
   exit(4);
}

if(FD_ISSET(fd0, &read_fds))
{
   //do things
}

if(FD_ISSET(fd1, &read_fds))
{
   //do more things
}

編輯
這是fd_set結構:

typedef struct fd_set {
        u_int   fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

其中,fd_count是設置的套接字數(因此,您可以使用此方法添加優化),fd_array是一個位向量(大小為FD_SETSIZE * sizeof(int) ,取決於機器 )。 在我的機器中,它是64 * 64 = 4096。

那么,你的問題基本上是:在位向量(大小約為4096位)中找到1位位置的最有效方法是什么?

我想澄清一件事:
“遍歷所有連接的套接字”並不意味着您實際上正在讀取/處理連接的內容。 FD_ISSET()僅檢查位於連接的已分配file_descriptor編號的fd_set中的位是否已設置。 如果效率是你的目標,那么這不是最有效的嗎? 使用啟發式?

請告訴我們這種方法有什么問題,以及您嘗試使用備用方法實現的目標。

這很直截了當:

for( int fd = 0; fd < max_fd; fd++ )
    if ( FD_ISSET(fd, &my_fd_set) )
        do_socket_operation( fd );

此循環是select()接口的限制。 fd_set的底層實現通常是一個位設置,這顯然意味着尋找一個套接字需要掃描位。

正是出於這個原因,已經創建了幾個備用接口 - 遺憾的是,它們都是特定於操作系統的。 例如,Linux提供epoll ,它返回僅列出活動文件描述符的列表。 FreeBSD和Mac OS X都提供了kqueue ,它們可以實現相同的結果。

請參閱Beej網絡指南的第7.2節 - '7.2。 select() - 使用FD_ISSET進行同步I / O多路復用。

簡而言之,您必須遍歷fd_set以確定文件描述符是否已准備好進行讀/寫...

我認為你不能有效地使用select()調用。 C10K問題 ”中的信息仍然有效。

您需要一些特定於平台的解決方案:

或者你可以使用一個事件庫隱藏平台細節為你libev

我不認為你想要做的是一個好主意。

首先它依賴於系統,但我相信你已經知道了。

其次,在內部級別,這些集合存儲為整數數組,fds存儲為設置位。 現在根據選擇FD_SETSIZE的手冊頁是1024.即使你想迭代並獲得你感興趣的fd,你也必須循環遍歷那個數字以及一點點操作。 因此,除非你等待選擇的FD_SETSIZE fd更多,我認為不可能這樣做,這不是一個好主意。

等一下!!。 無論如何,這不是一個好主意。

ffs() 可以在 POSIX 或 4.3BSD 上用於位迭代,盡管它需要 int(long 和 long long 版本是 glibc 擴展)。 當然,您必須檢查 ffs() 的優化是否與 strlen 和 strchr 一樣好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM