boost异步tcp通信技术练习

2023-05-16

 

本例演示了基本的boost异步tcp服务器程序的编写。

演示程序的构思是这样的:

服务端:

l  控制台程序;

l  侦听客户端连接,当有新连接时,在屏幕上打印消息;

l  当收到客户端数据包时,在屏幕上打印出数据包的大小;

l  当客户端断开时,在屏幕上打印消息;

l  用户在窗口输入exit时,退出程序

l  程序入口:main.cpp

l  类CAsyncTcpServer,头文件CAsyncTcpServer.h,源文件CAsyncTcpServer.cpp

客户端:

l  控制台程序;

l  启动后连接服务器,进入主循环

l  等待用户输入数据包大小,向服务端发送数据包

l  输入0退出程序

 

服务端:main.cpp


#include "stdafx.h"
#include <string>
#include <iostream>
#include "AsyncTcpServer.h"

using namespace std;

class CEventHandler : public CAsyncTcpServer::IEventHandler
{
public:
    virtual void ClientConnected(int clientId)
    {
        cout << "Client: " << clientId << " connected." << endl;
    }
    virtual void ClientDisconnect(int clientId)
    {
        cout << "Client: " << clientId << " disconnected." << endl;
    }
    virtual void ReceiveData(int clientId, const BYTE* data, size_t length)
    {
        cout << "Client: " << clientId << " receive data size: " << length << endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    string input;

    CAsyncTcpServer tcpServer(3, 10000);
    CEventHandler eventHandler;

    tcpServer.AddEventHandler(&eventHandler);
    
    while(1){
        cin >> input;
        if(input == "exit"){
            break;
        }
    }

    return 0;
}  

 

服务端:AsyncTcpServer.h


#pragma once
#include <thread>
#include <array>
#include <boost\bind.hpp>
#include <boost\noncopyable.hpp>
#include <boost\asio.hpp>
#include <boost\asio\placeholders.hpp>

using namespace boost::asio;
using namespace boost::asio::ip;

using namespace std;

///
// 对应一个Tcp客户端连接
///
class CTcpConnection
{
public:
    CTcpConnection(io_service& ios, int clientId) : m_socket(ios), m_clientId(clientId){}
    ~CTcpConnection(){}

