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

目录

一、前言

二、正文

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博客

         大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见!!

评论 127
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_麦麦_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值