c++异网连续异步发送数据

原文
asio的异步接口能连续调用吗?比如可以这样调用异步写接口吗?

向量<串>消息{"你好","发送许多"};
对(动&消息:消息){
    异网::异步写(套接字_,
    异网::缓冲(消息.数据(),消息.长度()),
    [本](错误码 ec,大小型 长度){
        如(ec)关闭();
    })
}

asio不允许连续调用异步接口,必须在上次调用完成之后才能再调用异步接口.
如何连续发送消息呢?
一个简单思路是client内部搞一个发送队列,用户连续发送数据丢到队列中,然后再异步发送队列中的数据.chat_client的例子中展示了如何使用队列实现连续发送消息:

空 写(常 串&消息)
  {
      极 正在写=!写消息_.空的();
      写消息_.压后(消息);
      如(!正在写)
      {
          干写();
      }
  }

  空 干写()
  {
    异网::异步写(套接字_,
      异网::缓冲(写消息_.前().数据(),
        写消息_.前().长度()),
      [本](错误码 ec,大小型/*长度*/)
      {
        如(!ec)
        {
          写消息_.弹前();
          如(!写消息_.空的())
          {
            干写();
          }
        }
        异
        {
          套接字_.关闭();
        }
      });
  }

  双队<串>写消息_;

干写展示了异步调用链,每次异步写时从队列头拿数据去发送,发送完成回调里移除头部已发送完成数据,接着再检查队列中是否还有数据,如果有就继续调用异步发送接口发送,直到队列为空.

这里一个小问题需要注意,队列中有很多数据时,正在递归异步发送数据时,如何保证外部用户不会调用异步写接口呢?
外面用户不知道是否已发送完成内部队列数据,如果这时用户在外面调用了异步写接口,那就相当于连续调用了异步接口,产生未定义行为(两次缓冲区交叠).这时应在用户发送接口中判断队列是否为空,如果不为空就放到队列中并返回,如果队列为空,就调用异步发送数据的接口.所以队列是否为空可判断能否调用异步写接口.

这个write_msgs_,还有线程安全问题.因为用户线程和io_context线程都可能访问这个write_msgs_.还要修改一下来保证线程安全.


#包含<io流>
#包含<异网.h++>
#包含<双队>
#包含<串>
#包含<向量>
#包含<互斥锁>

用 异网::ip::传控;

类 聊天客户
{
公:
  聊天客户(异网::io环境&io环境,
    常 传控::解析器::结果类型&端点)
    :io环境_(io环境),
    套接字_(io环境)
  {
    干连接(端点);
  }

  空 写(常 串&消息)
  {
      独锁 锁(写互斥锁_);
      极 正在写=!写消息_.空的();
      写消息_.压后(消息);
      如(!正在写)
      {
          干写();
      }
  }

  空 关闭()
  {
    异网::提交(io环境_,[本](){套接字_.关闭();});
  }

  空 跑一(){
      io环境_.跑一();
  }

私:
  空 干连接(常 传控::解析器::结果类型&端点)
  {
    异网::异步连接(套接字_,端点,[本](错误码 ec,传控::端点){
        如(!ec)干读();
      });
  }

  空 干读()
  {
    套接字_.异步读些(异网::缓冲(读消息_,512),
      [本](错误码 ec,大小型 长度)
      {
        如(!ec)
        {
          输出<<"接收消息:"<<串(读消息_,长度)<<"\n";
          干读();
        }
        异
        {
          套接字_.关闭();
        }
      });
  }

  空 干写()
  {
    异网::异步写(套接字_,
      异网::缓冲(写消息_.前().数据(),
        写消息_.前().长度()),
      [本](错误码 ec,大小型/*长度*/)
      {
        如(!ec)
        {
          独锁 锁(写互斥锁_);
          写消息_.弹前();
          如(!写消息_.空的())
          {
            干写();
          }
        }
        异
        {
          套接字_.关闭();
        }
      });
  }

私:
  异网::io环境&io环境_;
  传控::套接字 套接字_;
  符 读消息_[512];
  双队<串>写消息_;
  互斥锁 写互斥锁_;//用互斥锁来锁定.
};

整 主(整 参个数,符*参值[])
{
    试
    {
        如(参个数!=3)
        {
            c错误<<"使用:聊天客户<主机><端口>\n";
            中 1;
        }

        异网::io环境 io环境;

        传控::解析器 解析器(io环境);
        动 端点=解析器.解析(参值[1],参值[2]);
        聊天客户 c(io环境,端点);
        c.跑一();

        线程 t([&io环境](){io环境.跑();});

        向量<串>向量{"你好","发送许多"};
        对(动&消息:向量){
            c.写(消息);
        }

        t.合并();
        io环境.跑();
    }
    抓(异常&e)
    {
        c错误<<"异常:"<<e.什么()<<"\n";
    }

    中 0;
}

asio提供了strand,strand会保证并发调用异步接口的安全性,用strand就不用锁了,代码会更简单.

类 提交客户
{
公:
    提交客户(异网::io环境&io环境,
        常 传控::解析器::结果类型&端点)
        :io环境_(io环境),
        套接字_(io环境)
    {
        干连接(端点);
    }

    空 写(串 消息)
    {
        异网::提交(io环境_,
          [本,消息=移动(消息)]()
          {
            极 正在写=!写消息_.空的();
            写消息_.压后(移动(消息));
            如(!正在写)
            {
              干写();
            }
          });
    }

    空 关闭()
    {
        异网::提交(io环境_,[本](){套接字_.关闭();});
    }

    空 跑一(){
        io环境_.跑一();
    }

私:
    空 干连接(常 传控::解析器::结果类型&端点)
    {
        异网::异步连接(套接字_,端点,
            [本](错误码 ec,传控::端点)
            {
                如(!ec)
                {
                    输出<<"连接好\n";
                    干读();
                }
            });
    }

    空 干读()
    {
        套接字_.异步读些(异网::缓冲(读消息_,512),
            [本](错误码 ec,大小型 长度)
            {
                如(!ec)
                {
                    输出<<"接收消息:"<<串(读消息_,长度)<<"\n";
                    干读();
                }
                异
                {
                    关闭();
                }
            });
    }

    空 干写()
    {
        异网::异步写(套接字_,
            异网::缓冲(写消息_.前().数据(),
                写消息_.前().长度()),
            [本](错误码 ec,大小型/*长度*/)
            {
                如(!ec)
                {
                    写消息_.弹前();
                    如(!写消息_.空的())
                    {
                        干写();
                    }
                }
                异
                {
                    关闭();
                }
            });
    }

私:
    空 关闭()
    {
        异网::提交(io环境_,[本]{
            如(!套接字_.是打开()){
                中;
            }
            异网::错误码 忽略误码;
            套接字_.关闭(传控::套接字::都关闭,忽略误码);
            套接字_.关闭(忽略误码);
            });
    }
    异网::io环境&io环境_;
    传控::套接字 套接字_;
    符 读消息_[512];
    双队<串>写消息_;
};

posted @   zjh6  阅读(23)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示