    int                        m_clientId;
    tcp::socket                m_socket;
    array<BYTE, 16 * 1024>    m_buffer;
};
typedef shared_ptr<CTcpConnection>    TcpConnectionPtr;

///
// 异步TCP服务器
///
class CAsyncTcpServer
    : public boost::noncopyable
{
public:
    // 事件通知接口,类似于信号槽的机制,个人更喜欢这种简单粗暴的方式
    // 想要接受通知的对象只要实现这个接口,再将接口指针通过AddEventHandler添加进来就行了
    class IEventHandler
    {
    public:
        IEventHandler(){}
        virtual ~IEventHandler(){}
        virtual void ClientConnected(int clientId) = 0;
        virtual void ClientDisconnect(int clientId) = 0;
        virtual void ReceiveData(int clientId, const BYTE* data, size_t length) = 0;
    };
public:
    CAsyncTcpServer(int maxClientNumber, int port);
    ~CAsyncTcpServer();
    void AddEventHandler(IEventHandler* pHandler){ m_EventHandlers.push_back(pHandler); }

    void Send(int clientId, const BYTE* data, size_t length);

private:
    void bind_hand_read(CTcpConnection* client);
    void handle_accept(const boost::system::error_code& error);
    void handle_read(CTcpConnection* client, const boost::system::error_code& error, size_t bytes_transferred);

private:
    thread                m_thread;

    io_service            m_ioservice;

    // io_service中维持一个任务队列
    // 不使用io_service::work时,调用io_service::run时,执行完任务队列中的任务,函数就返回
    // 使用io_service::work时,任务队列为空时,io_service::run会挂起等待新任务
    io_service::work    m_work;

    tcp::acceptor        m_acceptor;

    int                            m_maxClientNumber;
    int                            m_clientId;
    TcpConnectionPtr            m_nextClient;
    map<int, TcpConnectionPtr>    m_clients;

    vector<IEventHandler*>        m_EventHandlers;
};  

 

服务端:AsyncTcpServer.cpp


#include "stdafx.h"

#include "AsyncTcpServer.h"

///
// CAsyncTcpServer的实现
///
CAsyncTcpServer::CAsyncTcpServer(int maxClientNumber, int port)
    : m_ioservice()
    , m_work(m_ioservice)
    , m_acceptor(m_ioservice)
    , m_maxClientNumber(maxClientNumber)
    , m_clientId(0)
{
    // thread对象不能使用临时变量,否则会析构
    // io_service::run本身不创建线程,只能依托于用户创建的线程里运行
    // io_service::run有两个重载,需要显式指定使用哪个重载,这里的两个类名都不能省略
    // 可以创建线程池,实现多线程并发
    m_thread = thread((size_t(io_service::*)())&io_service::run, &m_ioservice);

    m_nextClient = make_shared<CTcpConnection>(m_ioservice, m_clientId);
    m_clientId++;

    tcp::endpoint endpoint(tcp::v4(), port);
    m_acceptor.open(endpoint.protocol());
    m_acceptor.set_option(tcp::acceptor::reuse_address(true));
    m_acceptor.bind(endpoint);
    m_acceptor.listen();

    // 异步等待客户端连接
    m_acceptor.async_accept(m_nextClient->m_socket, boost::bind(&CAsyncTcpServer::handle_accept, this, boost::asio::placeholders::error));
}

CAsyncTcpServer::~CAsyncTcpServer()
{
    for (map<int, TcpConnectionPtr>::iterator it = m_clients.begin(); it != m_clients.end(); ++it){
        it->second->m_socket.close();
    }

    // 让m_thread对应的线程结束,不调用这个,会一直阻塞在m_thread.join()上
    m_ioservice.stop();

    // 不加这一行,退出程序时会报错
    m_thread.join();
}

void CAsyncTcpServer::Send(int clientId, const BYTE* data, size_t length)
{
    map<int, TcpConnectionPtr>::iterator it = m_clients.find(clientId);
    if (it == m_clients.end()){
        return;
    }
    // 同步发送数据
    it->second->m_socket.write_some(boost::asio::buffer(data, length));
}

void CAsyncTcpServer::handle_accept(const boost::system::error_code& error)
{
    if (!error){
        // 判断连接数目是否达到最大限度
        if (m_maxClientNumber > 0 && m_clients.size() >= m_maxClientNumber){
            m_nextClient->m_socket.close();
        }
        else{
            // 发送客户端连接的消息
            for (int i = 0; i < m_EventHandlers.size(); ++i){
                m_EventHandlers[i]->ClientConnected(m_nextClient->m_clientId);
            }

            // 设置异步接收数据
            bind_hand_read(m_nextClient.get());

            // 将客户端连接放到客户表中
            m_clients.insert(make_pair(m_nextClient->m_clientId, m_nextClient));

            // 重置下一个客户端连接
            m_nextClient = make_shared<CTcpConnection>(m_ioservice, m_clientId);
            m_clientId++;
        }
    }

    // 异步等待下一个客户端连接
    m_acceptor.async_accept(m_nextClient->m_socket, boost::bind(&CAsyncTcpServer::handle_accept, this, boost::asio::placeholders::error));
}

void CAsyncTcpServer::bind_hand_read(CTcpConnection* client)
{
    // 客户端只要发送一个数据包,handle_read就会有响应,哪怕是1个字节
    // 客户端发送超过buffer大小的数据时,handle_read会响应前面几个满的buffer,以及最后一个不满的buffer
    client->m_socket.async_read_some(boost::asio::buffer(client->m_buffer),
        boost::bind(&CAsyncTcpServer::handle_read, this, client, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

    return;

    // 与client->m_socket.async_read_some行为一样,仔细对比了boost头文件里的注释,除了名字不一样,其他的一毛一样
    client->m_socket.async_receive(boost::asio::buffer(client->m_buffer),
        boost::bind(&CAsyncTcpServer::handle_read, this, client, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

    // 客户端发送数据,把buffer填满后,handle_read 才会响应
    // 当客户端发送超过buffer大小的数据时,handle_read 只会响应前面整数个数据包,余下的不满buffer大小的数据不会响应
    boost::asio::async_read(client->m_socket, boost::asio::buffer(client->m_buffer),
        boost::bind(&CAsyncTcpServer::handle_read, this, client, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

void CAsyncTcpServer::handle_read(CTcpConnection* client, const boost::system::error_code& error, size_t bytes_transferred)
{
    if (!error){
        // 发送收到数据的信息
        for (int i = 0; i < m_EventHandlers.size(); ++i){
            m_EventHandlers[i]->ReceiveData(client->m_clientId, client->m_buffer.data(), bytes_transferred);
        }

        // 执行一次bind操作,往io_service的任务队列中加入一个任务
        // 任务执行后,需要将该任务再次加入队列
        bind_hand_read(client);
    }
    else{
        // 发送客户端离线的消息
        for (int i = 0; i < m_EventHandlers.size(); ++i){
            m_EventHandlers[i]->ClientDisconnect(client->m_clientId);
        }
        m_clients.erase(client->m_clientId);
    }
}  

 

客户端:main.cpp

 


#include "stdafx.h"
#include <iostream>
#include <boost\array.hpp>
#include <boost\bind.hpp>
#include <boost\noncopyable.hpp>
#include <boost\asio.hpp>

using namespace boost::asio;
using namespace boost::asio::ip;

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    int size;
    try{
        io_service service;
        tcp::socket socket(service);
        boost::system::error_code error;

        tcp::endpoint endpoint(address_v4::from_string("127.0.0.1"), 10000);

        socket.connect(endpoint, error);

        while (1){
            cin >> size;
            if (size == 0){
                break;
            }
            vector<BYTE> data(size);
            for (int j = 0; j < data.size(); ++j){
                data[j] = j%128;
            }
            socket.write_some(boost::asio::buffer(data, data.size()));
        }
    }catch(exception& e){
        string s = e.what();
    }

    return 0;
}  

 

转载于:https://www.cnblogs.com/zhuyingchun/p/5557443.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boost异步tcp通信技术练习 的相关文章

  • Scapy 不需要的 RST TCP 数据包

    为了理解TCP是如何工作的 我尝试伪造自己的TCP SYN SYN ACK ACK 基于教程 http www thice nl creating ack get packets with scapy http www thice nl c
  • 在 Perl 中如何接受多个 TCP 连接?

    我对 Linux 的 Perl 脚本有疑问 它的主要目的是成为 3 个应用程序之间的中间人 它应该做什么 它应该能够等待 UDP 文本 不带空格 udp port 当它收到 UDP 文本时 它应该将其转发到连接的 TCP 客户端 问题是我的
  • Go TCP 读取是非阻塞的

    我正在尝试用 Go 创建服务器和客户端 我已经成功地与服务器和客户端进行通信 但我遇到的问题是golang中的TCP读取是非阻塞的 我想知道 golang 中的读取是否有可能像 C 中的读取一样阻塞 谢谢 EDIT 这是服务器的源代码 fu
  • Boost 更新失败,现在不确定我拥有哪个版本

    在 Ubuntu 22 04 上运行此命令以获得 Boost 1 82 sudo add apt repository ppa mhier libboost latest sudo apt update sudo apt install l
  • 是否可以通过互联网在两个移动设备 (iPhone) 之间连接套接字?

    是否可以通过互联网在两个移动设备 iPhone 之间连接套接字 我正在尝试发现每个设备的IP并直接连接 我知道可以使用 Bonjour 来完成 但这只适用于本地网络 我需要通过互联网在两个设备之间建立高速连接 Thanks 如果你有两个 I
  • 使用Boost获取成员函数的数量和参数类型? (升压::function_traits)

    对于普通的普通函数来说 它工作得很好 下面的代码工作得很好 它只打印应该的内容 int cdecl int char 2 int char include
  • 将向量或参数传递给 boost::process (boost::fusion)

    我正在尝试创建一个boost process来自字符串参数向量 void runProcess const std string exe const std vector
  • Linux环境下串口数据转换为TCP/IP

    我需要从Linux系统的串口获取数据并将其转换为TCP IP发送到服务器 这很难做到吗 我有一些基本的编程经验 但对 Linux 的经验不多 有没有开源应用程序可以做到这一点 在 Linux 中您不需要编写程序来执行此操作 只是pipe h
  • 如何构建 Boost::program_options

    我想使用 boost program options 安装boost后 我认为我必须单独构建program options http www boost org doc libs 1 43 0 more getting started wi
  • 不明确的元函数或未定义的类型

    我是元功能的新手 我想编写一个函数 将复合类型中某种类型的所有匹配项替换为其他类型 在示例中 replace
  • 如何取消 boost asio io_service 帖子

    如何取消已发布的回调 getIoService gt post boost bind MyClass myCallback this 并保持其他发布的回调不变 问题是我有一些对象从不同线程接收事件 并将它们发布到 ioservice 以便处
  • C# Socket.receive连续接收0字节且循环中不阻塞

    我正在尝试用 C 编写一个最简单的多线程 TCP 服务器 它接收来自多个客户端的数据 每次连接新客户端时 都会建立套接字连接 并将套接字作为参数传递给新类函数 之后运行 while 循环并接收数据 直到客户端连接为止 这里的问题是 sock
  • boost::serialization 序列化期间内存消耗较高

    正如主题所示 在将大量数据序列化到文件时 我遇到了 boost serialization 的一个小问题 问题在于应用程序序列化部分的内存占用量大约是要序列化的对象内存的 3 到 3 5 倍 值得注意的是 我拥有的数据结构是基类指针和指向该
  • [现代] C++ 中 N 个变量的范围/循环

    遍历 N 个任意类型的变量来执行操作的简洁方法是什么 假设我有变量a b c d e并想要对他们所有人进行一些操作 使用 Boost Hana 和通用 lambda include
  • 我如何知道谁持有shared_ptr<>?

    I use boost shared ptr在我的 C 应用程序中 内存问题确实很严重 应用程序占用大量内存 但是 因为我将每个新对象放入shared ptr 当应用程序退出时 无法检测到内存泄漏 一定有类似的东西std vector
  • 如何使用 Boost 程序选项提取已解析选项的序列?

    我正在使用 Boost Graph 和程序选项构建一个图形生成器 例如 有两种类型的组件 C 和 W 每种都有 1 个源 1 个接收器以及一些用于指定其间拓扑的附加参数 我希望能够按照命令行参数的顺序提供的顺序将它们拼接在一起 例如 bin
  • 获取 std::variant 当前持有的 typeid(如 boost::variant type())

    我已经从 boost variant 迁移到 std variant 但遇到了障碍 我在 boost type 中使用了一个很好的函数 它可以让你获取当前持有的 typeid 看https www boost org doc libs 1
  • 使用 Boost program_options 指定级别(例如 --verbose)

    我的一些选择有多个级别 例如的 冗长 我希望我的用户在以下两种等效样式之间进行选择 no argument verbosity of 1 my program v count the v s verbosity of 4 my progra
  • 我应该害怕使用 UDP 进行客户端/服务器广播通话吗?

    我在过去的两天里阅读了每一篇StackOverflow问题和答案 以及googling当然 关于印地TCP and UDP协议 以便决定在我的用户应用程序和 Windows 服务之间的通信方法中应该使用哪一种 从我目前所看到的来看 UDP是
  • 提升shared_from_this<>()

    有人可以用几句话概括一下如何提升shared from this lt gt 应该使用智能指针 特别是从使用绑定函数在 io service 中注册处理程序的角度来看 编辑 一些回复要求提供更多背景信息 基本上 我正在寻找 陷阱 即人们使用

随机推荐