• muduo的线程模型为one loop per thread+thread pool模型,在前面一篇文章的末尾曾简单的提起过:javascript:void(0)
  • 本节以一个Sudoku Solver(数独求解)例子为例,回顾了并发网络服务程序的多种设计方案,并介绍了使用muduo网络库编写多线程服务器的两种最常用手法
  • 在后面“muduo编程示例”相关文章会展现muduo在编写单线程并发网络服务程序方面的能力与便捷性。本文先看一看它在多线程方面的表现。本节代码参见:https://github.com/dongyusheng/csdn-code/tree/master/muduo/examples/sudoku
一、数独求解服务器
  • 假设有这么一个网络编程任务:写一个求解数独的程序(Sudoku Solver),并把它做成一个网络服务
  • Sudoku Solver在很多文章中出现过,例如:
    • “分布式系统部署、监控与进程管理的几重境界”
    • “muduo Buffer类的设 计与使用”
    • “‘多线程服务器的适用场合’例释与答疑”:javascript:void(0)
    • 它也可以看成是echo服务的一个变种(附录A“谈一谈网络编程学习经验”把echo列为三大TCP网络编程案例之一)
  • 写这么一个程序在网络编程方面的难度不高,跟写echo服务差不多(从网络连接读入一个Sudoku题目,算出答案,再发回给客户),挑战在于怎样做才能发挥现在多核硬件的能力
  • 在谈这个问题之前,让我们先写一个基本的单线程版

协议设计

  • 一个简单的以\r\n分隔的文本行协议,使用TCP长连接,客户端在不需要服务时主动断开连接
    • 其中[id:]表示可选的id,用于区分先后的请求,以支持Parallel Pipelining,响应中会回显请求中的id
    • <81digits>是Sudoku的棋盘,9×9个数字,从左上角到右下角按行扫描,未知数字以0表示。如果Sudoku有解,那么响应是填满数字的棋盘;如果无解,则返回NoSolution

muduo网络库:21---muduo简介之(详解muduo多线程模型)_.net

  • 实例1:

muduo网络库:21---muduo简介之(详解muduo多线程模型)_服务器_02

  • 实例2:

muduo网络库:21---muduo简介之(详解muduo多线程模型)_网络编程_03

  • 实例3:

muduo网络库:21---muduo简介之(详解muduo多线程模型)_服务器_04

  • 基于这个文本协议,我们可以用telnet模拟客户端来测试Sudoku Solver,不需要单独编写Sudoku Client。Sudoku Solver的默认端口号是9981(因为它有9×9=81个格子)

Sudoku Solver程序实现

  • Sudoku的求解算法见《谈谈数独(Sudoku)》一文(javascript:void(0)),这不是本文的重点
  • 假设我们已经有一个函数能求解Sudoku,它的原型如下:
    • 参数为上面提到的的“<81digits>”
    • 返回值是“<81digits>”或“NoSolution”
    • 这个函数是个pure function,同时也是线程安全的
string sloveSudoku(const string& puzzle);
//onMessage()的主要功能是处理协议格式,并调用solveSudoku()求解问题。这个函数应该能正确处理TCP分包
void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)
{
    LOG_DEBUG << conn->name();
    size_t len = buf->readableBytes();
    while (len >= kCells + 2)
    {
      const char* crlf = buf->findCRLF();
      if (crlf)
      {
        string request(buf->peek(), crlf);
        buf->retrieveUntil(crlf + 2);
        len = buf->readableBytes();
        if (!processRequest(conn, request))
        {
          conn->send("Bad Request!\r\n");
          conn->shutdown();
          break;
        }
      }
      else if (len > 100) // id + ":" + kCells + "\r\n"
      {
        conn->send("Id too long!\r\n");
        conn->shutdown();
        break;
      }
      else
      {
        break;
      }
    }
}