rust学习基于tokio_actor聊天服务器实战(一 )

前言
tokio是Rust中使用最广泛的异步Runtime,它性能高、功能丰富、便于使用,是使用Rust实现高并发不可不学的一个框架
Actor 背后的基本思想是产生一个独立的任务,该任务独立于程序的其他部分执行某些工作。 通常,这些参与者通过使用消息传递信道与程序的其余部分进行通信。 由于每个 Actor 独立运行,因此使用它们设计的程序自然是并行的。 Actor 的一个常见用法是为 Actor 分配你要共享的某些资源的专有所有权,然后让其他任务通过与 Actor 通信来间接访问彼此的资源。 例如,如果要实现聊天服务器,则可以为每个连接生成一个任务,并在其他任务之间路由一个聊天消息的主任务。 十分有用,因为主任务可以避免必须处理网络IO,而连接任务可以专门处理网络IO;
为什么一定要用actor,这里只是仿照go项目里一部分,go 用的就是actor;

1:环境
rust1.75
ide rustrover64

2:设计及实现
这里使用类似单点登录模式,
useractor
先看go的
在这里插入图片描述
在这里插入图片描述
一共3个协程/future
接受网络消息 一个协程/future
发送网络消息 一个协程/future
逻辑处理 一个协程/future
协程/future间通信 直接用mpsc

world actor/accmgr 管理useractor 登录,踢人,广播等

一共1个协程/future 处理逻辑消息
在这里插入图片描述

rust 版
useractor
说明 receiver: mpsc::UnboundedReceiver, logic future 接受消息并处理
sendclient: mpsc::UnboundedSender 发送消息给 网络future 从而发送给前端
worldsender: mpsc::UnboundedSender, 跟world actor 通信接口

