基于套接字的事件循环

Socket based event loop

本文关键字:事件 循环 套接字      更新时间:2023-10-16

我想设置基于套接字的服务器-客户端通信。客户端可以连接到服务器并从中接收不同的通知。这可以在客户端实现,如下

...
Message* msg = NULL;
while ( msg = receiveMessage() )
    handleMessage( msg );
...

此代码将在客户端的独立线程中运行,并且应该处理来自服务器的不同类型的通知。但是客户端也应该能够通过发送请求与套接字通信,即

Request requestMsg;
if ( sendMessage( requestMsg ) )
{
    Message* response = receiveMessage();
    if ( response->type() == REQUEST_REPLY )
    ...
}

问题:如何实现这一点?我不想中断读取线程,但我应该收到对特定请求的响应。这是基于本地域流的unix套接字。

…::ASCII内容如下::。。。

如果你讨厌艺术,或者ASCII,就到此为止。

下面的示意图不会阻塞服务器和客户端
许多MMORPG都使用它来保护连接,并使协议更难破解。

     [================ ~~ Server ~~ ================]
     [ Select / Poll ]*4             5*[ Dispatcher ]
        ||      /                             ||
        ||      ||                             ||
     *1 ||      ||2*                         *3||
        ||      ||                             ||
        /      ||                             /
     [ Thread 1 Basic IO ]       [ Thread 2 Listener]
     [=============== ~~ Client ~~ =================]
 *1 // send
 *2 // recv
 *3 // bind listen accept recv OR they will communicate using UDP
    // to a different port
 *4 // the server will process the clients normally
    // using select / poll / epoll / kqueue / `/dev/poll`
 *5 // The client will either setup a temporary server to process
    // the servers opcodes
 OR
    // It will accept UDP packets using recvfrom()
 *5 // I'd recommend using UDP so that the server can loop through the existing
    // connections and sendto() the opcodes which will be provided via a message
    // queue.

在客户端的接收器线程中,应该使用线程安全对象来推送和弹出消息。如果您有权访问C++11编译器,您可以考虑使用std::vector<std::shared_ptr<Messsage>>。下面是一个线程安全对象的简单实现,它可能适合您的需要。

class MessageQueue
{
public:
  typedef std::shared_ptr<Message> SpMessage;
  bool empty() const {
    std::lock_guard<std::mutex> lock(mutex_);
    return messages_.empty();
  }
  SpMessage pop() {
    std::lock_guard<std::mutex> lock(mutex_);
    SpMessage msg(messages_.front());
    messages_.pop_front();
    return msg;
  }
  void push(SpMessage const& msg)
    std::lock_guard<std::mutex> lock(mutex_);
    messages_.push_back(msg);
  }
private:
  MessageQueue(const MessageQueue&);  // disable 
  MessageQueue& operator=(const MessageQueue&);  // disable
  std::vector<SpMessage> messages_;
  std::mutex mutex_;
};
typedef std::shared_ptr<MessageQueue> SpMessageQueue;

此时,您有了一个可共享的线程安全队列。在主线程和套接字线程之间共享此队列。如果您希望发送也在一个单独的线程上,也可以使用两个,例如,从一个消息队列弹出,处理它,并在另一个队列上排队响应。

你可以在你的服务器上使用同样的东西。

消息类应该能够存储一个std::vector<char>,这样您就可以通过套接字发送/recv普通的旧数据,并将其放入和取出Message进行处理。

如果您需要有关启动线程的帮助,请查看本教程。http://solarianprogrammer.com/2011/12/16/cpp-11-thread-tutorial/