boost asio异步读写网络聊天室【官方示例】

来源:互联网 发布:java制作计算器 编辑:程序博客网 时间:2024/04/19 18:20
//// chat_message.hpp// ~~~~~~~~~~~~~~~~//// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)//// Distributed under the Boost Software License, Version 1.0. (See accompanying// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)//#pragma warning(disable:4996)#ifndef CHAT_MESSAGE_HPP#define CHAT_MESSAGE_HPP#include <cstdio>#include <cstdlib>#include <cstring>class chat_message{public:enum { header_length = 4 };enum { max_body_length = 512 };chat_message(): body_length_(0){}const char* data() const{return data_;}char* data(){return data_;}size_t length() const{return header_length + body_length_;}const char* body() const{return data_ + header_length;}char* body(){return data_ + header_length;}size_t body_length() const{return body_length_;}void body_length(size_t length){body_length_ = length;if (body_length_ > max_body_length)body_length_ = max_body_length;}bool decode_header(){using namespace std; // For strncat and atoi.char header[header_length + 1] = "";strncat(header, data_, header_length);body_length_ = atoi(header);if (body_length_ > max_body_length){body_length_ = 0;return false;}return true;}void encode_header(){using namespace std; // For sprintf and memcpy.char header[header_length + 1] = "";sprintf(header, "%4d", body_length_);memcpy(data_, header, header_length);}private:char data_[header_length + max_body_length];size_t body_length_;};#endif // CHAT_MESSAGE_HPP

服务端:server.cpp

