多线程服务器,客户端之间发送消息

3
我正在编写一个C语言网络服务器,它将两个客户端配对并允许它们相互发送消息。
目前,服务器中每个客户端都有自己的线程,在线程中我有一个循环,基本上是while((numBytesRead = read(fd, buffer, 1024)) > 0)。这很好用,我能够接收消息并将其回显到客户端。
但我的问题是,我不确定通过服务器将消息从一个客户端传输到另一个客户端的最佳方法。
我认为我最大的问题是read()会阻塞,所以在客户端发送一些文本到服务器以使read停止阻塞之前,我将无法向客户端发送消息。
有没有办法解决这个问题?我的最初想法是为读取客户端和写入客户端分别创建一个线程,但如果read在一个线程中阻塞,然后我尝试向同一文件描述符写入,这是否会引起问题?
感激任何帮助!! :)

1
如何使用单个线程在TCP上实现全双工通道? - cmcginty
3个回答

2

read 只会阻塞没有发送数据的客户端线程,因此你理论上可以使用刚发送数据的客户端线程去 write 到另一个客户端。

另一种选择是将客户端套接字设置为非阻塞模式,这会导致如果流中没有数据,read 立即返回(带有错误)。然而,这会占用大量处理器资源,因为两个套接字都在不断地查询数据,并且(几乎总是)没有返回任何数据。

我的最后一个建议是查看 select 函数,它可以用于检查一组文件描述符并阻塞,直到其中任何一个具有流中的数据;此外,它还可以传递超时值(阻塞的最长时间上限)。一个粗略的算法示意如下:

  1. select( 客户端套接字, 100ms )
  2. 对于每个可用数据的套接字:
    1. 读取数据
    2. 将其存储在另一个套接字的输出缓冲区中
  3. 对于每个套接字:
    1. 写入其当前的输出缓冲区
  4. 重复执行

谢谢Matty!我对第一个解决方案很感兴趣,但是我有点担心在一个线程尝试“写入”文件描述符的同时,另一个线程正在尝试“读取”它。这样做安全吗? - JimR
@Sam,这应该完全没问题。如果有问题,请告诉我。 - Matty K
谢谢,最终我创建了一个写线程和一个读线程..看起来运行得非常好。 :) - JimR

1

这绝对不是一个简单的问题

  1. 设计协议和协议消息格式
  2. 从套接字读取协议消息
  3. 处理类型为 "登录" 的消息
  4. 将唯一标识符绑定到此套接字
  5. 从套接字(发送器)中读取其他类型为 "消息" 且包含 "接收者 id" 的消息
  6. 找到具有该 id(接收者)的套接字
  7. 将消息中的数据发送给接收者

建议使用 IO 多路复用而不是多线程。


0

使用非阻塞操作是服务器设计的好选择。在这种情况下,最简单的方法是使用select或poll。更先进的是kqueue(FreeBSD)和epoll(Linux),也可以使用异步I/O(AIO)。

因此,如果选择select(),可以使用以下方法(只是示例,类似于伪代码):

fd_set read_set, write_set;
struct timeval timeout;

while(!quit)
{
     // adds your sockets to fd_set structure, return max socket + 1, this is important! 
     max = fillFDSet(&read_set); 
     setReadTimeout(&timeout); // sets timeout for select

     if (0 < select(max, &read_set, NULL, NULL, &timeout)) // wait for read
     {
           // there is at least one descriptor ready
           if (FD_ISSET(your_socket))
           {
               socket_size = read(socket, socket_buffer, 1024);
           }
     }
     max = fillFDSet(&write_set); 
     setWriteTimeout(&timeout); // sets timeout for select

     if (0 < select(max, NULL, &write_set, NULL, &timeout)) // wait for write
     {
           // there is at least one descriptor ready
           if (FD_ISSET(your_socket))
           {
               write(socket, socket_buffer, socket_size);
           }
     }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接