张春 发表于 昨天 10:40

【C++从零实现Json-Rpc框架】第四弹——利用Muduo库实现英译汉

https://i-blog.csdnimg.cn/direct/b026389937144dc7b012a0bfef05c435.png

https://i-blog.csdnimg.cn/direct/3e0a674d594a49808323dafc27dc9670.jpeg

目录

一、前言
二、正文
1.Muduo库常见接口介绍
1.1 TcpServer类基础介绍
● TcpConnectionPtr
● ConnectionCallback
● MessageCallback
● InetAddress 
● TcpServer
1.2 EventLoop类
1.3 TcpConnection类
1.4 TcpClient类基础介绍
● TcpClient
●void connect()
●void disconnect()
 ●void setConnectionCallback(ConnectionCallback cb)
 ●void setMessageCallback(MessageCallback cb)
1.5 CountDownLatch 类
 1.6 Buffer类基础介绍
 2. Muduo库英译汉服务
2.1 英译汉TCP服务器
2.2 英译汉客户端
2.3 makefile文件
 三、结语

一、前言

           在本文将会为各人介绍Muduo库常用的一些接口,并借助这些接口来实现一个简单版的英译汉服务器和客户端,盼望可以或许资助各人加深对Muduo库的利用!!!!
二、正文

1.Muduo库常见接口介绍

1.1 TcpServer类基础介绍

● TcpConnectionPtr

typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;   TcpConnectionPtr属于TcpConnection类,但是我们在编写服务器的时候也需要接受客户端的毗连,当毗连到来的时候,由毗连事件的回调函数来处理毗连
● ConnectionCallback

typedef std::function<void (const TcpConnectionPtr&)> ConnectionCallback;   ConnectionCallback是服务器处理毗连事情的回调函数,当有来自客户端的毗连到来时,就会自动调用该函数来处理毗连
● MessageCallback

typedef std::function<void (const TcpConnectionPtr&, Buffer*,
Timestamp)> MessageCallback;    MessageCallback是服务器处理毗连消息的回调函数,当客户端传来消息时,就会自动调用该函数来处理消息
● InetAddress 

class InetAddress : public muduo::copyable
{
public:
   InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);
};   InetAddress 字段与服务器的设置有关,该字段将ip,port与ipv6合并成为一个字段,当我们设置服务器的时候就需要传递该字段,来指明服务器的ip地址,端口号和是否采取ipv6。
● TcpServer

class TcpServer : noncopyable
{
public:
   enum Option
   {
         kNoReusePort,
         kReusePort,
   };
   
    TcpServer(EventLoop* loop,const InetAddress& listenAddr,const string&         
            nameArg,Option option = kNoReusePort);

    void setThreadNum(int numThreads);
    void start();
   
    /// 当⼀个新连接建⽴成功的时候被调⽤
    void setConnectionCallback(const ConnectionCallback& cb { connectionCallback_ = cb; }
   
    ///消息的业务处理回调函数--这是收到新连接消息的时候被调⽤的函数
    void setMessageCallback (const MessageCallback& cb)
   { messageCallback_ = cb; }
};   TcpServer类则是我们等会字典服务器要利用的主体,在其构造函数中我们要传递InetAddress字段,表明ip和端口号;传递EventLoop指针来进行事件监控,Option选项则是指明服务器端口是否服用;start函数则是启动服务器;在启动服务器之前,我们还需要设置毗连创建回调函数和消息处理的回调函数
1.2 EventLoop类

class EventLoop : noncopyable
{
public:
/// Loops forever.
/// Must be called in the same thread as creation of the object.
void loop();
/// Quits loop.
/// This is not 100% thread safe, if you call through a raw pointer,
/// better to call through shared_ptr<EventLoop> for 100% safety.
void quit();
TimerId runAt(Timestamp time, TimerCallback cb);
/// Runs callback after @c delay seconds.
/// Safe to call from other threads.
TimerId runAfter(double delay, TimerCallback cb);
/// Runs callback every @c interval seconds.
/// Safe to call from other threads.
TimerId runEvery(double interval, TimerCallback cb);
/// Cancels the timer.
/// Safe to call from other threads.
void cancel(TimerId timerId);
private:
std::atomic<bool> quit_;
std::unique_ptr<Poller> poller_;
mutable MutexLock mutex_;
std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
};   EventLoop类是资助我们服务器进行事件监控的,一旦调用loop( )函数就会不绝死循环进入事件监控的状态 
1.3 TcpConnection类

class TcpConnection : noncopyable,
public std::enable_shared_from_this<TcpConnection>
{
public:
/// Constructs a TcpConnection with a connected sockfd
///
/// User should not create this object.
TcpConnection(EventLoop* loop,
               const string& name,
               int sockfd,
               const InetAddress& localAddr,
               const InetAddress& peerAddr);
bool connected() const { return state_ == kConnected; }
bool disconnected() const { return state_ == kDisconnected; }
void send(string&& message); // C++11
void send(const void* message, int len);
void send(const StringPiece& message);
// void send(Buffer&& message); // C++11
void send(Buffer* message);// this one will swap data
void shutdown(); // NOT thread safe, no simultaneous calling
void setContext(const boost::any& context)
{ context_ = context; }
const boost::any& getContext() const
{ return context_; }
boost::any* getMutableContext()
{ return &context_; }
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback& cb)
{ messageCallback_ = cb; }
private:
enum StateE { kDisconnected, kConnecting, kConnected, kDisconnecting };
EventLoop* loop_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
boost::any context_;
};   在TcpConnection与毗连相干的类常用函数的有:
①判定当前的毗连情况——connected() / disconnected()
②向毗连的远端服务端发送消息——send()
1.4 TcpClient类基础介绍

