我有一个有多个线程的应用程序,一个线程正在创建 4 个 tcp 连接,然后这个线程创建另一个线程将使用 poll 或其他任何方式处理这 4 个连接的接收函数,以及原始线程(第一个)开始向这 4 个连接发送消息(循环)。就像下面的伪代码,
main()
{
pthread_create( &thread1, NULL, sending_thread, (void*) args);
}
void *sending_thread( void *ptr )
{
int i=0;
int *connections;
connections = create_connections();
pthread_create( &thread1, NULL, receiving_thread, (void*)&connections);
while(1) {
send(connections[i], "test");
i++;
if (i > 3)
i=0;
}
}
void *receiving_thread( void *ptr )
{
char *msg;
msg = receive(connections[i]);
save_received_msg_to_disk(msg);
}
我的问题是如何检查我的连接并调出断开连接的连接?例如,假设 connection1 出现故障,我是否需要使用相同的 fd 创建另一个连接,在本例中为 connection[1]?还是有其他方法来处理这种情况?
Linux环境为C/pthread
最佳答案
以下是基于您的代码和评论的一些要点。
- 虽然线程确实允许某些事情并行发生,但就始终归结为同步的更复杂的设计而言,它们通常具有非零成本。就像您的情况一样,没有简单的方法可以重新建立其中一个连接并再次在两个线程之间共享它。
- 我从未见过输入和输出流完全独立的应用程序。我可以想象像隧道代理或基于 TCP 的 VPN 这样可能有意义的东西,但在一般情况下,一些更高级别的协议(protocol)仍然强加一些请求-响应语义,这些语义将再次强加/要求发送和接收线程之间的仲裁。<
- 当您需要吞吐量 和尝试最小化延迟 时,情况就不同了。在互斥量上 sleep 等待对于前者来说通常是可以的,但对于后者来说却不是一个好主意。从单个线程循环写入多个阻塞套接字对两者都有伤害。
- 如果您有大量的输出流和稀疏的输入,只有使用类似
epoll(7)
的东西来检测饱和连接并在它们再次可用时收到通知然后让其他人挨饿才有意义。
我知道这不能直接回答你的问题,但我的rant 列表不适合评论。希望这会有所帮助。
编辑0:
这是 epoll(7)
的常规设置:
- 使您的套接字成为非阻塞的(
fcntl(2)
和O_NONBLOCK
)。 - 将
epoll_data.fd
设置为每个潜在 channel 的套接字描述符(在您的示例中为四个套接字)。如果您想保留比套接字描述符更复杂的结构,则可以使用union epoll_data
进行其他选择。 - 使用
EPOLLIN
和EPOLLET
获得边缘触发行为,即当输入缓冲区不为空时被唤醒。 - 仅当您从
write(2)
获得EWOULDBLOCK
时才设置EPOLLOUT
,否则照常输出。这里的逻辑与EPOLLET
相同,用于检测输出缓冲区空间是否可用。 - 使用
EPOLLRDHUP
检测另一端完全断开(对于突然断开,您需要处理EPIPE
错误形式write(2)
)。 epoll_wait(2)
返回要迭代的事件数。分别检查输入(events & EPOLLIN
)和输出(events & EPOLLOUT
)。- 从
data.fd
(或其他关联的套接字)读取输入,直到得到EWOULDBLOCK
。 - 在输出上写入直到您得到
EWOULDBLOCK
或您没有更多待处理的输出数据(在这种情况下删除EPOLLOUT
)。
看起来很多,但是一旦掌握了它就非常简单。
你也可以做非阻塞 connect(2)
,如果你想重新建立中断的流而不伤害其他仍在继续的流,这可能是个好主意(connect(2)
返回 -1
errno(3)
设置为 EINPROGRESS
然后等待套接字变得可写,如上所示)。
关于linux - 多线程和多 TCP 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14489378/