pub enum ActorMessage {synmsgwaitrep {//同步等待回复//需要发送到别处等到别处返回结果,类似于同步操作,只是异步执行的  //oneshot  spscrespond_to: crate::synMsgWaitRep, //同步消息},wtc_userchann {respond_to: crate::userChan_WTC, //},wtc_msg(sendMsgAndType),wtc_forwardmsg(sendMsgAndType), //直接转发 datactw_msg(sendMsgAndType),ctc_nettologic_msg(sendMsgAndType), //网络消息 to logicctc_logictonet_msg(sendMsgAndType), //logic to net  sendctc_signal_event(signalType),ctw_signal_event(signalType),wtc_signal_event(signalType),wtc_getChan_msg(userChannChann),
}
pub struct MyUserActor {connid: ConnectID,userid: UserID,username: String,guildid: GuildID,userstate: Arc<AtomicU8>,receiver: mpsc::UnboundedReceiver<ActorMessage>,sendclient: mpsc::UnboundedSender<VU8>,worldsender: mpsc::UnboundedSender<ActorMessage>,msgmask: u32,lasttime: [u32; ChatChannel_Num],
}

world actor
mpscrecv: mpsc::UnboundedReceiver, 接收ActorMessage logic future
chanchan: mpsc::UnboundedReceiver, 接受 ActorMessage2 logic future

pub enum ActorMessage2 {synmsgwaitrep {//同步等待回复//需要发送到别处等到别处返回结果,类似于同步操作,只是异步执行的  //oneshot  spscrespond_to: crate::synMsgWaitRep2, //同步消息},ctw_userhann {respond_to: crate::userChan_CTW, //同步消息},
}
pub struct userSendChanActorMessage {pub(crate) chanchan: Option<mpsc::UnboundedSender<ActorMessage>>,pub(crate) username: String,pub(crate) userguildid: GuildID,pub(crate) connectid: ConnectID,pub(crate) chanState: Arc<AtomicU8>, //user 状态
}pub struct worldActor {sharestate: Arc<AtomicU8>,mpscrecv: mpsc::UnboundedReceiver<ActorMessage>,chanchan: mpsc::UnboundedReceiver<ActorMessage2>,usermap: HashMap<UserID, userChan_world>,namemap: HashMap<String, UserID>,guildmap: HashMap<GuildID, HashSet<UserID>>,maxonlinerole: u32,
}async fn run(mut self) {// let logic_handle = self.handle_logic(recv);loop {tokio::select! {recvmsg= self.mpscrecv.recv()=> {if let Some(actmsg) = recvmsg {self.handle_logic(actmsg).await ;}}recvmsgchan= self.chanchan.recv()=>{if let Some(actmsg) = recvmsgchan {self.handle_logic2(actmsg).await ;}}_=tokio::time::sleep(Duration::from_millis(1000*8)) =>{}}} //end loop}

同步的方式的异步 go 很简单, rust go 上多一点点
go
在这里插入图片描述
rust
在这里插入图片描述

在这里插入图片描述

网络跟逻辑分开,这样 挤号,只需要把 logic future 里 sendclient mpsc 更新, 把网络 to logic mpsc 更新 及一些 状态重置下 即可,无需重新加载现有useractor 里的信息
类试单点登录 对于聊天服务器来说 ,只需要 角色进入后,由logic服 ase 对称加密(密钥及盐,logic 服 chat 服 共享/配置,共享方式自行决定)或 非对称(ECC) 等都可以,加密的token 由前端发送 给chat 服,chat 解密 得到 相应信息 并验证有效性 参考加解密验证用户的合法性

3:测试
前端简单用go 写了个

var origin = "http://192.168.1.32:8080"
var url = "wss://192.168.1.32:8080/websocket"
func GetProtoMsgID(data []byte) uint32 {var sMsgID uint16 = uint16(uint8(data[3] & 0x7f))if (uint8(data[3]) & 0x80) > 0 {sMsgID += (uint16(data[4]) & 0x7f) << 7}return uint32(sMsgID)
}func  sendMsg(ws *websocket.Conn,pb proto.Message) {if ws != nil {if data, err2 := proto.Marshal(pb); err2 != nil {log.Printf("SendMessage pb=%v err2=%v \n", pb, err2)} else {if err4 := websocket.Message.Send(ws, data); err4 != nil {log.Printf("send error =%v \n", err4)}}}
}func doLogicMsg(data []byte)  {msgId := GetProtoMsgID(data)fmt.Printf("msgid=%v",msgId)switch msgId {case uint32(chatproto.CHATMSG_CHC_Login_Rep):{loginReq := &chatproto.ChatMessageLoginRep{}if err := proto.Unmarshal(data, loginReq); err != nil {} else {fmt.Printf("CHATMSG_CHC_Login_Rep =%v \n",loginReq.Res)}}case uint32(chatproto.CHATMSG_CCH_Chat_Rep):{chatrep := &chatproto.ChatMessageChatRep{}if err := proto.Unmarshal(data, chatrep); err != nil {} else {fmt.Printf("CHATMSG_CCH_Chat_Rep =%v \n",chatrep.Res)}}case uint32(chatproto.CHATMSG_CHC_Notify_Chat):{chatmsg := &chatproto.ChatMessageNotifyChat{}if err := proto.Unmarshal(data, chatmsg); err != nil {} else {fmt.Printf("CHATMSG_CHC_Notify_Chat =%v fromuserid=%v text=%v \n",chatmsg.Chattype,chatmsg.Senderid,chatmsg.Strcontext)}}}}
func getTimestamp() uint32 {return  uint32(time.Now().UTC().Unix());
}func main(){//if os.Args[0]userid :=  getTimestamp()guildid := uint32(0)if len(os.Args) > 1 {if s,e := strconv.Atoi(os.Args[1]);e ==nil {userid = uint32(s)}}if len(os.Args) > 2 {if s,e := strconv.Atoi(os.Args[2]);e ==nil {guildid = uint32(s)}}ws, err := websocket.Dial(url, "", origin)if err != nil {log.Fatal(err)}fmt.Printf("userid=%v guild=%v \n",userid,guildid){msg := new(chatproto.ChatMessageLoginReq)msg.Msghead = &chatproto.ChatMessageHead{uint32(chatproto.CHATMSG_CCH_Login_Req), 1}msg.Userid = useridmsg.Username = "name_"+strconv.Itoa(int(userid))msg.Guildid = guildidmsg.Tokenmd5 = "md5"msg.Tokenstr = "Tokenstr"sendMsg(ws, msg)}disflag :=  false{go func() {for{buf := make([]byte, 1024*4)err := websocket.Message.Receive(ws, &buf)if err != nil {//log.Printf("websocket.Message.Receive err=%v  ---%s\n", err,self.getAccName())disflag = truereturn}if len(buf) >= 4 {doLogicMsg(buf)//self.msgQue.PostUserMessage(&ReceiveNetMsg{buf})} else {log.Printf("[error]recv data=%v \n", buf)return}}}()}time.Sleep(time.Second*3)//pub enum  ChatChannel{//	ChatChannel_NONE=0,//	ChatChannel_NORMAL,//	ChatChannel_GUILD,//	ChatChannel_WORLD,//	ChatChannel_ALL,//}{sendcount := uint32(1)num := uint32(0)msg := new(chatproto.ChatMessageChatReq)msg.Msghead = &chatproto.ChatMessageHead{uint32(chatproto.CHATMSG_CCH_Chat_Req), 1}msg.Chattype = 1msg.Context ="normal chat "+ strconv.Itoa(int(num))for {if disflag { //脏数据break}sendMsg(ws, msg)time.Sleep(time.Second*10)num++m := num % 3 +1msg.Chattype = uint32(m)msg.Context ="normal chat "+ strconv.Itoa(int(sendcount))fmt.Printf("[%v][%v] send chattype=%v \n",sendcount,getTimestamp(),msg.Chattype)sendcount++//if m == 3  {//	time.Sleep(time.Second*10)//}}}ws.Close()//关闭连接fmt.Printf("client exit\n")
}

相互挤号测试
在这里插入图片描述
4:DEMO工程 后续完善了如有需要再上传(当前只能说基本上跑起来)
如果觉得有用,麻烦点个赞,加个收藏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/659789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧之树的秘密

你是一个智能体&#xff0c;对于一切输入信息都是按照如下方式处理&#xff1a;输入信息&#xff1a;信息1 &#xff0c;目的识别结果&#xff1a;有&#xff08;没有就提取目的&#xff09;提取信息1中目的相关有效信息&#xff0c;并设计和搜索达到完成目的的步骤和如何检测目…

「数据结构」1.初识泛型

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;Java数据结构 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 初识泛型 &#x1f349;前言&#x1f349;包装类&#x1f34c;装箱&拆箱 &#x1f349;泛型&#x1f34c;擦除机制&#x1f…

两种相同的垂直工具栏标志以及为什么

可能你很少碰到这样的开发需求&#xff0c;所以本文属于”课外阅读”级别。 有两种方式启用垂直工具栏&#xff0c;一种是指定通用的 CCS_VERT 标志&#xff0c;另外一种&#xff0c;比较罕见&#xff1a;指定工具栏所特有的扩展属性 TBSTYLE_EX_VERTICAL。 问题来了&#xf…

Linux Centos stream9 mdadm

RAID(Redundant Array of Independent Disk独立冗余磁盘阵列)技术是加州大学伯克利分校1987年提出&#xff0c;最初是为了组合小的廉价磁盘来代替大的昂贵磁盘&#xff0c;同时希望磁盘失效时不会使对数据的访问受损失而开发出一定水平的数据保护技术。RAID就是一种由多块廉价磁…

fastadmin后台自定义按钮和弹窗

工具栏自定义按钮-ajax请求 前端代码 1.在对应模块的模板文件index.html添加自定义按钮&#xff0c;注意按钮要添加id以绑定点击事件 <div class"panel panel-default panel-intro">{:build_heading()}<div class"panel-body"><div id&qu…

pysyft框架中WebsocketClientWorker与WebsocketServerWorker的消息传输

引言 pysyft是基于pytorch的一个联邦学习框架&#xff08;虽然用起来很难受&#xff09;&#xff0c;通过内存管理实现联邦学习的模拟。 在pysyft中&#xff0c;WebsocketServerWorker充当数据的提供方&#xff08;数据存储方&#xff09;&#xff0c;而WebsocketClientWorker…

专业的韩语导游翻译需要具备哪些能力

近年来&#xff0c;随着中韩关系的友好发展&#xff0c;两国之间的旅游交流呈现出爆发式的增长。这一趋势不仅深化了中韩民众之间的交流与理解&#xff0c;也对韩语导游翻译人才的需求产生了显著的影响。那么&#xff0c;为了做好韩语翻译工作&#xff0c;我们需要具备哪些专业…

页面通过Vue进行整体页面不同语言切换 i18n库

目录 引入 如何做到 下载i18n库 构建整体翻译文件结构 语言包文件 i18n配置文件 把i18n挂载到vue实例上 添加按钮点击事件切换语言 引入 我们现在有这样一个要求,我们想要对我们开发的网页进行国际化操作,也就是我们不仅要有中文,还要有英文等。用户可以随时进行不同语言…

代码随想录day16--二叉树的应用4

LeetCode513.找树左下角的值 题目描述&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1示例 2: 输入: [1,2,3,4,null,5,6,null,null,7] 输出: 7 解题思…

C++ //练习 3.21 请使用迭代器重做3.3.3节(第94页)的第一个练习。

C Primer&#xff08;第5版&#xff09; 练习 3.21 练习 3.21 请使用迭代器重做3.3.3节&#xff08;第94页&#xff09;的第一个练习。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*************************************…

【重磅】中国隐私计算平台市场,摩斯第一

摩斯市场份额遥遥领先 10月11日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布了《中国隐私计算平台厂商市场份额&#xff0c;2022》报告。蚂蚁集团凭借商用隐私计算平台摩斯&#xff08;MORSE&#xff09;&#xff0c;以 36.9%的市场份额排名第一。…

Git系列---远程操作

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 引用 1.理解分布式版本控制…

java+springboot校园体育场地预约预订使用系统vue+ssm

研究内容和研究方法 1.研究内容 网站主要包括管理员和用户两个部分&#xff0c;用户可以登录与注册自己的基本信息、查询哪些场地可以使用、提前预约场地、取消预约的场地、使用完场地后进行缴费。管理员可以审批用户的注册信息、对用户信息进行增删改查、查询场地的使用情况、…

JUnit

前言&#xff1a;自动化就是selenium脚本来实现的&#xff0c;JUnit是java的单元测试工具&#xff0c;只不过我们在实现自动化的时候需要借助一下JUnit库里面提供的一些方法。 1、Test Test &#xff1a;表示方法是测试方法&#xff0c;执行当前这个类的时候&#xff0c;会自动…

微服务知识

1、概念 大型单体应用拆分成多个独立部署运行的微服务&#xff08;解决并发问题&#xff09;​​​​​​​ 2、特点 3、技术栈 4、微服务带来的问题 ​​​​​​​ 5、微服务的注册中心 服务注册与发现&#xff1a;微服务实例在启动时会向注册中心注册自己的信息&#xf…

Centos慢慢长大(一)

1、写在前面 这将是一个系列性的文章。可能更多的是记录我在学习的过程中的一些感悟吧。我想强调的是在这一系列文章里我会从最小化的安装开始&#xff0c;然后逐渐的增加需要安装的软件。就象一个婴儿的诞生&#xff0c;慢慢的学走路、学说话、学使用筷子。。。。。。 这将是一…

nginx反向代理-负载均衡

nginx环境搭建 wget https://nginx.org/download/nginx-1.21.6.tar.gz&#xff08;下载nginx安装包&#xff09; tar -xvzf nginx-1.21.6.tar.gz&#xff08;解压缩&#xff09; yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel&#xff08;下载依赖库和…

Wireshark网络协议分析 - UDP协议

在我的博客阅读本文 文章目录 1. 基础2. 实战2.1. 用Go写一个简单的UDP服务器与客户端2.2. Wireshark抓包分析 3. UDP与TCP的区别4. 参考资料 1. 基础 UDP包的数据结构&#xff1a; 2. 实战 2.1. 用Go写一个简单的UDP服务器与客户端 我们这里使用Golang写了一个简单的9830端…

强化学习原理python篇07——策略梯度法

强化学习原理python篇07——策略梯度法 Average state valueAverage rewardMonte Carlo policy gradient (REINFORCE)REINFORCE示例在torch里面编写这段代码1、用随机权重初始化策略网络2、运行N个完整的片段&#xff0c;保存其(s,a,r,s)状态转移3、对于每个片段k的每一步t&…

RocketMQ—RocketMQ安装

RocketMQ—RocketMQ安装 在安装RocketMQ之前需要先安装Jdk&#xff0c;并配置JAVA_HOME环境变量。 在安装之前我们要先下载RocketMQ的安装包。 下载 到RocketMQ的官网https://rocketmq.apache.org/ 选择Download。 本文使用4.9.2版本。 将下载页面拉到最后面&#xff0c;有…