// server.cpp : 定义控制台应用程序的入口点。// 聊天室程序,支持多个client相互聊天#include "stdafx.h"//// chat_server.cpp// ~~~~~~~~~~~~~~~//// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)//// Distributed under the Boost Software License, Version 1.0. (See accompanying// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)//#include <algorithm>#include <cstdlib>#include <deque>#include <iostream>#include <list>#include <set>#include <boost/bind.hpp>#include <boost/shared_ptr.hpp>#include <boost/enable_shared_from_this.hpp>#include <boost/asio.hpp>#include "chat_message.hpp"using boost::asio::ip::tcp;using namespace std;//----------------------------------------------------------------------typedef std::deque<chat_message> chat_message_queue;//----------------------------------------------------------------------// 一个抽象类,用于提供聊天室成员的接口class chat_participant //  聊天参与者{public:virtual ~chat_participant() {}virtual void deliver(const chat_message& msg) = 0; // 发言};typedef boost::shared_ptr<chat_participant> chat_participant_ptr;//----------------------------------------------------------------------class chat_room // 聊天室{public:void join(chat_participant_ptr participant) // 6{cout<<__FUNCTION__<<endl;participants_.insert(participant);// 把聊天室内缓存的消息发送给新加入的成员,相当于:// chat_message_queue::const_iterator it;// for(it = recent_msgs_.begin(); it!=recent_msgs_.end(); ++it)// participant->deliver(*it); std::for_each(recent_msgs_.begin(), recent_msgs_.end(),boost::bind(&chat_participant::deliver, participant, _1)); // 12 动态绑定}void leave(chat_participant_ptr participant){cout<<__FUNCTION__<<endl;participants_.erase(participant);}// 存入msg到缓冲区队列void deliver(const chat_message& msg) // 11 发言{cout<<__FUNCTION__<<endl;recent_msgs_.push_back(msg);while (recent_msgs_.size() > max_recent_msgs)recent_msgs_.pop_front(); // 将过时发言清出缓冲区// 将新消息发给每个聊天室成员,相当于:// std::set<chat_participant_ptr>::iterator it;// for(it=participants_.begin(); it!=participants_.end(); ++it)// (*it)->deliver(msg);std::for_each(participants_.begin(), participants_.end(),boost::bind(&chat_participant::deliver, _1, boost::ref(msg))); // 12}private:std::set<chat_participant_ptr> participants_; // 当前聊天室的n个参与者:set,不能重复enum { max_recent_msgs = 100 }; // 最大最近消息:缓冲区最多保存最近100条发言chat_message_queue recent_msgs_; // 消息队列:deque,先到先出};//----------------------------------------------------------------------// 在聊天室环境下,一个session就是一个成员class chat_session: public chat_participant, // 继承public boost::enable_shared_from_this<chat_session> // 可以使用shared_from_this()(即shared_ptr<chat_session>){public:chat_session(boost::asio::io_service& io_service, chat_room& room) // 2 7: socket_(io_service),room_(room){cout<<__FUNCTION__<<endl;}tcp::socket& socket() // 3 8{cout<<__FUNCTION__<<endl;return socket_;}void start() // 5{cout<<__FUNCTION__<<endl;room_.join(shared_from_this());  // 6 room_.join(shared_ptr<chat_session>);// async_read是事件处理一个机制,使用回调函数从而实现事件处理器方法// 本示例大量采用这个机制,也就是异步机制// 通过回调函数可以形成一个事件链,即在回调函数中设置一个新的事件与新回调函数boost::asio::async_read(socket_,boost::asio::buffer(read_msg_.data(), chat_message::header_length), // 取buffer头部,正文字符数到read_msg_boost::bind(&chat_session::handle_read_header, shared_from_this(), // 9 调用:shared_from_this()->handle_read_header(boost::asio::placeholders::error);boost::asio::placeholders::error));}// 存buffer中的数据到read_msg_:header部分void handle_read_header(const boost::system::error_code& error) // 9{cout<<__FUNCTION__<<endl;if (!error && read_msg_.decode_header()){boost::asio::async_read(socket_,boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),// 取buffer文本部分到read_msg_boost::bind(&chat_session::handle_read_body, shared_from_this(), // 10 调用:shared_from_this()->handle_read_body(boost::asio::placeholders::error);boost::asio::placeholders::error));}else{room_.leave(shared_from_this()); // 14}}// 存buffer中的数据到read_msg_:body部分void handle_read_body(const boost::system::error_code& error) // 10{cout<<__FUNCTION__<<endl;if (!error){room_.deliver(read_msg_); // 11boost::asio::async_read(socket_,boost::asio::buffer(read_msg_.data(), chat_message::header_length), // 取buffer头部,正文字符数到read_msg_boost::bind(&chat_session::handle_read_header, shared_from_this(),// 调用:shared_from_this()->handle_read_header(boost::asio::placeholders::error);boost::asio::placeholders::error));}else{room_.leave(shared_from_this());}}//存入数据到write_msgs_,送队列的最开始一条发言到buffervoid deliver(const chat_message& msg) // 12,有几个客户端调用几次{cout<<__FUNCTION__<<endl;bool write_in_progress = !write_msgs_.empty();write_msgs_.push_back(msg);if (!write_in_progress){boost::asio::async_write(socket_,boost::asio::buffer(write_msgs_.front().data(),write_msgs_.front().length()), // 队列的最开始一条发言到bufferboost::bind(&chat_session::handle_write, shared_from_this(), // 13 shared_from_this()->handle_write(boost::asio::placeholders::error)boost::asio::placeholders::error));}}// 把write_msgs_数据送buffer,使客户端可以得到,递归调用自身值到write_msgs_为空void handle_write(const boost::system::error_code& error) // 13,有几个客户端调用几次{cout<<__FUNCTION__<<endl;if (!error){write_msgs_.pop_front();if (!write_msgs_.empty()){boost::asio::async_write(socket_,boost::asio::buffer(write_msgs_.front().data(),// 队列的最开始一条发言到bufferwrite_msgs_.front().length()),boost::bind(&chat_session::handle_write, shared_from_this(),// 13 shared_from_this()->handle_write(boost::asio::placeholders::error)boost::asio::placeholders::error));}}else{room_.leave(shared_from_this());}}private:tcp::socket socket_;chat_room& room_;chat_message read_msg_; // 存从buffer读出的数据chat_message_queue write_msgs_; // 欲写入buffer的数据队列,deque};typedef boost::shared_ptr<chat_session> chat_session_ptr;//----------------------------------------------------------------------class chat_server{public:chat_server(boost::asio::io_service& io_service, // 1const tcp::endpoint& endpoint): io_service_(io_service),acceptor_(io_service, endpoint){cout<<__FUNCTION__<<endl;chat_session_ptr new_session(new chat_session(io_service_, room_)); // 2acceptor_.async_accept(new_session->socket(), // 3boost::bind(&chat_server::handle_accept, this, new_session, // 4  this->handle_accept(new_session, boost::asio::placeholders::error);boost::asio::placeholders::error));}// 有连接到来时触发,然后等待下个连接到来void handle_accept(chat_session_ptr session, // 4const boost::system::error_code& error){cout<<__FUNCTION__<<endl;if (!error){session->start(); // 5 chat_session_ptr new_session(new chat_session(io_service_, room_)); // 7acceptor_.async_accept(new_session->socket(), // 8boost::bind(&chat_server::handle_accept, this, new_session, //this->handle_accept(new_session, boost::asio::placeholders::error);boost::asio::placeholders::error));}}private:boost::asio::io_service& io_service_;tcp::acceptor acceptor_;chat_room room_;};typedef boost::shared_ptr<chat_server> chat_server_ptr;typedef std::list<chat_server_ptr> chat_server_list;//----------------------------------------------------------------------int _tmain(int argc, _TCHAR* argv[]){// 调用栈:// 打开服务端并等待连接:1-3,acceptor_.async_accept()// 一个客户端进入并连接:4-8,然后等待下个客户端:acceptor_.async_accept();又加入一个客户端:重复调用4-8// 任意客户端发言:9-13,12\13调用n次;任意客户发言,重复9-13// 聊天中途又新加入一个客户端:456 12(n) 78 13(n)// 一个客户端断开连接:9 14// 异步机制:立刻返回,boost提供的异步函数前缀async_要求一个回调函数,回调函数只在某一事件触发时才被调用// 回调函数:void your_completion_handler(const boost::system::error_code& ec);try{boost::asio::io_service io_service;chat_server_list servers; // server列表:一个server就是一个聊天室tcp::endpoint endpoint(/*boost::asio::ip::address_v4::from_string("127.0.0.1")*/tcp::v4(), 1000);chat_server_ptr server(new chat_server(io_service, endpoint)); // 1servers.push_back(server);io_service.run();}catch (std::exception& e){std::cerr << "Exception: " << e.what() << "\n";}return 0;}
客户端:client.cpp

