2015-09-26 124 views
0

我试图编写一个RFC 2812兼容的C++ IRC库。 我在客户端本身的设计上遇到了一些麻烦。 从我读到的IRC通信往往是异步的。我使用boost::asio::async_readboost::asio::async_write。 通过阅读我收集的文档,您无法在完成一个async_write请求之前执行多个async_write请求。因此,你最终会得到相当嵌套的回调。这不是打败了做异步调用的目的吗?使用同步调用来防止嵌套会不会更好?如果不是,为什么?其次,如果我没有弄错,每个boost::asio::async_write后跟一个boost::asio::async_read来接收服务器对发送命令的响应。因此,我的客户端的函数需要一个回调参数,以便在客户端收到响应后(例如发送另一个消息...),该类的用户可以执行某些操作。C++ IRC客户端设计

如果我要继续使用异步来实现此操作,我应该保留一个std::deque<std::tuple<message, callback>>并且每次boost::asio::async_write完成,并且队列中有一个元组,并且出队并发送消息,然后引发回调?这是实现这个系统的最佳方式吗?

我在想,因为消息一直在发送,所以我将不得不实现某种侦听器循环来排队响应,但是如何将这些响应与触发它们的特定命令关联?或者在这种情况下,响应只是来自另一个用户的信道消息?

回答

2

IRC协议是一种全双工协议。因此,应该始终监听期望命令处理的服务器连接。有人可能会认为,应该主要使用从服务器接收的消息来更新状态,而不是将请求和响应关联起来,因为服务器可能不会对命令做出响应或可能比预期的响应晚得多。例如,可以发出WHOIS命令,但在接收到对WHOIS的响应之前接收多个PRIVMSG命令。对于聊天客户端,用户可能期望在等待对WHOIS的响应时能够接收聊天消息。因此,有一个async_write()async_read()调用链可能不是理想的处理协议。

对于给定的插座中,短耳文件并建议不要启动额外的读操作,如果有一个优秀的组成读操作,并不触发额外写入操作,如果有一个优秀的组成写操作。排队消息并从队列中获取异步调用链过程是实现此建议的好方法。考虑阅读这个answer一个很好的解决方案,使用一个队列和一个异步调用链。

此外,要知道,服务器甚至可以在活动连接发送PING命令。当客户端使用PONG命令进行响应时,可能需要在出站队列的前端附近插入PONG命令,以便尽快将其发送出去。

+0

所以你说我应该有一两个'的std :: deque的<性病::组<消息,回调>>'和一个'的std :: deque的<性病::组<回复,回调>>' ? 另外我应该产生一个线程来阻塞和不断阅读,并填写答复队列?或者,递归调用async_read? 更新状态是什么意思 - 你能举个例子吗? 至于whois或ping命令,我可以在接收到特定回复(如这些回复)时“挂钩”发送特定消息的例程。 –

+0

@FranciscoAguilera IRC协议本质上是异步的,所以我不会建议阻止调用。该协议也不是特定于何时需要发送对命令的回复,并且一些服务器实现可能不发送回应。我认为通过事件来处理服务器命令会更加健壮(例如,当接收到“JOIN”命令时调用'on_join'),而不是尝试管理如此接近协议层的回调(客户端可能会收到'JOIN'不响应客户端发送的“JOIN”命令的命令)。 –

+0

对,但你如何建议我收到这些命令?我需要调用async_read,不是吗?我应该循环调用async_read从它的回调检查数据读取整个客户端? –

1

这是不是打败了做异步调用的目的?

通常的解决方案是使用链:

Why do I need strand per connection when using boost::asio?

你可以自由地排队上使用(隐式)strand¹同一IO对象的多个异步操作。

使用链确保完成处理程序在同一个逻辑线程上被调用。


在协议

你确实可以发送下一个之前保持的命令队列,等待每个命令的响应。

你可能是这个有点聪明,如果你能发现,由于不同类型的答复的相关性,但随后你需要保持每类型的命令队列。我会考虑过早的优化。