● TcpClient

TcpClient(EventLoop* loop,const InetAddress& serverAddr,
const string& nameArg);   对于TcpClient的构造需要传递loop指针进行事件监控,InetAddress来指明服务端的IP和port,nameArg则是指明TcpClient的定名
●void connect()

   调用该函数,TcpClient则会向已经设置好的远端服务器进行毗连 
●void disconnect()

   调用该函数,TcpClient则会取消与远端服务器的毗连
 ●void setConnectionCallback(ConnectionCallback cb)

/// 连接服务器成功时的回调函数

void setConnectionCallback(ConnectionCallback cb)
{ connectionCallback_ = std::move(cb); }  ●void setMessageCallback(MessageCallback cb)

/// 收到服务器发送的消息时的回调函数

void setMessageCallback(MessageCallback cb)
{ messageCallback_ = std::move(cb); } 1.5 CountDownLatch 类

   因为 muduo 库不管是服务端还是客⼾端都是异步操作, 对于客⼾端来说如果我们在毗连还没有完全建⽴乐成的时候发送数据,这是不被允许的。 因此我们可以使⽤内置的CountDownLatch 类进⾏同步控制。具体的思路就是给计数器count一个初值,比如说1,当毗连创建乐成的时候,我们将该值镌汰为0,才进行loop的事件监控,否则就不绝处于阻塞等待毗连的状态,避免client还没有创建毗连乐成,就进入事件的监控,这是不符合逻辑的。
class CountDownLatch : noncopyable
{
public:
   explicit CountDownLatch(int count);
   void wait(){
         MutexLockGuard lock(mutex_);
         while (count_ > 0)
         {
             condition_.wait();
         }
   }

   void countDown(){
         MutexLockGuard lock(mutex_);--count_;
         if (count_ == 0)
         {
             condition_.notifyAll();
         }
   }
   int getCount() const;
private:
   mutable MutexLock mutex_;
   Condition condition_ GUARDED_BY(mutex_);
   int count_ GUARDED_BY(mutex_);
};  1.6 Buffer类基础介绍

class Buffer : public muduo::copyable
{
public:
static const size_t kCheapPrepend = 8;
static const size_t kInitialSize = 1024;
explicit Buffer(size_t initialSize = kInitialSize)
   : buffer_(kCheapPrepend + initialSize),
   readerIndex_(kCheapPrepend),
   writerIndex_(kCheapPrepend);
void swap(Buffer& rhs)
size_t readableBytes() const
size_t writableBytes() const
const char* peek() const
const char* findEOL() const
const char* findEOL(const char* start) const
void retrieve(size_t len)
void retrieveInt64()
void retrieveInt32()
void retrieveInt16()
void retrieveInt8()
string retrieveAllAsString()
string retrieveAsString(size_t len)
void append(const StringPiece& str)
void append(const char* /*restrict*/ data, size_t len)
void append(const void* /*restrict*/ data, size_t len)
char* beginWrite()
const char* beginWrite() const
void hasWritten(size_t len)
void appendInt64(int64_t x)
void appendInt32(int32_t x)
void appendInt16(int16_t x)
void appendInt8(int8_t x)
int64_t readInt64()
int32_t readInt32()
int16_t readInt16()
int8_t readInt8()
int64_t peekInt64() const
int32_t peekInt32() const
int16_t peekInt16() const
int8_t peekInt8() const
void prependInt64(int64_t x)
void prependInt32(int32_t x)
void prependInt16(int16_t x)
void prependInt8(int8_t x)
void prepend(const void* /*restrict*/ data, size_t len)
private:
std::vector<char> buffer_;
size_t readerIndex_;
size_t writerIndex_;
static const char kCRLF[];
};     在Buffer类中,我们这次用到的接口是retrieveAllAsString(),由于我们字典翻译的哀求隔断时间比较长,因此默认缓冲区里的数据就是一次完整的翻译哀求,所以我们就利用retrieveAllAsString()接口将缓冲区的数据全部读作为一次完整的哀求
 2. Muduo库英译汉服务

