第一篇:asio网络通讯
开通博客很久了,一直没有写什么有价值的东西,此中缘由一半是因为没什么值得写的,一半是觉得自己所知太少。最近闲下来,想把自己的一些心得体会分享出来,也为自己做一个总结。ok废话不多说了。
最近公司做了一个捕鱼项目,和捕鱼达人玩法相类似的一款手游。
本人是客户端,使用了技术是cocos2dx,版本是3.2的引擎,Java服务器的东西这里就不做赘述(其实是我不了解)。在以后的每期更新中,重点记载一下客户端的相关内同。
因为是网络游戏,第一个重点是网络,第二个重点是游戏。各位即使大拿们如果发现其中的错误,请勿喷。嘴下留情,谢谢
网络:
关于网络,个人还是比较喜欢原生态的socket通信的,原生态的socket,套用linux的epool封装成异步网络模型,个人还是比较热衷,奈何ios系统不能支持epool(ios支持pool),同时项目负责人对asio比较熟悉,好吧,放弃epool自己封装。ps:后面会补上关于epool封装的心得。
asio c++ library
google搜索asio上面第一句就是:
Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.
翻译:asio是一个跨平台的提供网络底层,为开发者提供的一个异步模型的网络库。在写此文章的时候asio更新到1.10.4.请下载asio的源码,然后把include中asio文件夹拷贝到项目中,此部分是asio的核心。提供asio的底层支持.同时拷贝asio.hpp文件到项目中,参考源码库中的,该文件与asio文件夹同级,使用的时候直接在需要的文件中添加引用即可 #include <asio.hpp>
这里添加一点hpp文件说明
其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再将cpp加入到project中进行编译。而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,采用hpp将大幅度减少调用project中的cpp文件数与编译次数,也不用再发布烦人的lib与dll,因此非常适合用来编写公用的开源库
hpp的相关内容不再介绍。asio的相关东东,本人仅限于了了解,如果有大神精通,希望大神能给予指点。
贴第一份代码
// // AsioSocketClient.cpp #include <cstdlib> #include <deque> #include <iostream> #include <thread> #include <asio.hpp> #include <google/protobuf/message.h> #include "ReceiveMessage.hpp" #include "AsioSendMessage.hpp" #include "CommonMsgHandler.h" #include "CmdTypeMap.h" #include "commond/RequestProt.pb.h" using asio::ip::tcp; typedef std::deque<AsioSendMessage> SendMessageQueue; class AsioSocketClient { public: AsioSocketClient(asio::io_service& io_service, tcp::resolver::iterator _endpoint_iterator) : io_service_(io_service), socket_(io_service), read_cmdId_(0) { endpoint_iterator = _endpoint_iterator; do_connect(endpoint_iterator, nullptr, nullptr); } void write(const AsioSendMessage& msg) { io_service_.post( [this, msg]() { bool write_in_progress = !write_msgs_.empty(); write_msgs_.push_back(msg); if (!write_in_progress) { do_write(); } }); } void close() { io_service_.post([this]() { write_msgs_.clear(); socket_.close(); }); } bool isOpen(){ return socket_.is_open(); } private: void do_connect(tcp::resolver::iterator endpoint_iterator , std::function< void(int res) > func_sucess , std::function< void(error_code ec) > func_fail) { asio::async_connect(socket_, endpoint_iterator, [this, func_sucess, func_fail](std::error_code ec, tcp::resolver::iterator) { if (!ec) { cocos2d::log("Connect success"); if(func_sucess != nullptr){ func_sucess(0); } do_read_header(); }else{ if (func_fail != nullptr){ func_fail(ec); } } }); } void do_read_header() { asio::async_read(socket_, asio::buffer(read_msg_.data(), ReceiveMessage::header_length + ReceiveMessage::cmdid_length), [this](std::error_code ec, std::size_t /*length*/) { if (!ec && read_msg_.decode_header(&read_cmdId_)) { do_read_body(); } else { EventObject eventObject; eventObject.cmd_id = (int)SOCKET_CMD_ERROR; eventObject.protobuf_msg = nullptr; CommonMsgHandler::sharedMsgHandler()->addEvent(eventObject); socket_.close(); } }); } void do_read_body() { asio::async_read(socket_, asio::buffer(read_msg_.body(), read_msg_.body_length()), [this](std::error_code ec, std::size_t /*length*/) { if (!ec) { void* body_ = malloc(read_msg_.body_length()); memcpy(body_, read_msg_.body(), read_msg_.body_length()); chooseCmdId ( read_cmdId_ , body_ ,read_msg_.body_length() ); free (body_); do_read_header(); } else { EventObject eventObject; eventObject.cmd_id = (int)SOCKET_CMD_ERROR; eventObject.protobuf_msg = nullptr; CommonMsgHandler::sharedMsgHandler()->addEvent(eventObject); } }); } void do_write() { std::error_code ec; asio::write(socket_, asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), ec); if (!ec) { if(write_msgs_.front().func_sucess != nullptr){ write_msgs_.front().func_sucess(0); } write_msgs_.pop_front(); if (!write_msgs_.empty()) { do_write(); } }else{ if (!write_msgs_.empty()) write_msgs_.clear(); } } void chooseCmdId(int32_t cmdid ,void * body ,int size){ std::string type_name(""); if (CmdTypeMap::getInstance()->getCmdTypeMap()->find(cmdid) != CmdTypeMap::getInstance()->getCmdTypeMap()->end() ){ type_name = CmdTypeMap::getInstance()->getCmdTypeMap()->find(cmdid)->second; } google::protobuf::Message *msg = createMessage(type_name); if (!msg) { return; } msg->ParseFromArray(body, size); EventObject eventObject; eventObject.cmd_id = cmdid; eventObject.protobuf_msg = msg; CommonMsgHandler::sharedMsgHandler()->addEvent(eventObject); } inline google::protobuf::Message* createMessage(const std::string& type_name) { google::protobuf::Message* message = NULL; const google::protobuf::Descriptor* descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(type_name); if (descriptor) { const google::protobuf::Message* prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor); if (prototype) { message = prototype->New(); } } return message; } public: void reconnect(string ip, string port, std::function< void(int res) > success_func, std::function< void(error_code ec) > fail_func){ using asio::ip::tcp; tcp::resolver::query query(ip, port); tcp::resolver resolver(io_service_); auto endpoint_iterator = resolver.resolve(query); do_connect(endpoint_iterator, success_func, fail_func); } private: asio::io_service& io_service_; tcp::resolver::iterator endpoint_iterator; tcp::socket socket_; ReceiveMessage read_msg_; int32_t read_cmdId_; SendMessageQueue write_msgs_; };
各位看官会发现,除了上面的几个头文件是系统的,其余要么是google的protobuf,要么是自己写的文件,这些在后面都会一一补上,今天先到这里
2014-11-12
浙公网安备 33010602011771号