文章目錄
- 網絡版漢譯英服務(muduo)
- muduo庫
- muduo 庫是什么
- muduo 庫常見接口介紹
- muduo::net::EventLoop
- muduo::net::TcpConnection
- muduo::net::TcpServer
- muduo::net::TcpClient
- muduo::net::Buffer
- 漢譯英服務
- 服務端
- 客戶端
網絡版漢譯英服務(muduo)
項目源碼:漢譯英服務
muduo庫
muduo 庫是什么
Muduo 由陳碩大佬開發,是一個基于非阻塞 IO 和事件驅動的 C++高并發 TCP 網絡編程庫。 它是一款基于主從 Reactor 模型的網絡庫,其使用的線程模型是 one loop per thread, 所謂 one loop per thread 指的是:
- 一個線程只能有一個事件循環(EventLoop), 用于響應計時器和 IO 事件
- 一個文件描述符只能由一個線程進行讀寫,換句話說就是一個 TCP 連接必須歸屬 于某個 EventLoop 管理
muduo 庫常見接口介紹
muduo::net::EventLoop
class EventLoop : noncopyable
{ public: //開始永久事件循環void loop(); //停止循環void quit(); TimerId runAt(Timestamp time, TimerCallback cb); TimerId runAfter(double delay, TimerCallback cb); TimerId runEvery(double interval, TimerCallback cb); void cancel(TimerId timerId); private: std::atomic<bool> quit_; std::unique_ptr<Poller> poller_; mutable MutexLock mutex_; std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
};
muduo::net::TcpConnection
class TcpConnection : noncopyable,
public std::enable_shared_from_this<TcpConnection>
{ public: 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_;
};
muduo::net::TcpServer
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef std::function<void (const TcpConnectionPtr&)> ConnectionCallback;
typedef std::function<void (const TcpConnectionPtr&, Buffer*, Timestamp)> MessageCallback; class InetAddress : public muduo::copyable
{
public: InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);
}; 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; }
};
muduo::net::TcpClient
class TcpClient : noncopyable
{ public: // TcpClient(EventLoop* loop); // TcpClient(EventLoop* loop, const string& host, uint16_t port); TcpClient(EventLoop* loop, const InetAddress& serverAddr, const string& nameArg); ~TcpClient(); // force out-line dtor, for std::unique_ptr members. //連接服務器void connect(); void disconnect();//關閉連接void stop(); //獲取客戶端對應的通信連接 Connection 對象的接口,發起 connect 后,有可能還沒有連接建立成功 TcpConnectionPtr connection() const { MutexLockGuard lock(mutex_); return connection_; } /// 連接服務器成功時的回調函數 void setConnectionCallback(ConnectionCallback cb) { connectionCallback_ = std::move(cb); } /// 收到服務器發送的消息時的回調函數 void setMessageCallback(MessageCallback cb) { messageCallback_ = std::move(cb); } private: EventLoop* loop_; ConnectionCallback connectionCallback_; MessageCallback messageCallback_; WriteCompleteCallback writeCompleteCallback_; TcpConnectionPtr connection_ GUARDED_BY(mutex_);
}; /*
需要注意的是,因為 muduo 庫不管是服務端還是客戶端都是異步操作,
對于客戶端來說如果我們在連接還沒有完全建立成功的時候發送數據,這是不被允
許的。
因此我們可以使用內置的 CountDownLatch 類進行同步控制
*/
class CountDownLatch : noncopyable
{ public: explicit CountDownLatch(int count);//配合client的connect()函數使用,在連接還沒有建立好時進行wait//直到client的連接回調函數被執行時,使用countDown()函數解除等待狀態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_);
};
muduo::net::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 retrieve(size_t len) void retrieveInt64() void retrieveInt32() void retrieveInt16() void retrieveInt8() string retrieveAllAsString() string retrieveAsString(size_t len) private: std::vector<char> buffer_; size_t readerIndex_; size_t writerIndex_; static const char kCRLF[];
};
漢譯英服務
服務端
#include "include/muduo/net/TcpServer.h"
#include "include/muduo/net/TcpConnection.h"
#include "include/muduo/net/EventLoop.h"#include <iostream>
#include <functional>
#include <unordered_map>class TranslateServer
{public:TranslateServer(uint16_t port): _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port),"TranslateServer", muduo::net::TcpServer::kReusePort){// 將成員函數設置成server的回調函數_server.setConnectionCallback(std::bind(&TranslateServer::onConnection, this, std::placeholders::_1));_server.setMessageCallback(std::bind(&TranslateServer::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 << "新連接建立成功!" << std::endl;}else{std::cout << "新連接退出" << std::endl;}}std::string translate(const std::string& req){static std::unordered_map<std::string,std::string> dictMap = {{"hello","你好"},{"Hello","你好"},{"吃了嗎","肉夾饃"}};auto it = dictMap.find(req);if(it == dictMap.end())return "沒找到";elsereturn it->second;}void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buffer, muduo::Timestamp){// 處理請求的回調函數//1.從buffer中拿到請求std::string req = buffer->retrieveAllAsString();//2.對請求進行處理std::string resp = translate(req);//3.發送響應conn->send(resp);}private:muduo::net::TcpServer _server;muduo::net::EventLoop _baseloop;
};int main()
{TranslateServer server(8088);server.Start();return 0;
}
客戶端
#include "include/muduo/net/TcpServer.h"
#include "include/muduo/net/TcpClient.h"
#include "include/muduo/net/TcpConnection.h"
#include "include/muduo/base/CountDownLatch.h"
#include "include/muduo/net/EventLoopThread.h"#include <iostream>
#include <functional>class TranslateClient
{
public:TranslateClient(const std::string &server_ip, int server_port): _latch(1), _client(_loopthread.startLoop(), muduo::net::InetAddress(server_ip,server_port), "TranslateClient"){_client.setConnectionCallback(std::bind(&TranslateClient::onConnection, this, std::placeholders::_1));_client.setMessageCallback(std::bind(&TranslateClient::onMessage, this, std::placeholders::_1,std::placeholders::_2, std::placeholders::_3));}// 阻塞式等待連接成功void connect(){_client.connect();_latch.wait();}bool send(const std::string &msg){//連接狀態正常再發送if(_conn->connected()){_conn->send(msg);return true;}return false;}private:// 建立連接成功后,喚醒上面的阻塞void onConnection(const muduo::net::TcpConnectionPtr &conn){if(conn->connected()){_latch.countDown();_conn = conn;}else{//連接關閉時的操作_conn.reset();}}void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buffer, muduo::Timestamp){std::cout<<"翻譯結果:"<<buffer->retrieveAllAsString()<<std::endl;}muduo::CountDownLatch _latch;muduo::net::EventLoopThread _loopthread;muduo::net::TcpClient _client;muduo::net::TcpConnectionPtr _conn;
};int main()
{TranslateClient client("127.0.0.1", 8088);client.connect();while (1){std::string msg;std::cin >> msg;client.send(msg);}return 0;
}