2.1 英译汉TCP服务器

/*
    实现一个翻译服务器,客户端发送过来一个英语单词,返回一个汉语词语
*/


#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/Buffer.h>
#include <iostream>
#include <string>
#include <unordered_map>


class DicServer{
public:
    DicServer(int port)
      :_server(&_baseloop,
      muduo::net::InetAddress("0.0.0.0",port),
      "DicServer",muduo::net::TcpServer::Option::kReusePort)
    {
      //设置连接事件(连接建立/管理)的回调
      _server.setConnectionCallback(std::bind(&DicServer::onConnection,this,std::placeholders::_1));
      //设置连接消息的回调
      _server.setMessageCallback(std::bind(&DicServer::onMessage,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));

    }

    void start()
    {
      _server.start();    //先开始监听
      _baseloop.loop();   //再开始死循环事件监控
    }
private:
    void onConnection(const muduo::net::TcpConnectionPtr &conn)
    {
      if(conn->connected())
            std::cout<<"连接建立!\n";
      else
            std::cout<<"连接断开!\n";
    }

    void onMessage(const muduo::net::TcpConnectionPtr &conn,muduo::net::Buffer *buf,muduo::Timestamp)
    {
      static std::unordered_map<std::string,std::string> dict_map={
            {"hello","你好"},
            {"world","世界"},
            {"worker","打工人"}
      };
      std::string msg=buf->retrieveAllAsString();
      std::string res;
      auto it=dict_map.find(msg);
      if(it != dict_map.end())
            res=it->second;
      else   
            res="未知单词!";
      
      conn->send(res);
    }

public:
    muduo::net::EventLoop _baseloop;
    muduo::net::TcpServer _server;

};

int main()
{
    DicServer server(9090);
    server.start();
    return 0;
} 2.2 英译汉客户端

#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/Buffer.h>
#include <iostream>
#include <string>


class DictClient{
public:
    DictClient(const std::string &sip,int sport)
      :_baseloop(_loopthrad.startLoop())
      ,_downlatch(1) //初始化计数器为1,只有为0时才会唤醒
      ,_client(_baseloop,muduo::net::InetAddress(sip,sport),"DicClient")
    {
      //设置连接事件(连接建立/管理)的回调
      _client.setConnectionCallback(std::bind(&DictClient::onConnection,this,std::placeholders::_1));
      //设置连接消息的回调
      _client.setMessageCallback(std::bind(&DictClient::onMessage,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
      
      //连接服务器
      _client.connect();
      _downlatch.wait();
    }

    bool send(const std::string &msg)
    {
      if(_conn->connected() ==false)
      {
            std::cout<<"连接已经断开,发送数据失败!\n";
            return false;
      }
      _conn->send(msg);
    }
private:
    void onConnection(const muduo::net::TcpConnectionPtr &conn)
    {
      if(conn->connected())
      {
            std::cout<<"连接建立!\n";
            _downlatch.countDown();//计数--,为0时唤醒阻塞
            _conn=conn;
      }
      else
      {
            std::cout<<"连接断开!\n";
            _conn.reset();
      }
    }

    void onMessage(const muduo::net::TcpConnectionPtr &conn,muduo::net::Buffer *buf,muduo::Timestamp)
    {
      std::string res = buf->retrieveAllAsString();
      std::cout<< res <<std::endl;
    }


private:
    muduo::net::TcpConnectionPtr _conn;
    muduo::CountDownLatch _downlatch;
    muduo::net::EventLoopThread _loopthrad;
    muduo::net::EventLoop *_baseloop;
    muduo::net::TcpClient _client;

};


int main()
{
    DictClient client("127.0.0.1",9090);
    while(1)
    {
      std::string msg;
      std::cin>>msg;
      client.send(msg);
    }
    return 0;
} 2.3 makefile文件

CFLAG= -std=c++11 -I ../../../../build/release-install-cpp11/include/
LFLAG= -L../../../../build/release-install-cpp11/lib-lmuduo_net -lmuduo_base -pthread

all: server client
server: server.cpp
        g++$(CFLAG) $^ -o $@ $(LFLAG)
client: client.cpp
        g++$(CFLAG) $^ -o $@ $(LFLAG)    CFLAG:处理文件中包罗的头文件
 LFLAG:指明链接的库
 三、结语

           到此为止,本文关于从零实现Json-RPC框架第四弹的内容到此竣事了,如有不敷之处,欢迎小伙伴们指出呀!
         关注我 _麦麦_分享更多干货:_麦麦_-CSDN博客
         各人的「关注❤️ + 点赞
页: [1]
查看完整版本: 【C++从零实现Json-Rpc框架】第四弹——利用Muduo库实现英译汉