我正在用 C 语言为 Windows 和 Unix 系统编写一个服务器。
该服务器的一个关键特性是它必须能够随时接收和发送网络数据包。 具体来说,服务器不仅必须能够向客户端发送数据以响应其消息,而且还能够以推送方式向其异步发送数据包。
我在实施上述场景中使用
select()
函数的解决方案时遇到困难。
我目前实现的解决方案根本不能说服我,我认为它可以用更好的模式/解决方案来实现。
我目前有一个专用线程(选择器),它通过侦听读取服务器套接字(以接受新连接)和连接到服务器的套接字中的事件来执行选择。
这是主要的 select() 循环:
if((sel_res_ = select(nfds_+1, &read_FDs_, NULL, &excep_FDs_, &sel_timeout)) > 0){
if(FD_ISSET(serv_socket, &read_FDs_)){
//we have to handle a newly connection.
...
if(sel_res_ > 1){
//in addition to the newly connection, there is also some other message incoming on client sockets.
...
}
}else{
//we have to handle incoming messages on client sockets
...
}
}
该解决方案非常适合接收数据并以同步形式响应客户端请求。 但是,服务器还必须能够发送异步数据,并在必要时发送推送数据包。
为此,我目前使用单独的线程直接在客户端套接字上执行
send()
。
这个解决方案不能说服我,我想将数据包的接收和发送集中在选择器线程上。
主要的困难是
select()
本质上是阻塞的,我无法控制,直到客户端不发送任何数据包或触发超时。
将超时设置得很低的解决方案并不能说服我;我认为这是一个简单的解决方案,实际上是在进行主动等待,而且不仅如此,最坏的情况是我会在发送推送数据包之前付出超时的代价。
我想到了一个更“优雅”的解决方案;我认为,会很好地工作,但仅适用于 Unix/Linux 平台。 我想使用匿名管道并将匿名管道读取描述符插入到
select()
read_FDs_ 中。
这样,当线程想要以推送方式发送数据时,它会在该管道上写入一些内容,从而中断 select()
并将控制权返回给选择器,然后选择器可以预先安排将数据发送到客户端,而不会造成大量时间损失。
不幸的是,我认为这个解决方案无法在 Windows 上实现,因为该系统上的 select()
函数仅适用于实际上是套接字的 fd。
所以问题是:是否有一些众所周知的解决方案可以用来解决这种情况(Linux 和 Windows)?
您可以创建一个自连接的 UDP 套接字,这在 Windows 和 Linux 上同样有效。
基本上,您创建一个 UDP 套接字,
bind()
将其连接到 INADDR_LOOPBACK
和端口 0,然后 connect()
将其连接到自身(地址取自 getsockname()
)。
此时,您可以向自己发送一条单字节消息(或更具体的消息)来唤醒自己。