//
// chat_server.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 <list>
#include <memory>
#include <set>
#include <utility>
#include <boost/asio.hpp>
#include "../chat_message.hpp"
using boost::asio::ip::tcp;
//----------------------------------------------------------------------
// 聊天的消息队列
typedef std::deque<chat_message> chat_message_queue;
//----------------------------------------------------------------------
// 聊天参与者的纯虚接口类
class chat_participant
{
public:
virtual ~chat_participant() {}
virtual void deliver(const chat_message& msg) = 0;
};
// 聊天参与者的共享指针类型
typedef std::shared_ptr<chat_participant> chat_participant_ptr;
//----------------------------------------------------------------------
// 聊天室
class chat_room
{
public:
// 新加入聊天人员
void join(chat_participant_ptr participant)
{
participants_.insert(participant);
for (auto msg: recent_msgs_)
participant->deliver(msg);
}
// 删除该聊天人员
void leave(chat_participant_ptr participant)
{
participants_.erase(participant);
}
// 发布该聊天消息
void deliver(const chat_message& msg)
{
// 添加到聊天队列中,如果超出最大消息数目,则弹出1条最早的消息
recent_msgs_.push_back(msg);
while (recent_msgs_.size() > max_recent_msgs)
recent_msgs_.pop_front();
// 给聊天室内每个人发送最新消息
for (auto participant: participants_)
participant->deliver(msg);
}
private:
// 聊天参与者容器
std::set<chat_participant_ptr> participants_;
// 最大100条最近的消息
enum { max_recent_msgs = 100 };
// 聊天消息队列
chat_message_queue recent_msgs_;
};
//----------------------------------------------------------------------
// 新的聊天客户端会话
class chat_session
: public chat_participant,
public std::enable_shared_from_this<chat_session>
{
public:
chat_session(tcp::socket socket, chat_room& room)
: socket_(std::move(socket)),
room_(room)
{
}
void start()
{
// 把该聊天客户端加入到聊天室中
room_.join(shared_from_this());
// 读取消息头
do_read_header();
}
// 发送聊天消息给该客户端
void deliver(const chat_message& msg)
{
bool write_in_progress = !write_msgs_.empty();
write_msgs_.push_back(msg);
if (!write_in_progress)
{
do_write();
}
}
private:
void do_read_header()
{
// 指向this的安全共享指针
auto self(shared_from_this());
// 异步调用读取函数,当读取到或发生错误时,会回调第3个函数指向的Lambda函数
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
// 没有发生错误,且帧头正确,则继续读取帧体(最大512字节)
if (!ec && read_msg_.decode_header())
{
do_read_body();
}
else
{
// 发生错误,把该聊天会话的客户端从聊天室中删除
room_.leave(shared_from_this());
}
});
}
void do_read_body()
{
// 指向this的安全共享指针
auto self(shared_from_this());
// 异步读取针体,最大512字节。当有错误发生,或读取完毕,会自动回调第3个函数指向的Lambda函数
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec) // 没有发生错误
{
// 将收到的消息发送到聊天室中,会发给每一个客户端
room_.deliver(read_msg_);
// 继续读取帧头
do_read_header();
}
else // 发生错误,从聊天室中删除该对象
{
room_.leave(shared_from_this());
}
});
}
// 向该客户端发送消息队列中的消息
void do_write()
{
// 取得this对象的安全共享指针
auto self(shared_from_this());
// 执行异步写数据给客户端:从发送消息队列中取条消息,当发送成功或发生错误时,调用第3个参数指向的Lambda函数
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec) // 没有发生错误
{
// 把已经被成功发送的消息弹出队列
write_msgs_.pop_front();
// 如果队列中还有消息,则继续发送
if (!write_msgs_.empty())
{
do_write();
}
}
else // 发生错误,从聊天室中删除该对象
{
room_.leave(shared_from_this());
}
});
}
// 该聊天对象的socket对象
tcp::socket socket_;
// Sever中的聊天室房间,所有客户端成员均在该聊天室中
chat_room& room_;
// 读取到的消息
chat_message read_msg_;
// 待发送的消息队列
chat_message_queue write_msgs_;
};
//----------------------------------------------------------------------
class chat_server
{
public:
chat_server(boost::asio::io_context& io_context,
const tcp::endpoint& endpoint)
: acceptor_(io_context, endpoint)
{
// server对象构造后,直接进入accept接受客户端连接的异步调用
do_accept();
}
private:
void do_accept()
{
// 执行异步调用接受客户端连接,当有连接到来、或错误发生,会回调下面指定的lambda函数
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket)
{
if (!ec)
{
// 创建新的客户端会话,并调用新客户端的start()方法
std::make_shared<chat_session>(std::move(socket), room_)->start();
}
// 继续接受新的客户端连接
do_accept();
});
}
// 单个服务器端的接受器
tcp::acceptor acceptor_;
// 单个服务器端使用的聊天室
chat_room room_;
};
//----------------------------------------------------------------------
int main(int argc, char* argv[])
{
try
{
// 参数1:程序名, 参数2:端口号1, 参数3:端口号2, 参数4:端口号3 ......
if (argc < 2)
{
std::cerr << "Usage: chat_server <port> [<port> ...]\n";
return 1;
}
// 创建网络io服务上下文
boost::asio::io_context io_context;
// 创建server组,允许存在多个服务端,每个服务端的端口号必须不一样
std::list<chat_server> servers;
for (int i = 1; i < argc; ++i)
{
tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[i]));
// 添加到Server list的尾部
servers.emplace_back(io_context, endpoint);
}
// 直到所有网络事件退出,才会返回该函数调用
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
![avatar](https://profile-avatar.csdnimg.cn/default.jpg!1)
piaozhou_cd
- 粉丝: 2
- 资源: 17
最新资源
- 打包和分发Rust工具.pdf
- SQL中的CREATE LOGFILE GROUP 语句.pdf
- C语言-leetcode题解之第172题阶乘后的零.zip
- C语言-leetcode题解之第171题Excel列表序号.zip
- C语言-leetcode题解之第169题多数元素.zip
- ocr-图像识别资源ocr-图像识别资源
- 图像识别:基于Resnet50 + VGG16模型融合的人体细胞癌症分类模型实现-图像识别资源
- C语言-leetcode题解之第168题Excel列表名称.zip
- C语言-leetcode题解之第167题两数之和II-输入有序数组.zip
- C语言-leetcode题解之第166题分数到小数.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
![feedback](https://img-home.csdnimg.cn/images/20220527035711.png)
![feedback](https://img-home.csdnimg.cn/images/20220527035711.png)
![feedback-tip](https://img-home.csdnimg.cn/images/20220527035111.png)