C 有没有办法将文件描述符与用户定义的数据相关联?

C 有没有办法将文件描述符与用户定义的数据相关联?,c,sockets,posix,C,Sockets,Posix,我正在编写一个客户机-服务器应用程序,它使用POSIXpoll函数来提供一种形式的并发客户机处理。客户机还具有状态和其他相关数据,这些数据存储在客户机结构中 我眼前的问题是,当我从poll得到一个提示,要对与客户机关联的套接字文件描述符执行I/O操作(概念上),我必须实际将文件描述符与其关联的客户机数据结构相匹配。目前我做了一个O(n_clients)查找(我的客户机数据结构存储描述符),但我想知道是否有更好的替代方法?最便宜的方法是只制作一个固定大小的连接结构数组,每个条目有{state,*c

我正在编写一个客户机-服务器应用程序,它使用POSIX
poll
函数来提供一种形式的并发客户机处理。客户机还具有状态和其他相关数据,这些数据存储在客户机结构中


我眼前的问题是,当我从
poll
得到一个提示,要对与客户机关联的套接字文件描述符执行I/O操作(概念上),我必须实际将文件描述符与其关联的客户机数据结构相匹配。目前我做了一个
O(n_clients)
查找(我的客户机数据结构存储描述符),但我想知道是否有更好的替代方法?

最便宜的方法是只制作一个固定大小的连接结构数组,每个条目有{state,*context,…,可能是回调函数},由fd索引(=O(1))。内存很便宜,您可以负担几百或几千个文件描述符和表条目


编辑:您不需要将其设置为固定大小。如果pollstructure或fdset是固定的:使其固定;否则,请使用getdtablesize()或getrlimit()获取要分配的条目数。

否。如果有,则必须由内核跟踪,因此查找该数据将涉及系统调用。系统调用的成本比在用户空间中进行O(n)查找要高出一个数量级

你一次和多少客户打交道?除非它的数量是数百或更多,否则与任何类型的I/O相比,查找的成本将是微乎其微的

不使用O(n)查找,您也可以只使用由文件描述符索引的数组,假设一次打开的描述符不会超过一定数量。例如:

#define MY_MAX_FD 1024  // Tune this to your needs
void *per_fd_data[MY_MAX_FD];

void *get_per_fd_data(int fd)
{
    assert(fd >= 0);
    if(fd < MY_MAX_FD)
        return per_fd_data[fd];
    else
    {
        // Look up fd in a dynamic associative array (left as an exercise to the
        // reader)
    }
}
#定义我的_MAX_FD 1024//根据您的需要调整此选项
void*每个fd数据[我的最大fd];
void*get_per_fd_数据(int fd)
{
断言(fd>=0);
如果(fd
如果您使用或,那么您应该自己保存数据,例如,如其他人所述,保存在哈希表或数组中。这是最可移植的解决方案。某些替代界面确实有方法关联您自己的用户数据。例如,使用(例如),您可以提供一个用户值,该值可以在异步请求完成时传递给信号处理程序或线程。Linux界面还允许为集合中的每个文件描述符指定用户数据。

除了所有其他非常有用的答案之外,我还想提供以下信息,希望这些信息对其他人有用,这是本着知识库的精神

问题是,如果我们假设一个POSIX兼容系统,它会指定以下内容(我的重点):

除非另有规定,所有打开一个或多个文件描述符的函数应在每次分配时自动分配可用的最低编号(即,调用过程中尚未打开)文件描述符。如果单个函数分配两个文件描述符(例如,
pipe()
socketpair()
),则分配可能是独立的,因此应用程序不应期望它们具有相邻的值或依赖于哪个值更高


这允许程序简单地保留一个数组,最多保留它想要支持的多个描述符,其中一个开放描述符可以用作数组下标,以引用与客户端连接相关的数据。基本上,此类系统上的开放文件描述符可以直接用作表的索引,该表被实现为数组。毕竟,文件描述符编号不仅会从最低可用值向上增长,而且似乎还会被重用——如果关闭描述符10,而仍然打开描述符11及以上,那么下次打开描述符时,符合POSIX的系统将打开带有索引10的描述。这也使得fd索引表中的行的重用变得非常简单。

您分析过您的程序吗?您是否发现这是瓶颈?不,我没有:(但是内存比CPU便宜,你不觉得吗?我厌倦了我的笔记本电脑变热了…数组的一个问题是,为了迭代有效的fd,你还必须迭代所有无效的插槽…考虑到使用select()或poll()不好)每次事件循环迭代,您通常必须对FD进行至少两次迭代(一次是在select()之前进行FD_SET()调用,然后再进行FD_ISSET()调用)。最好使用映射(或类似的映射)。不是迭代,而是索引。“array[FD].stuff”。请记住,FD是一个值为[0,FD_max]的小整数。您只需捕获fd=-1的情况,这可能是open()、listen()或accept()的结果。您所遭受的唯一迭代是pollfd[]或fd_set array.+1,指出文件描述符用作数组是合理的indices@R这不是一个很大的数组吗?我的意思是,POSIX文件描述符是
int
s,对吗?顺便说一下@Adam,已经有一个系统调用涉及到了-
poll
,所以当你暗示一个额外的调用查找用户数据时需要ld。例如,如果
pollfd
有一个
void*
空间,这将是用户定义的数据,
poll
调用将只是“passthru”如果您的哈希函数是identify函数,并且您的bucket都是size-1…:-),则哈希表是唯一合理的解决方案