// client.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"//// chat_client.cpp// ~~~~~~~~~~~~~~~//// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)//// Distributed under the Boost Software License, Version 1.0. (See accompanying// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)//#include <cstdlib>#include <deque>#include <iostream>#include <boost/bind.hpp>#include <boost/asio.hpp>#include <boost/thread.hpp>#include "chat_message.hpp"using boost::asio::ip::tcp;using namespace std;typedef std::deque<chat_message> chat_message_queue;class chat_client{public:chat_client(boost::asio::io_service& io_service, // 1tcp::resolver::iterator endpoint_iterator): io_service_(io_service),socket_(io_service){cout<<__FUNCTION__<<endl;tcp::endpoint endpoint = *endpoint_iterator;socket_.async_connect(endpoint,boost::bind(&chat_client::handle_connect, this, // 2boost::asio::placeholders::error, ++endpoint_iterator));}void write(const chat_message& msg) // 5{cout<<__FUNCTION__<<endl;io_service_.post(boost::bind(&chat_client::do_write, this, msg));}void close(){cout<<__FUNCTION__<<endl;io_service_.post(boost::bind(&chat_client::do_close, this));}private:void handle_connect(const boost::system::error_code& error, // 2tcp::resolver::iterator endpoint_iterator){cout<<__FUNCTION__<<endl;if (!error){boost::asio::async_read(socket_,boost::asio::buffer(read_msg_.data(), chat_message::header_length),  //copy buffer to read_msg_'s headerboost::bind(&chat_client::handle_read_header, this, // 3boost::asio::placeholders::error));}else if (endpoint_iterator != tcp::resolver::iterator()){socket_.close();tcp::endpoint endpoint = *endpoint_iterator;socket_.async_connect(endpoint,boost::bind(&chat_client::handle_connect, this, // 2boost::asio::placeholders::error, ++endpoint_iterator));}}void handle_read_header(const boost::system::error_code& error) // 3{cout<<__FUNCTION__<<endl;if (!error && read_msg_.decode_header()){boost::asio::async_read(socket_,boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), //copy buffer to read_msg_'s bodyboost::bind(&chat_client::handle_read_body, this, // 4boost::asio::placeholders::error));}else{do_close();}}void handle_read_body(const boost::system::error_code& error) // 4{cout<<__FUNCTION__<<endl;if (!error){std::cout.write(read_msg_.body(), read_msg_.body_length()); // print read_msg_'s bodystd::cout << "\n";boost::asio::async_read(socket_,boost::asio::buffer(read_msg_.data(), chat_message::header_length),boost::bind(&chat_client::handle_read_header, this, // 4boost::asio::placeholders::error));}else{do_close();}}void do_write(chat_message msg) // 6{cout<<__FUNCTION__<<endl;bool write_in_progress = !write_msgs_.empty();write_msgs_.push_back(msg);if (!write_in_progress){boost::asio::async_write(socket_,boost::asio::buffer(write_msgs_.front().data(),write_msgs_.front().length()), // copy write_msgs_.front() to bufferboost::bind(&chat_client::handle_write, this, // 7 send messageboost::asio::placeholders::error));}}void handle_write(const boost::system::error_code& error) // 7{cout<<__FUNCTION__<<endl;if (!error){write_msgs_.pop_front();if (!write_msgs_.empty()){boost::asio::async_write(socket_,boost::asio::buffer(write_msgs_.front().data(),write_msgs_.front().length()),boost::bind(&chat_client::handle_write, this, // 7boost::asio::placeholders::error));}}else{do_close();}}void do_close(){cout<<__FUNCTION__<<endl;socket_.close();}private:boost::asio::io_service& io_service_;tcp::socket socket_;chat_message read_msg_;  // 存从buffer读出的数据chat_message_queue write_msgs_; // 欲写入buffer的数据队列,deque};int _tmain(int argc, _TCHAR* argv[]){// 调用栈:// 打开客户端并连接(聊天室没有对话时):1-2// 打开客户端并连接(聊天室有对话时):1-4// 自己发言:56734// 聊天室内其他成员发言:34try{boost::asio::io_service io_service;tcp::resolver resolver(io_service);tcp::resolver::query query("127.0.0.1", "1000"); // ip port:本机tcp::resolver::iterator iterator = resolver.resolve(query);chat_client c(io_service, iterator); // 初始化、连接boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); // 线程char line[chat_message::max_body_length + 1];while (std::cin.getline(line, chat_message::max_body_length + 1)){chat_message msg;msg.body_length(strlen(line));memcpy(msg.body(), line, msg.body_length());// line to msgmsg.encode_header();c.write(msg);}c.close();t.join(); // 执行线程}catch (std::exception& e){std::cerr << "Exception: " << e.what() << "\n";}return 0;}

在这里,为了查看程序的调用过程,在每个成员函数中加了一条:
cout<<__FUNCTION__<<endl;

同时,为了测试方便,固定ip与端口为本机,而不是从main参数中传递进来

参考资料:http://blog.csdn.net/cyg0810/article/details/36179195


1 0