【问题标题】:Threaded server, sending messages between clients线程服务器,在客户端之间发送消息
【发布时间】:2011-05-30 07:22:46
【问题描述】:

我正在编写一个 C 网络服务器,它将两个客户端配对在一起并允许它们相互发送消息。

目前,每个客户端在服务器中都有自己的线程,在线程中我有一个循环,基本上是while((numBytesRead = read(fd, buffer, 1024)) > 0)。这工作正常,我能够接收消息,然后将它们回显给客户端。

我的问题是,虽然我不确定通过服务器将消息从一个客户端传输到另一个客户端的最佳方式。

我认为我最大的问题是 read() 阻塞,所以我无法向客户端发送消息,直到客户端向服务器发送一些文本以便读取停止阻塞。

有没有办法解决这个问题?我最初的想法是创建一个线程用于从客户端读取,一个线程用于写入客户端,但如果读取在一个线程中阻塞,然后我尝试写入同一个文件描述符,那么这不会导致问题吗?

感谢任何帮助! :)

【问题讨论】:

标签: c multithreading networking tcp pthreads


【解决方案1】:

read 只阻塞不发送数据的客户端线程;所以理论上你可以使用刚刚将数据发送到write的客户端线程给另一个。

另一种方法可能是使客户端套接字成为非阻塞的,如果流中没有数据,这会导致读取立即返回(有错误)。然而,这是非常占用处理器资源的,因为两个套接字都在不断地被探测数据并且(几乎总是)没有返回。

我对地方的最后建议是select 函数,它可用于检查一组文件描述符并阻塞,直到其中任何一个在流中有数据;此外,它可以通过超时(阻止多长时间的上限)。算法的粗略草图可能是:

  1. select(client-sockets, 100ms)
  2. 对于每个具有可用数据的套接字:
    1. 读取数据
    2. 将其存储在 other 套接字的输出缓冲区中
  3. 对于每个套接字:
    1. 写入其当前输出缓冲区
  4. 重复

【讨论】:

  • 谢谢马蒂!!我对第一个解决方案感兴趣,但我有点担心在一个线程中尝试 write 到文件描述符,而另一个线程正在尝试从中 read?这样做安全吗?
  • @Sam 应该没问题。如果不是,请告诉我。
  • 谢谢,我最终做了一个写线程和一个读线程.. 似乎工作得很好。 :)
【解决方案2】:

这当然不是一个简单的问题

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

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

【讨论】:

    【解决方案3】:

    使用非阻塞操作是服务器设计的不错选择。在这种情况下,最简单的方法是使用 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);
               }
         }
    }
    

    【讨论】:

      猜你喜欢
      • 2022-11-29
      • 2017-05-01
      • 2015-01-05
      • 2017-01-12
      • 1970-01-01
      • 2013-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多