问题描述
|
我正在编写一个C网络服务器,它将两个客户端配对在一起,并允许它们彼此发送消息。
目前,每个客户端在服务器中都有自己的线程,而在线程中,我有一个基本上为
while((numBytesRead = read(fd,buffer,1024)) > 0)
的循环。这工作正常,我能够接收消息,然后将它们回显给客户端。
我的问题是尽管我不确定通过服务器将消息从一个客户端传输到另一个客户端的最佳方法。
我认为我最大的问题是ѭ1阻止了,因此,直到客户端向服务器发送一些文本以使读取停止阻止之前,我才能向客户端发送消息。
有没有办法解决?我最初的想法是使一个线程可以从客户端读取,而一个线程可以写入客户端,但是如果读取阻塞在一个线程中,然后我尝试写入同一文件描述符,那么这不会引起问题吗?
感谢任何帮助! :)
解决方法
“ 2”仅阻塞未发送数据的客户端线程;因此,从理论上讲,您可以使用刚刚将数据发送到
write
的客户端线程。
另一种选择是使客户端套接字为非阻塞状态,如果流中没有数据,则会导致读取立即返回(有错误)。但是,这非常耗费处理器资源,因为两个套接字都在不断地探测数据,并且(几乎总是)不返回任何数据。
我对外观的最终建议是使用select
函数,该函数可用于检查一组文件描述符并进行阻塞,直到其中任何一个流中都有数据为止。此外,它还可以传递超时(阻止多长时间的上限)。一个算法的大概草图可能是:
选择(客户端套接字,100ms)
对于每个具有可用数据的套接字:
读取数据
将其存储在另一个套接字的输出缓冲区中
对于每个插槽:
写入其当前输出缓冲区
重复
, 这当然不是一个简单的问题
设计协议和协议消息格式
从套接字读取协议消息
处理“登录”类型的消息
绑定一个uniq ID到这个套接字
从套接字(发送者)读取其他类型为“ message”的消息,其中包含“接收者ID”
查找具有该id(接收器)的套接字
将消息中的数据发送给接收者
建议使用IO复用,而不是多线程。
, 使用非阻塞操作是服务器设计的不错选择。在这种情况下,最简单的方法是使用选择或轮询。更高级的是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,&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,&write_set,&timeout)) // wait for write
{
// there is at least one descriptor ready
if (FD_ISSET(your_socket))
{
write(socket,socket_size);
}
}
}