前言:
上章以前实现了一个基本的服务器,这章主要是完善功能
1: 介绍
useractor worldactor 等分开
use crate::gobal::*;
use crate::pb::chatproto;
use crate::pb::chatproto::*;
use prost::Message as ProstMssage;
use std::error::Error;
use std::sync::atomic::{
AtomicU8, Ordering};
use std::sync::Arc;
use tokio::sync::{
mpsc, oneshot};
pub struct MyUserActor {
pub(crate) connid: ConnectID,
pub(crate) userid: UserID,
pub(crate) username: String,
pub(crate) guildid: GuildID,
pub(crate) userstate: Arc<AtomicU8>,
pub(crate) receiver: mpsc::UnboundedReceiver<ActorMessage>,
pub(crate) sendclient: mpsc::UnboundedSender<SendClientMsg>,
pub(crate) worldsender: mpsc::UnboundedSender<ActorMessage>,
pub(crate) msgmask: u32,
pub(crate) lasttime: [u32; ChatChannel_Num],
}
impl MyUserActor {
//,worldsender:mpsc::UnboundedSender<ActorMessage>
pub(crate) fn new(
connid: ConnectID,
receiver: mpsc::UnboundedReceiver<ActorMessage>,
sendclient: mpsc::UnboundedSender<SendClientMsg>,
worldsender: mpsc::UnboundedSender<ActorMessage>,
) -> Self {
Self {
connid,
userid: 0,
username: "".to_string(),
guildid: 0,
userstate: Arc::new(AtomicU8::new(0)),
receiver,
sendclient,
worldsender,
msgmask: MASK_START,
lasttime: [0; ChatChannel_Num],
// worldsender,
}
}
pub(crate) fn setuserinfo(
&mut self,
userid: UserID,
username: String,
guildid: GuildID,
state: u8,
) {
self.userid = userid;
self.userstate.store(state, Ordering::Relaxed);
self.username = username;
self.guildid = guildid;
}
pub(crate) fn setguildid(&mut self, guildid: GuildID) {
self.guildid = guildid;
}
pub(crate) fn getguildid(&mut self) -> GuildID {
self.guildid
}
pub(crate) fn getuserstateclone(&mut self) -> Arc<AtomicU8> {
self.userstate.clone()
}
pub(crate) fn setuserstate(&mut self, state: u8) {
self.userstate.store(state, Ordering::Relaxed);
}
pub(crate) fn getuserstate(&mut self) -> u8 {
self.userstate.load(Ordering::Relaxed)
}
pub(crate) fn doLogicMsg(&mut self, datatype: u32, msgdata: VU8) {
if datatype == MyMsgType::Binary as u32 {
let (msgid, mask) = getProtoMsgIdAndMask(&msgdata);
const chat_req: u32 = chatproto::Chatmsg::CchChatReq as u32;
match msgid {
chat_req => {
if let Ok(req) = chatproto::ChatMessageChatReq::decode(&msgdata[..]) {
// println!(
// "user={} msgid={} chatchannel={}",
// self.userid.clone(),
// msgid.clone(),
// req.chattype.clone()
// );
if req.chattype == ChatChannel::ChatChannel_GUILD as u32 && self.guildid > 0 {
let head = chatproto::ChatMessageHeadNoMask {
msgid: Chatmsg::ChcNotifyChat as u32,
};
let mut chatreq = chatproto::ChatMessageNotifyChat {
msghead: Some(head),
chattype: req.chattype,
senderid: self.userid.clone(),
sendername: self.username.clone(),
strcontext: req.context,
};
let mut buf = Vec::new();
chatreq.encode(&mut buf).unwrap();
// buf.insert(0, MyMsgType::Binary as u8);
let msg = sendMsgAndType {
fromid: self.guildid.clone(),
datatype: req.chattype.clone(),
data: buf,
};
self.worldsender.send(ActorMessage::ctw_msg(msg)).unwrap();
// println!("guild msg to world ");
} else if req.chattype == ChatChannel::ChatChannel_WORLD as u32 {
let head = chatproto::ChatMessageHeadNoMask {
msgid: Chatmsg::ChcNotifyChat as u32,
};
let mut chatreq = chatproto::ChatMessageNotifyChat {
msghead: Some(head),
chattype: req.chattype,
senderid: self.userid.clone(),
sendername: self.username.clone(),
strcontext: req.context,
};
let mut buf = Vec::new();
chatreq.encode(&mut buf).unwrap();
// buf.insert(0, MyMsgType::Binary as u8);
let msg = sendMsgAndType {
fromid: 0,
datatype: req.chattype.clone(),
data: buf,
};
self.worldsender.send(ActorMessage::ctw_msg(msg)).unwrap();
} else if req.chattype == ChatChannel::ChatChannel_NORMAL as u32 {
//对个人或朋友
let head = chatproto::ChatMessageHeadNoMask {
msgid: Chatmsg::CchChatRep as u32,
};
let mut chatreq = chatproto::ChatMessageChatRep {
msghead: Some(head),
res: 0,
};
let mut buf = Vec::new();
chatreq.encode(&mut buf).unwrap();
// buf.insert(0, MyMsgType::Binary as u8);
self.sendclient
.send(SendClientMsg {
msgypte: MyMsgType::Binary as u8,
msgdata: buf,
})
.unwrap();
}
}
}
_ => {
}
}
}
}
pub(crate) async fn handle_message(&mut self, msg: ActorMessage) -> Result<Vec<u8>, Box<dyn Error>> {
// println!("handle_message ActorMessage ");
match msg {
ActorMessage::synmsgwaitrep {
respond_to } => {
// self.next_id += 1;
// The `let _ =` ignores any errors when sending.
// `let _ =` 忽略了发送的任何 error
// This can happen if the `select!` macro is used
// to cancel waiting for the response.
// 当 `select!` 宏被用到时将会停止接受响应
// let _ = respond_to.send(vec![self.next_id as u8]);
}
ActorMessage::ctc_signal_event(signalmsg) => {
//通知world actor 客户端可以清理了
let _ = self
.worldsender
.send(ActorMessage::ctw_signal_event(signalType {
signaltype: signalTypeId::Signal_Kick_User as u32,
signalparam: self.userid.clone(),
}));
self.userstate.store(UserState_NONE,Ordering::Relaxed);
}
ActorMessage::wtc_forwardmsg(mut msg) => {
// msg.data.insert(0, MyMsgType::Binary as u8);
//let _ = self.sendclient.send(msg.data);
// println!("wtc_forwardmsg to client {} {} {} {} {}",msg.data[0],msg.data[1],msg.data[2],msg.data[3],msg.data[4]);
let _ = self.sendclient.send(SendClientMsg {
msgypte: msg.datatype as u8,
msgdata: msg.data,
});
}
ActorMessage::ctc_nettologic_msg(msg) =>{
self.doLogicMsg(msg.datatype, msg.data);
}
ActorMessage::wtc_signal_event(msg) => {
match msg.signaltype {
signalTypeId_Kick_User => {
//let _ =self.sendclient.send(SendClientMsg{msgypte:0,msgdata:vec![]});
self.notify_network_disconnect().await;
self.userstate.store(UserState_NONE, Ordering::Relaxed);
//修改为0
}
_ => {
}
}
}
ActorMessage::wtc_getChan_msg(msg) => {
//更新连接ID 及 网络发送通道
//有些数据需要重置
//原来的网络连接需要断开 //可以提示 你的账号在别的设备上登录
self.notify_client_msg(1,"你的账号在别的设备上登录".to_string()).await;
self.notify_network_disconnect().await;
self.connid = msg.connid;
self.sendclient = msg.chan;
}
_ => {
return Err("".into());
}
}
Ok(vec![])
}
async fn notify_network_disconnect(&mut self) {
let _ = self.sendclient.send(SendClientMsg {
msgypte: 0,
msgdata: vec![],
});
}
async fn notify_client_msg(&mut self,msgtype:u32,context:String ) {
let head = chatproto::ChatMessageHeadNoMask {
msgid: Chatmsg::ChcNotifyMsg as u32,
};
let mut chatnotifymsg = chatproto::ChatMessageNotifyMsg {
msghead: Some(head),
msgtype,
strcontext:context,
};
let mut buf = Vec::new();
chatnotifymsg.encode(&mut buf).unwrap();
let _ = self.sendclient.send(SendClientMsg {
msgypte: MyMsgType::Binary as u8,
msgdata: buf,
});
}
}
worldActor
use crate::gobal::*;
use crate::pb::chatproto::*;
use prost::Message as ProstMssage;
use std::collections::{
HashMap, HashSet};
use std::sync::atomic::{
AtomicU8, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{
mpsc, oneshot};
use tokio::{
select, signal, time};
pub struct worldActor {
pub(crate) sharestate: Arc<AtomicU8>,
pub(crate) mpscrecv: mpsc::UnboundedReceiver<ActorMessage>,
pub(crate) chanchan: mpsc::UnboundedReceiver<ActorMessage2>,
pub(crate) usermap: HashMap<UserID, userChan_world>,
pub(crate) namemap: HashMap<String, UserID>,
pub(crate) guildmap: HashMap<GuildID, HashSet<UserID>>, //Rc<RefCell<userChan_world>>>>
pub(crate) maxonlinerole: u32,
}
impl worldActor {
pub(crate) fn new(
sharestate: Arc<AtomicU8>,
receiver: mpsc::UnboundedReceiver<ActorMessage>,
chanchan: mpsc::UnboundedReceiver<ActorMessage2>,
) -> Self {
Self {
sharestate,
mpscrecv: receiver,
chanchan,
usermap: HashMap::new(),
namemap: HashMap::new(),
guildmap: HashMap::new(),
maxonlinerole: MAX_ONLINE_ROLE, //最大人数
}
}
pub(crate) fn kickalluser(&mut self) {
for (k, v) in &self.usermap {
if v.chanState.load(Ordering::Relaxed) == UserState::UserState_NORMAL as u8 {
let _ = v.chanchan.send(ActorMessage::wtc_signal_event(signalType {
signaltype: signalTypeId_Kick_User,
signalparam: 0,
}));
}
}
self.sharestate.store(0, Ordering::Relaxed);
self.usermap.clear();
self.namemap.clear();
self.guildmap.clear();
}
pub(crate) fn removeuser(&mut self, userid: UserID) {
if let Some(v) = self.usermap.remove(&userid) {
self.namemap.remove(&v.username);
let guildid = v.userguildid.clone();
if guildid > 0 {
if let Some(v1) = self.guildmap.get_mut(&guildid) {
v1.remove(&userid);
}
}
}
}
pub(crate) async fn guildboradcast(
&mut self,
formuserid: UserID,
toguildid: GuildID,
msg: VU8,
) {
if let Some(v1) = self.guildmap.get_mut(&toguildid) {
let sendmsg = crate::gobal::sendMsgAndType {
fromid: formuserid,
datatype: MyMsgType::Binary as u32,
data: msg,
};
let mut sendcount = 0;
let userstate = UserState::UserState_NORMAL as u8;
for key in v1.iter() {
if let Some(v2) = self.usermap.get(&key) {
if v2.chanState.load(Ordering::Relaxed) == userstate {
sendcount += 1;
v2.chanchan
.send(ActorMessage::wtc_forwardmsg(sendmsg.clone()))
.unwrap();
}
}
}
// println!(" world guild msg to role {}", sendcount);
} else {
println!("no guild {} \n", toguildid);
}
}
pub(crate) async fn worldboradcast(&mut self, formuserid: UserID, msg: VU8) {
let userstate = UserState::UserState_NORMAL as u8;
let sendmsg = crate::gobal::sendMsgAndType {
fromid: formuserid,
datatype: MyMsgType::Binary as u32,
data: msg,
};
for (_, v) in &self.usermap {
if let Some(v2) = self.usermap.get(&v.userid) {
if v2.chanState.load(Ordering::Relaxed) == userstate {
let _ = v2
.chanchan
.send(ActorMessage::wtc_forwardmsg(sendmsg.clone()));
}
}
}
}
pub(crate) async fn handle_logic(&mut self, msg: ActorMessage) {
match msg {
ActorMessage::ctw_signal_event(signalmsg) => match signalmsg.signaltype {
signalTypeId_Server_Close => {
}
signalTypeId_Kick_User => {
//self.usermap.remove(&(signalmsg.signalparam as UserID));
self.removeuser(signalmsg.signalparam as UserID);
}
_ => {
}
},
ActorMessage::ctw_msg(mut msg) => {
if msg.datatype == ChatChannel::ChatChannel_GUILD as u32 {
// println!("world recv guild msg");
self.guildboradcast(0, msg.fromid, msg.data).await;
} else if msg.datatype == ChatChannel::ChatChannel_WORLD as u32 {
self.worldboradcast(0, msg.data).await;
}
}
_ => {
}
}
}
pub(crate) async fn handle_logic2(&mut self, msg: ActorMessage2) {
match msg {
ActorMessage2::synmsgwaitrep {
respond_to } => {
if let Some(t) = self.usermap.get(&respond_to.id) {
let userchanworld = &t;
let userstate = userchanworld.chanState.load(Ordering::Relaxed);
if userstate == UserState::UserState_NORMAL as u8 {
//
let _ = respond_to.msgChann.send(synWaitRep2 {
state: 0,
op: Some(repChannAndState2 {
actorState: userchanworld.chanState.clone(),
sendchann: userchanworld.chanchan.clone(),
}),
});
// t.logicchann.send();
//t.logicChannChann.send(userChannChann{connid:respond_to.id2 ,chan:respond_to.tonetChann});
//chanchan.send(userChannChann{connid:respond_to.id2 ,chan:respond_to.tonetChann});
let _ = userchanworld.chanchan.send(ActorMessage::wtc_getChan_msg(
userChannChann {
connid: respond_to.id2,
chan: respond_to.tonetChann,
},
));
} else if userstate == UserState::UserState_INIT as u8 {
// msg.MsgChan <- &shr.UpdateUserWebQue{nil, true, 0}
let _ = respond_to.msgChann.send(synWaitRep2 {
state: 1, op: None });
} else {
let _ = respond_to.msgChann.send(synWaitRep2 {
state: 0, op: None });
}
} else {
let usercount = self.usermap.len();
if usercount >= self.maxonlinerole as usize {
//是否超过最大人数了
let _ = respond_to.msgChann.send(synWaitRep2 {
state: 2, op: None });
} else {
let _ = respond_to.msgChann.send(synWaitRep2 {
state: 0, op: None });
}
}
}
ActorMessage2::ctw_userhann {
respond_to } => {
let userid = respond_to.userid;
let mut userid_2 = userid.clone();
if let Some(t) = self.usermap.remove(&userid_2) {
let _ = t.chanchan.send(ActorMessage::wtc_signal_event(signalType {
signaltype: signalTypeId::Signal_Kick_User as u32,
signalparam: respond_to.userid.clone(),
}));
}
{
// if let Some(t) = self.usermap.get(& userid) {
// self.usermap.remove(&userid_2);
// t.chanchan.send(ActorMessage::wtc_signal_event(signalType{signaltype:signalTypeId::Signal_Kick_User as u32,signalparam:userid_2}));
// }
}
let guildid = respond_to.userguildid.clone();
let u = userChan_world {
userid: respond_to.userid,
username: respond_to.username,
userguildid: respond_to.userguildid,
connectid: respond_to.connectid,
chanchan: respond_to.logicChann,
chanState: respond_to.chanState,
};
self.usermap.insert(respond_to.userid, u.clone());
if guildid > 0 {
if let Some(vg) = self.guildmap.get_mut(&guildid) {
vg.insert(userid);
} else {
let mut set = HashSet::new();
set.insert(userid);
self.guildmap.insert(guildid, set);
}
}
}
_ => {
}
}
}
//mut sendnetmsg:mpsc::UnboundedSender<VU8>
pub(crate) 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(3_000)) =>{
// if self.sharestate.load(Ordering::Relaxed) == 0 {
// break ;
// }
// }
}
} //end loop
}
}
main
use std::sync::atomic::AtomicU8;
mod gobal;
use gobal::*;
use std::any::Any;
use std::cell::RefCell;
use std::collections::{
HashMap, HashSet};
use std::error::Error;
use std::fmt::Debug;
use std::fs::copy;
use std::{
fs::File,
io::{
self, BufReader},
net::SocketAddr,
sync::Arc,
};
use std::io::{
ErrorKind, Read};
use std::pin::Pin;
use std::sync::atomic::{
AtomicU32, AtomicUsize, Ordering};
use std::time::Duration;
use tokio::sync::{
mpsc, oneshot};
use futures_util::{
FutureExt, SinkExt, StreamExt, TryStreamExt};
use rustls_pemfile::{
certs, pkcs8_private_keys};
use rustls_pki_types::{
CertificateDer, PrivateKeyDer};
use tokio::io::{
AsyncReadExt, AsyncWrite, AsyncWriteExt};
use tokio::net::{
TcpListener, TcpStream};
use tokio::{
select, signal, time};
use tokio_rustls::{
rustls, server, TlsAcceptor, TlsStream};
use tokio_websockets::{
proto, Config, Limits, Message, Payload, WebSocketStream};
mod aes_fun;
mod gobalfun;
mod json_config;
mod pb;
mod useractor;
mod worldactor;
use crate::gobalfun::*;
use crate::pb::chatproto::Chatmsg;
use crate::useractor::MyUserActor;
use crate::worldactor::worldActor;
use bytes::Bytes;
use pb::chatproto;
use prost::Message as ProstMssage;
use tokio::sync::mpsc::UnboundedSender;
use crypto;
use crypto::aes::KeySize;
use crypto::digest::Digest;
use serde_json;
use serde_json::Value as json_value;
//use serde_json::Value::{String as json_String};
//use crate::json_config::u8_aes_128_len;
use env_logger::{
Builder, Target};
use log::{
debug, error, info, log_enabled, Level, LevelFilter};
use std::env;
//use tracing_subscriber::{filter, prelude::*};
//https://blog.csdn.net/BBinChina/article/details/119520531
// const PATH_TO_CERT: &str = "certs/ca.crt"; //"certs/localhost.crt"; //ca.crt //server.csr
// const PATH_TO_KEY: &str = "certs/server.key"; //"certs/localhost.key";
const PATH_TO_JSON: &str = "chatserver.json"; //配置文件
fn load_certs(path: &str) -> io::Result<Vec<CertificateDer<'static>>> {
if let Ok(f) = File::open(path) {
certs(&mut BufReader::new(f)).collect()
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "file no exist"))
}
// certs(&mut BufReader::new(File::open(path)?)).collect()
}
fn load_key(path: &str) -> io::Result<PrivateKeyDer<'static>> {
if let Ok(f) = File::open(path) {
pkcs8_private_keys(&mut BufReader::new(f))
.next()
.unwrap()
.map(Into::into)
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "file no exist"))
}
// pkcs8_private_keys(&mut BufReader::new(File::open(path)?))
// .next()
// .unwrap()
// .map(Into::into)
}
static NEXT_CONNECT_ID: AtomicU32 = AtomicU32::new(1);
fn init_log() {
use chrono::Local;
use std::io::Write;
//env_logger 库不合适要写入文件的日志,不能直接输出到文件和日志轮换(rotating)
let env = env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "trace");
let mut builder = env_logger::Builder::from_env(env);
builder.format(|buf, record| {
writeln!(
buf,
"{} {} [{}:{}:{}]",
Local::now().format("%Y-%m-%d %H:%M:%S"),
record.level(),
record.module_path().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
});
builder.target(Target::Stdout);
builder.filter_level(LevelFilter::Warn);
builder.init();
info!("env_logger initialized.");
}
#[cfg(unix)]
async fn sigal_ctrl() -> io::Result<()> {
if let (mut stream) = signal(SignalKind::hangup()) {
stream.recv().await;
}
Ok(())
}
#[cfg(not(unix))]
async fn sigal_ctrl() -> io::Result<()> {
signal::ctrl_c().await
}
//#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
//#[tokio::main(flavor = "current_thread")]
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() -> Result<(), Box<dyn Error>> {
//std::error::
// let mut builder = Builder::from_default_env();
// builder.target(Target::Stdout);
// console_subscriber::ConsoleLayer::builder()
// // set how long the console will retain data from completed tasks
// .retention(Duration::from_secs(60))
// // set the address the server is bound to
// .server_addr(([127, 0, 0, 1], 5555))
// // ... other configurations ...
// .init();
console_subscriber::init();
//日志///
init_log();
// let mut builder = Builder::from_default_env();
// builder.target(Target::Stdout);
// builder.filter_level(LevelFilter::Warn);
// builder.init();
//
// debug!("this is a debug {}", "message");
// error!("this is printed by default");
// info!("this is infomsg");
//env_logger::init();
/
let chat_cfg = loadchatcfg(PATH_TO_JSON).await?;
//return Err(Box::try_from(io::Error::new(io::ErrorKind::Other, "something went wrong")).unwrap()); //Err("read json file error");
let addr = SocketAddr::from(([0, 0, 0, 0], chat_cfg.nlistenport as u16)); //default 8080
let certs = load_certs(chat_cfg.szcacrtfile.as_str())?; //load_certs(PATH_TO_CERT)?;
let key = load_key(chat_cfg.szprivatekeyfile.as_str())?; //load_key(PATH_TO_KEY)?;
let config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
let acceptor = TlsAcceptor::from(Arc::new(config));
let listener = TcpListener::bind(&addr).await?;
//let world_mgr = MyWorldHandle::new();
let (world_sender, receiver) = mpsc::unbounded_channel();
let (world_chan_sender, world_chan_receiver) = mpsc::unbounded_channel();
let world = worldActor::new(Arc::new(AtomicU8::new(1)), receiver, world_chan_receiver);
let world_handle = tokio::spawn(world.run());
let (aes_key, aes_iv) = (chat_cfg.u8AES128keyhandshake, chat_cfg.u8AES128iv);
println!("server running {}", timestamp_seconds());
loop {
select! {
accept_result = listener.accept() => {
let (mut stream2,addr) = accept_result?;
let acceptor = acceptor.clone();
println!("accept tcp connect {}",timestamp_seconds());
///
let world_chan_sender_1 = world_chan_sender.clone();
let world_clone_1 = world_sender.clone();
tokio::spawn(tls_accept(stream2,acceptor,world_chan_sender_1,world_clone_1,addr.clone(),aes_key.clone(),aes_iv.clone())) ;
},
// 接收Ctrl+c SIGINT
// _ = signal::ctrl_c() => {
// //info!("KV Server is shutting down!!!");
// world_sender.send(ActorMessage::ctw_signal_event(signalType{signaltype:signalTypeId::Signal_Server_Close as u32,signalparam:0}));
// break ;
// }
_= sigal_ctrl()=>{
world_sender.send(ActorMessage::ctw_signal_event(signalType {
signaltype: signalTypeId::Signal_Server_Close as u32, signalparam: 0 }));
break ;
}
} //end select!
} //end loop
// 等待服务器关闭
world_handle.await.unwrap();
println!("server exit {}", timestamp_seconds());
Ok(())
}
//async fn readjsonfile(path:&str)->Option<json_value>{
async fn readjsonfile(path: &str) -> Result<json_value, Box<dyn Error>> {
let mut f = File::open(path).expect("open file fail");
let mut data = vec![];
let s = f.read_to_end(&mut data).expect("read file fail");
if s < 10 {
//return Err(std::io::Error::other("data short"));
// Err("".into());
return Err(
Box::try_from(io::Error::new(io::ErrorKind::Other, "something went wrong")).unwrap(),
);
}
let string = std::string::String::from_utf8_lossy(&data);
Ok(serde_json::from_str(string.as_ref())?)
// if let Ok( mut f)= File::open(path) {
// let mut data = vec![];
// if let Ok(s) = f.read_to_end(&mut data) {
// if s < 10 {
// return None;
// }
// let string = String::from_utf8_lossy(&data); // 将字节向量转换为字符串
// if let Ok(v) = serde_json::from_str(string.as_ref()){
// return Some(v);
// }
// }
//
// }else{
// println!(" read file {} fail",path);
// }
// None
}
async fn stringtrimchar(string: std::string::String) -> std::string::String {
"".to_string()
}
async fn loadchatcfg(path: &str) -> Result<json_config::chatservercfg, Box<dyn Error>> {
let chat_v: json_value = readjsonfile(path).await?;
let mut chatcfg = json_config::chatservercfg::new();
const LISTENIP: &str = "listenip";
const LISTENPORT: &str = "listenport";
const USESSL: &str = "usessl";
const PRIVATEKEYFILE: &str = "privatekeyfile";
const SERVERCRTFILE: &str = "servercrtfile";
const CACRTFILE: &str = "cacrtfile";
const DOMAINNAME: &str = "domainname";
const CROSSDOMAINNAME: &str = "crossdomainname";
const AES128KEYHANDSHAKE: &str = "aes128keyhandshake";
const AES128IV: &str = "aes128iv";
const MAXCONN: &str = "maxconn";
const CHECKHEARTBEAT: &str = "checkheartbeat";
const OPENBLACKWHITEIP: &str = "openblackwhiteip";
const SINGLEIPMAXCONN: &str = "singleipmaxconn";
// macro_rules! getProp {
// ($x:expr, $y:expr) => {
// $x.get($y)
// };
// }
// macro_rules! getProp {
// ($x:expr)=>{
// chat_v.get($x)
// }
// }
// const type_num_u:u32 = 1 ;
// const type_num_i:u32 = 2 ;
// const type_string:u32 = 3 ;
//
// macro_rules! getProp {
// ($x:expr,$y:expr)=>{
// if let Some(v) = chat_v.get($x){
// match $y {
// type_num_u=>{
// if let Some(v2) = v.as_u64(){
// return Ok(v2) ;
// }
// }
// }
// }
// return Err("");
// }
// }
if let Some(ip) = chat_v.get(LISTENIP) {
chatcfg.szlistenip = ip.as_str().unwrap().to_string();
}
if let Some(port) = chat_v.get(LISTENPORT) {
chatcfg.nlistenport = port.as_u64().unwrap() as i32;
}
if let Some(bssl) = chat_v.get(USESSL) {
chatcfg.buseSsl = bssl.as_bool().is_some();
}
if let Some(maxconn) = chat_v.get(MAXCONN) {
chatcfg.u16maxconn = maxconn.as_u64().unwrap() as u16;
}
if let Some(keyfile) = chat_v.get(PRIVATEKEYFILE) {
chatcfg.szprivatekeyfile = keyfile.as_str().unwrap().to_string();
}
if let Some(cacrtfile) = chat_v.get(CACRTFILE) {
chatcfg.szcacrtfile = cacrtfile.as_str().unwrap().to_string();
}
if let Some(key) = chat_v.get(AES128KEYHANDSHAKE) {
//chatcfg.u8AES128keyhandshake = key.as_array().unwrap() ;
let mut v1 = key.as_str().unwrap().as_bytes(); //.as_slice();
//32 0x20 (space) 空格
//34 0x22 " 双引号
//10 0x0A 换行键 \n是换行
//13 0x0D CR (carriage return) 回车键 '\r'是回车
//39 0x27 ' 闭单引号 单引号的ASCII码是39
// let vlen = v1.len();
// if vlen < 16{
// return Err(Box::from(std::io::Error::new(ErrorKind::Other,"parse error")));
// }
//
// let tailch = v1[vlen-1] ;
// if 10 == tailch || 13 == tailch || 32 == tailch || 34 == tailch || 39 == tailch {
// // v1 = v1[..(vlen-1)].to_vec() ;
// v1.pop() ;
// }
//
// if 10 == v1[0] || 13 == v1[0] || 32 == v1[0] || 34 == v1[0] || 39 == v1[0] {
// v1 = v1[1..].to_vec() ;
// }
//0b_1010_1010
//let num1: u8 = 0b_1010_1010;
//println!("{:08b}", !num1);按位取反
let vlen = v1.len();
//es 128=16*8 192 =24*8 256=32*8
if vlen % 8 == 0
&& match vlen / 8 {
2 | 3 | 4 => true,
_ => false,
}
{
chatcfg.u8AES128keyhandshake = v1.to_vec();
}
//赋值数据到切片里
// let mut x = vec![0; 8];
// let y = [1, 2, 3];
// x[..3].clone_from_slice(&y);
// println!("{:?}", x);
// Output:
// [1, 2, 3, 0, 0, 0, 0, 0]
}
if let Some(iv) = chat_v.get(AES128IV) {
let i = iv.as_str().unwrap().as_bytes();
if i.len() == 16 {
chatcfg.u8AES128iv.clone_from_slice(&i);
} else {
return Err(Box::from(std::io::Error::new(
ErrorKind::Other,
"parse error",
)));
}
} else {
return Err(Box::from(std::io::Error::new(
ErrorKind::Other,
"parse error",
)));
}
if chatcfg.u8AES128keyhandshake.len() < 16 {
return Err(Box::from(std::io::Error::new(
ErrorKind::Other,
"parse error",
)));
}
Ok(chatcfg)
// if let (Some(ip),Some(port),Some(bssl),Some(maxconn),Some(key),Some(iv)) = (getProp!(LISTENIP),getProp!(LISTENPORT),getProp!(USESSL),getProp!(MAXCONN),
// getProp!(AES128KEYHANDSHAKE),getProp!(AES128IV)){
// chatcfg.szlistenip = ip.to_string() ;
//
// }else{
// println!("parse error");
// return Err(Box::from(std::io::Error::new(ErrorKind::Other,"parse error")));
// }
// if (root[LISTENIP].empty() || root[LISTENPORT].empty() || root[MAXCONN].empty() \
// || root[USESSL].empty() || root[AES128KEYHANDSHAKE].empty() || root[AES128IV].empty()) {
// printf("read_json_file base fail\n");
// return false;
//
// }
// chatcfg.szlistenip = chat_v.get(LISTENIP).to_string().?;
// chatcfg.nlistenport = chat_v.get(LISTENPORT)?;
//
// chatcfg.buseSsl = chat_v.get(USESSL).expect("ssl")?;
// chatcfg.szprivatekeyfile = chat_v.get(PRIVATEKEYFILE).expect("keyfile")?;
// //chatcfg.szservercrtfile = chat_v.get(SERVERCRTFILE).expect("szservercrtfile")?;
//
// chatcfg.szcacrtfile = chat_v.get(CACRTFILE).expect("cacrtfile")?;
//
// chatcfg.u8AES128keyhandshake = chat_v.get(AES128KEYHANDSHAKE).expect("AES128keyhandshake")?;
// chatcfg.u8AES128iv = chat_v.get(AES128IV).expect("AES128iv")?;
// chatcfg.u16maxconn = chat_v.get(MAXCONN).expect("u16maxconn")?;
// chatcfg.bcheckheartbeat = chat_v.get(CHECKHEARTBEAT).expect("checkheartbeat")?;
//Ok(chatcfg)
// pub(crate) szlistenip:String,
// pub(crate) nlistenport:i32,
// pub(crate) buseSsl:bool,
// pub(crate) szprivatekeyfile:String,
// pub(crate) szservercrtfile:String,
// pub(crate) szcacrtfile:String,
// pub(crate) szdomainname:String,//域名
// pub(crate) szcrossdomainname:String, //跨域名
// pub(crate) u8AES128keyhandshake:[u8;u8_aes_128_len as usize], //16*8=128 16个字节就可以了 yyuilioudkiojun aes 128=16*8 192 =24*8 256=32*8
// pub(crate) u8AES128iv:[u8;16],//16个字节 固定的16个字节 不管是ase128 192 256
// pub(crate) u16maxconn:u16,//最大连接数(总的)
// pub(crate) bcheckheartbeat:bool,//检测心跳
// pub(crate) u8openblackwhiteip:u8,//开启黑白名单
// pub(crate)u8singleipmaxconn:u8,//单个IP 最大连接数
}
//&'static str
//Result<(),dyn std::error::Error> Box<dyn Error
证书校验 完成
async fn wait_tls_check(
stream2: TcpStream,
acceptor: TlsAcceptor,
timeoutcheck: u64,
) -> Result<server::TlsStream<TcpStream>, &'static str> {
//
let dur = Duration::from_millis(timeoutcheck);
match time::timeout(dur, acceptor.accept(stream2)).await {
Ok(v) => {
match v {
Ok(v1)=>Ok(v1),
e=>{
println!("wait_tls_check accept error");
println!("{:?}",e);
Err("accept error")
}
}
// if let Ok(v1) = v {
// Ok(v1)
// } else {
// println!("wait_tls_check accept error");
// // let _= stream2.shutdown().await ;
// // let _= stream2.borrow().shutdown().await.unwrap();
// Err("accept error")
// }
}
_ => {
// Err(io::Error::new(io::ErrorKind::InvalidInput, "file no exist"))
// stream2.borrow().shutdown().await.unwrap();
// stream2.borrow().shutdown();
//怎么关闭 stream2 ????
println!("wait_tls_check timeout");
Err("time out")
}
}
}
async fn wait_handleshake(
stream: server::TlsStream<TcpStream>,
timeoutcheck: u64,
) -> Option<WebSocketStream<server::TlsStream<TcpStream>>> {
let f1 = async {
tokio_websockets::ServerBuilder::new()
.limits(Limits::default().max_payload_len(Some(MAX_PAYLOAD_LEN)))
.config(Config::default().frame_size(MAX_FRAME_SIZE))
.accept(stream)
.await
};
let dur = Duration::from_millis(timeoutcheck);
match time::timeout(dur, f1).await {
Ok(v) => {
if let Ok(v1) = v {
Some(v1)
} else {
None
}
}
_ => {
// stream.shutdown()
//关闭 stream
println!("wait_handleshake timeout");
None
}
}
}
async fn recv_frist_msg(
mut ws: WebSocketStream<server::TlsStream<TcpStream>>,
timeoutcheck: u64,
) -> Option<(WebSocketStream<server::TlsStream<TcpStream>>, Message)> {
let dur = Duration::from_millis(timeoutcheck);
let f1 = async {
ws.next().await };
match time::timeout(dur, f1).await {
Ok(v) => {
if let Some(Ok(m)) = v {
Some((ws, m))
} else {
None
}
}
_ => {
println!("recv_frist_msg timeout {}", timestamp_seconds());
ws.close().await.unwrap();
None
}
}
}
async fn verify_user_login(
userid: &UserID,
username: &String,
guild_id: &GuildID,
ase_token: &String,
md5_v: &String,
ip_addr: &String,
aeskey: &VU8,
iv: &[u8; 16],
) -> Result<(), Box<dyn Error>> {
let v = concat_string_token(&userid, &username, &guild_id, &ase_token);
let mut sh = crypto::md5::Md5::new();
sh.input_str(v.as_str());
let md5str = sh.result_str();
if !md5_v.eq(&md5str) {
return Err("fail".into());
}
let keylen = match aeskey.len() as u16 {
24 | 32 => aeskey.len() as u16,
_ => 16,
};
if let Ok(mut v) = aes_fun::encrypt(ase_token.as_bytes(), &aeskey, iv.as_ref(), keylen * 8) {
let len_v = v.len();
if len_v > 10 {
if v[len_v - 1] == ']' as u8 {
v.pop();
}
if v[0] == '[' as u8 {
v = v[1..].to_vec();
}
}
if let Ok(str) = std::str::from_utf8(&v) {
let t_string = str.to_string();
let raw_parts: Vec<&str> = t_string.split("#").collect();
//username base64 //小写字母a-z、大写字母A-Z、数字0-9、符号"+"、"/"(再加上作为垫字的"=",实际上是65个字符)
//[userid#username#guildid#ipaddr#timestamp#serverid#random] == 7 //username 为 base64encode 后的
if raw_parts.len() == 7 {
if let Ok(v_userid) = raw_parts[0].parse::<u32>() {
let v_username = raw_parts[1].to_string();
if let Ok(v_guildid) = raw_parts[2].parse::<u32>() {
let v_ipaddr = raw_parts[3].to_string();
if let Ok(v_timestampe) = raw_parts[4].parse::<u32>() {
if let Some(en_username) = aes_fun::base64encode(username.as_str()) {
//加密后再比较也一样
}
if v_userid == *userid && v_guildid == *guild_id && *ip_addr == v_ipaddr
{
if gobalfun::timestamp_seconds() <= (v_timestampe + 30) as u64 {
//30秒内有效
return Ok(());
}
}
}
}
}
}
}
}
// let encrypted_data = aes_fun::encrypt(ase_token.as_bytes(), &aeskey, iv.as_ref(),128).ok().unwrap();
// if !md5str.eq(&md5_v) {
// return Err("fail".into());
// }
// let data ="1233" ;
// let v: Value = serde_json::from_str(data)?;
// crypto::aes::cbc_decryptor()
Err("fail".into())
}
async fn tls_accept(
mut stream2: TcpStream,
acceptor: TlsAcceptor,
world_chan_sender_1: UnboundedSender<ActorMessage2>,
world_clone_1: UnboundedSender<ActorMessage>,
addr: SocketAddr,
vecaeskey: VU8,
iv: [u8; 16],
) {
if let Ok(v) = wait_tls_check(stream2, acceptor, 6_000).await {
if let Some(mut ws) = wait_handleshake(v, 4_000).await {
if let Some((mut ws, req)) = recv_frist_msg(ws, 3_000).await {
//处理接受的第一个消息
let world_chan_sender_2 = world_chan_sender_1.clone();
let world_clone_2 = world_clone_1.clone();
let connid = NEXT_CONNECT_ID.fetch_add(1, Ordering::Relaxed);
let (t1, r1) = mpsc::unbounded_channel::<SendClientMsg>();
let msgData = Vec::from(req.as_payload().to_vec());
let (msgid, mask) = getProtoMsgIdAndMask(&msgData);
let res = match msgid {
msg_id_login => {
if let Ok(req) = chatproto::ChatMessageLoginReq::decode(&msgData[..]) {
// error!("recv frirst msg");
// crypto::aes::cbc_decryptor(KeySize::KeySize128,"12345678".to_string().encode_to_vec().borrow(),)
//检测 req.tokenstr req.tokenmd5
//检查合法性
// if req.tokenstr.len() > 20 { //[userid#username#guildid#ipaddr#timestamp#serverid#random]
// if let Ok(_) = verify_user_login(&req.userid,&req.username,&req.guildid,&req.tokenstr,&req.tokenmd5,&addr.to_string(),&vecaeskey,&iv).await {
//
// }else{
// let _ =ws.close();
// return;
// }
// }else{
// let _ =ws.close();
// return;
// }
//check_user_login()
let world_chan_sender_3 = world_chan_sender_2.clone();
if let Ok(res) = query_user_channel2(
req.userid,
world_chan_sender_2,
connid,
t1.clone(),
)
.await
{
if res.state > 0 {
if res.state > 1 {
//超过最大在线人数
let mut repmsg = chatproto::ChatMessageLoginRep {
msghead: Some(chatproto::ChatMessageHeadNoMask {
msgid: chatproto::Chatmsg::ChcLoginRep as u32,
}),
res: 2,
tokenrelogin: "".to_string(),
timestamp: 0,
};
// ws.send(Message::text(String::from("Hello, world!").into()))
let mut buf = Vec::new();
if let Ok(_) = repmsg.encode(&mut buf) {
// Payloads can be created by using the From<Bytes>, From<BytesMut> and From<String>
let sendmsg =
tokio_websockets::Message::binary(Bytes::from(buf));
let _ = ws.send(sendmsg).await;
}
}
let _ = ws.close().await;
} else if let Some(s) = res.op {
//用现成的 //挤号
let mut repmsg = chatproto::ChatMessageLoginRep {
msghead: Some(chatproto::ChatMessageHeadNoMask {
msgid: chatproto::Chatmsg::ChcLoginRep as u32,
}),
res: 1,
tokenrelogin: "".to_string(),
timestamp: 0,
};
let mut buf = Vec::new();
if let Ok(_) = repmsg.encode(&mut buf) {
let sendmsg =
tokio_websockets::Message::binary(Bytes::from(buf));
let _ = ws.send(sendmsg).await;
}
tokio::spawn(run_user_actor_network(
ws,
s.sendchann,
r1,
s.actorState,
connid.clone(),
));
} else {
// 创建新的 //登录
let (logicchantx, logicchanrx) = mpsc::unbounded_channel();
let mut useractor = MyUserActor::new(
connid.clone(),
logicchanrx,
t1,
world_clone_2,
);
useractor.setuserinfo(
req.userid.clone(),
req.username.clone(),
req.guildid.clone(),
UserState_INIT,
);
//把发送通道发送给worldactor
let userinfo = userChan_CTW {
msgtype: 0,
userid: req.userid,
username: req.username,
userguildid: req.guildid,
connectid: connid,
logicChann: logicchantx.clone(),
chanState: useractor.getuserstateclone(),
};
let _ = world_chan_sender_3.send(ActorMessage2::ctw_userhann {
respond_to: userinfo,
});
let ustate = useractor.getuserstateclone();
let mut repmsg = chatproto::ChatMessageLoginRep {
msghead: Some(chatproto::ChatMessageHeadNoMask {
msgid: chatproto::Chatmsg::ChcLoginRep as u32,
}),
res: 0,
tokenrelogin: "".to_string(),
timestamp: 0,
};
let mut buf = Vec::new();
if let Ok(_) = repmsg.encode(&mut buf) {
// buf.insert(0,MyMsgType::Binary as u8);
let sendmsg =
tokio_websockets::Message::binary(Bytes::from(buf));
let _ = ws.send(sendmsg).await;
}
useractor.setuserstate(UserState::UserState_NORMAL as u8);
tokio::spawn(run_my_user_actor(useractor)); //逻辑future
tokio::spawn(run_user_actor_network(
ws,
logicchantx,
r1,
ustate,
connid.clone(),
)); //网络发送接受 future
}
} else {
let _ = ws.close().await;
}
} else {
let _ = ws.close().await;
}
// return Ok(());
}
msg_id_relogin => {
if let Ok(req) = chatproto::ChatMessageReLoginReq::decode(&msgData[..]) {
// if let Ok(res) = world_clone_2.query_user_channel(req.userid).await {
//
// }
}
// return Ok(());
}
_ => {
let _ = ws.close().await;
//return Err("msg is error");
}
};
} // 3 wait
}
}
}
2 toml 文件
[dependencies]
tokio-websockets= {
version = "0.5",features = ["server","ring"] }
futures-util = {
version = "0.3.30",features = ["sink"] }
rustls-pemfile = "2.0.0"
#tokio = { version = "1.35.1", features = ["full"] }
tokio = {
version = "1.35", features = ["full", "tracing"] }
tokio-rustls = "0.25.0"
rustls = "0.22.1"
rustls-pki-types = "1.1.0"
tokio-stream = "0.1.14"
bytes = "1.5.0"
prost="0.12"
prost-types = "0.12"
rust-crypto = "^0.2"
serde_json = "^1.0"
base64 = "0.21.7"
log = "0.4.0"
env_logger = "0.11"
chrono = "0.4.33"
#tracing-subscriber = { version = "0.3", default-features = false, features = ["alloc"] }
console-subscriber = "0.2.0"
#tracing = { version = "0.1", features = ["max_level_debug", "release_max_level_warn"] }
#tokio-console = "0.1.10"
#[dependencies]
#hello_utils = { path = "hello_utils" }
# 以下路径也可以
# hello_utils = { path = "./hello_utils" }
# hello_utils = { path = "../hello_world/hello_utils" }
#运行测试时用到
[build-dependencies]
#tokio-websockets= { version = "0.5",features = ["server","ring"] }
#futures-util = { version = "0.3.30",features = ["sink"] }
#rustls-pemfile = "2.0.0"
#tokio = { version = "1.35.1", features = ["full"] }
#tokio-rustls = "0.25.0"
#rustls = "0.22.1"
#rustls-pki-types = "1.1.0"
#tokio-stream = "0.1.14"
#bytes = "1.5.0"
prost-build = {
version = "0.12"}
3:代码说明
tls_accept 函数里
1> tls 握手(网上抄了张别人的图)这里设定必须N秒完成
2> websocket 握手 这里设定必须N秒完成
WebSocket握手过程
wobsocket会经过下面几个步骤:
(1)客户端发送WebSocket握手
(2)服务器响应握手请求
(3)客户端验证握手响应
(4)认确握手成功并建立websocket连接
3> 连接成功,接收登录消息 这里设定必须N秒完成
4> 处理登录验证** 具体代码 到看main.rs
(1) 超过最大人数 处理
(2) 挤号处理
(3) 新连接理
5>增加日志
4:测试
重新修改golang的前端
启动 testclientweb 1001 100 1000 192.168.1.131:8080
1001 启示userid
100 就是 100个连接 usertid [1001,1001+100)
1000 guildid
192.168.1.131:8080 连接的IP:PORT
package main
import (
"fmt"
"github.com/golang/protobuf/proto"
"golang.org/x/net/websocket"
"log"
"os"
"strconv"
"sync"
"testwebclient/chatproto"
"time"
)
//var origin = "http://115.159.207.227:7077" //http://login.yyfjfy.com/websocket/
//var url = "ws://115.159.207.227:7077/websocket"
//var origin = "http://192.168.1.131:9000" //http://login.yyfjfy.com/websocket/
//var url = "ws://192.168.1.131:7077/"
//var url = "wss://192.168.1.131:9000/websocket"
//var origin = "http://chat.yy9w.com:9501"
//var url = "wss://chat.yy9w.com:9501/websocket"
var origin = "http://192.168.1.131:8080"
var url_wss = "wss://192.168.1.131:8080/websocket"
var url_ws ="ws://192.168.1.131:8080/websocket"
//var origin = "http://124.222.83.132:9501"
//var url = "ws://124.222.83.132:9501/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 GetProtoMsgIdAndMask(data []byte) (uint32, uint32) {
var sMsgID uint16 = uint16(uint8(data[3] & 0x7f))
if (uint8(data[3]) & 0x80) > 0 {
sMsgID += (uint16(data[4]) & 0x7f) << 7
}
var sMaskID uint32 = uint32(data[6])
sMaskID |= uint32(data[7]) << 8
sMaskID |= uint32(data[8]) << 16
sMaskID |= uint32(data[9]) << 24
return uint32(sMsgID), sMaskID
}
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("recv 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)
}
}
case uint32(chatproto.CHATMSG_CHC_Notify_Msg):
{
chatmsg := &chatproto.ChatMessageNotifyMsg{
}
if err := proto.Unmarshal(data, chatmsg); err != nil {
} else {
// fmt.Printf("CHATMSG_CHC_Notify_Msg msgtype=%v text=%v \n",chatmsg.Msgtype,chatmsg.Strcontext)
}
}
}
}
func getTimestamp() uint32 {
return uint32(time.Now().UTC().Unix())
}
func testone() {
userid := getTimestamp()
guildid := uint32(0)
delayseconds := int64(1)
usewss := true
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)
}
}
if len(os.Args) > 3 {
if s,e := strconv.Atoi(os.Args[3]);e ==nil {
delayseconds = int64(s)
}
}
if len(os.Args) > 4 {
if s,e := strconv.Atoi(os.Args[4]);e ==nil {
if s > 0 {
usewss = true
}else{
usewss = false
}
}
}
url := url_wss
if !usewss {
url = url_ws
}
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
fmt.Printf("getTimestamp=%v userid=%v guild=%v dayseconds=%v url=%v \n",getTimestamp(),userid,guildid,delayseconds,url)
if delayseconds > 0 {
d := time.Duration(delayseconds)*time.Second
//fmt.Printf("[%v]dayseconds[start] =%v %v \n",getTimestamp(),d,dayseconds)
time.Sleep(d)
// fmt.Printf("[%v]dayseconds[end] =%v \n",getTimestamp(),dayseconds)
}
{
msg := new(chatproto.ChatMessageLoginReq)
msg.Msghead = &chatproto.ChatMessageHead{
uint32(chatproto.CHATMSG_CCH_Login_Req), 1}
msg.Userid = userid
msg.Username = "name_"+strconv.Itoa(int(userid))
msg.Guildid = guildid
msg.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 = true
return
}
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 = 1
msg.Context ="normal chat "+ strconv.Itoa(int(num))
for {
if disflag {
//脏数据
break
}
sendMsg(ws, msg)
time.Sleep(time.Second*2)
//time.Sleep(time.Millisecond *10)
num++
m := num % 3 +1
msg.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")
}
//testclientweb 1001 1 1000 192.168.1.131:8080
func main(){
wg := new(sync.WaitGroup)
if len(os.Args) == 5 {
useridmin := uint32(0)
var num uint32 = 0
var guildid uint32 = 0
//wss://192.168.1.32:8080/websocket
url_wss = fmt.Sprintf("wss://%v/websocket",os.Args[4])
//"http://192.168.1.32:8080"
origin = fmt.Sprintf("http://%v",os.Args[4])
fmt.Printf("url=%v origin=%v \n",url_wss,origin)
if s,e := strconv.Atoi(os.Args[3]);e ==nil {
guildid = uint32(s)
}
if s,e := strconv.Atoi(os.Args[2]);e ==nil {
num = uint32(s)
}
if s,e := strconv.Atoi(os.Args[1]);e ==nil {
useridmin = uint32(s)
}
//wg.Add(int(num))
testrobot(wg,useridmin,num,guildid)
// wg.Add(int(num))
}else{
wg.Add(1)
testone()
wg.Done()
}
wg.Wait()
fmt.Printf("client wait exit")
//select {
//
//}
}
func loginmsg(userid uint32,guildid uint32)proto.Message {
msg := new(chatproto.ChatMessageLoginReq)
msg.Msghead = &chatproto.ChatMessageHead{
uint32(chatproto.CHATMSG_CCH_Login_Req), 1}
msg.Userid = userid
msg.Username = "name_"+strconv.Itoa(int(userid))
msg.Guildid = guildid
msg.Tokenmd5 = "md5"
msg.Tokenstr = "Tokenstr"
return msg
}
func runonerobot(wg *sync.WaitGroup,ws *websocket.Conn,userid ,guildid uint32) {
disflag := false
defer wg.Done()
log.Printf("ready recv msg [%v,%v] \n",userid ,guildid)
go func() {
recvcount := uint32(0)
lastcount := uint32(0)
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 = true
return
}
if len(buf) >= 4 {
doLogicMsg(buf)
recvcount++
if (recvcount /50000) > lastcount {
lastcount = recvcount /50000
log.Printf("[%v]recv logic msg %v \n",userid,recvcount)
}
//self.msgQue.PostUserMessage(&ReceiveNetMsg{buf})
} else {
log.Printf("[error]recv data=%v \n", buf)
return
}
}
}()
wg.Add(1)
msg := loginmsg(userid,guildid)
sendMsg(ws,msg)
time.Sleep(time.Millisecond *50)
{
sendcount := uint32(1)
num := uint32(0)
msg := new(chatproto.ChatMessageChatReq)
msg.Msghead = &chatproto.ChatMessageHead{
uint32(chatproto.CHATMSG_CCH_Chat_Req), 1}
msg.Chattype = 1
msg.Context ="normal chat "+ strconv.Itoa(int(num))
//1 normal 2 guild 3 world 4 all server(后续实现)
for {
if disflag {
//脏数据
break
}
sendMsg(ws, msg)
//time.Sleep(time.Second)
time.Sleep(time.Millisecond *1000) //50
num++
//m := num % 3 +1
msg.Chattype = uint32(2)
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 %v \n",userid)
}
func testrobot(wg *sync.WaitGroup,useridmin,num uint32,guildid uint32) {
fmt.Printf("startuser=%v num=%v guild=%v \n",useridmin,num,guildid)
//sendMsg(ws, msg)
for i:=useridmin;i<useridmin+num;i++ {
//loginmsg
ws, err := websocket.Dial(url_wss, "", origin)
if err != nil {
//log.Fatal(err)
log.Printf("[%v]Dial fail %v \n",i,err.Error())
time.Sleep(time.Millisecond *100)
continue
}
go runonerobot(wg,ws,i,guildid)
time.Sleep(time.Millisecond *200)
//time.Sleep(time.Millisecond *10)
}
}
再来个c++版的 不知道是不是用虚拟机的问题,带宽最大只能达100M 左右 再多开前端 ssl_write 有几率会失败(返回 SSL_ERROR_SSL)
一个父进程(daemon进程)
一个子进程 (网络进程)
一个子进程 (逻辑进程)
c++ protobuf 建议使用 3.21.12及以下 ,往上版本需要abseil的三方库,安装方式比较复杂
这里使用的3.21.11
主要逻辑处理代码
const uint32_t nlen = pblock->len;
uint64_t msgid_mask = getmsgidandmask(buf, nlen);
const uint32_t msgid = msgid_mask >> 32;
const uint32_t mask = msgid_mask & 0xFFFFFFFF;
switch (msgid)
{
case chatproto::CCH_Login_Req:
{
chatproto::ChatMessageReLoginReq req;
if (!req.ParseFromArray((void*)buf, nlen)) {
// 反序列化
printf("parse fail protobuf(CCH_Login_Req) \n");
}
else {
printf("client login %d %d\n", pblock->fd, req.userid());
}
const uint32_t userid = req.userid();
string username = req.username();
const uint32_t guildid = req.guildid();
UserBaseInfo* puser = UserMgr::GetInstance().getUserBaseInfo(userid);
if (puser != nullptr) {
//暂未处理
}
else {
UserBaseInfo* p = new UserBaseInfo();
p->userid = userid;
strncpy(p->username, username.c_str(), 63);
p->userguildid = guildid;
p->userstate = 1;
p->fd = pblock->fd;
p->id = pblock->id;
UserMgr::GetInstance().addNewUserInfo(p);
}
chatproto::ChatMessageHeadNoMask* prephead = new chatproto::ChatMessageHeadNoMask();
prephead->set_msgid(chatproto::CHATMSG::CHC_Login_Rep);
chatproto::ChatMessageLoginRep rep;
rep.set_allocated_msghead(prephead);
rep.set_res(1);
int len = rep.ByteSize(); //获取长度
// uint8_t databuf[1024];
memset(databuf, 0, 1024);
rep.SerializeToArray(databuf, len);//序列化
send_data(pblock->fd,pblock->id, databuf, len, WebSocketFrameType::BINARY_FRAME);
printf("client login finish %d\n", userid);
}
break;
case chatproto::CCH_Chat_Req:
{
//const uint32_t userid = req.param();
const uint64_t key = (uint64_t(pblock->fd) << 32) + pblock->id;
UserBaseInfo* puser = UserMgr::GetInstance().getUserBaseInfoBySession(key);
if (puser) {
//const uint32_t userid = req.param();
chatproto::ChatMessageChatReq req;
if (!req.ParseFromArray((void*)buf, nlen)) {
// 反序列化
printf("parse fail protobuf(ChatMessageChatReq) \n");
}
const uint32_t userid = puser->userid;
const uint32_t channel = req.chattype();
if (channel == ChatChannel::ChatChannel_GUILD || channel == ChatChannel::ChatChannel_WORLD) {
chatproto::ChatMessageHeadNoMask* prephead = new chatproto::ChatMessageHeadNoMask();
prephead->set_msgid(chatproto::CHATMSG::CHC_Notify_Chat);
string strcontext = req.context();
chatproto::ChatMessageNotifyChat rep;
rep.set_allocated_msghead(prephead);
rep.set_chattype(channel);
rep.set_senderid(userid);
rep.set_sendername(puser->username);
rep.set_strcontext(strcontext);
// rep.mutable_sendername() = puser->username;
//rep.mutable_sendername()->CopyFrom(puser->username);
// rep.set_allocated_strcontext(&strcontext);
//rep.mutable_strcontext() = ;
const int len = rep.ByteSize(); //获取长度
memset(databuf, 0, 1024);
rep.SerializeToArray(databuf, len);//序列化
const uint32_t u32len = generateframedata(databuf, len, WebSocketFrameType::BINARY_FRAME,outbuf);
if (u32len <= len) {
printf("[error]data generate fail \n");
}
if (channel == ChatChannel::ChatChannel_GUILD && puser->userguildid > 0) {
vector<UserBaseInfo*> v = UserMgr::GetInstance().getGuildInfo(puser->userguildid);
UserBaseInfo* ptuser = nullptr;
const uint32_t guildsize = v.size();
for (uint32_t i = 0; i < guildsize; ++i) {
ptuser = v[i];
if (ptuser && ptuser->userstate > 0) {
//send_data(ptuser->fd, ptuser->id, databuf, len, WebSocketFrameType::BINARY_FRAME);
senddata_immediately(outbuf, u32len, WebSocketFrameType::BINARY_FRAME, ptuser->fd, ptuser->id);
}
}
}
else if (channel == ChatChannel::ChatChannel_WORLD) {
UserBaseInfo* ptuser = nullptr;
map<uint32_t, UserBaseInfo*> m = UserMgr::GetInstance().getallUsermap();
for (auto i = m.begin(); i != m.end(); ++i) {
ptuser = i->second;
if (ptuser && ptuser->userstate > 0) {
//send_data(ptuser->fd, ptuser->id, databuf, len, WebSocketFrameType::BINARY_FRAME);
senddata_immediately(outbuf, u32len, WebSocketFrameType::BINARY_FRAME, ptuser->fd, ptuser->id);
}
}
}
//send_data(pblock->fd, pblock->id, databuf, len, WebSocketFrameType::BINARY_FRAME);
}
else if (channel == ChatChannel::ChatChannel_NORMAL) {
}
}
}
break;
default:
break;
}
5:DEMO工程
1>DEMO工程 内存一直在增长 (需要改善)
2> 黑白名单没有
3>日志 写文件没有,当前只有打印
4>限制单个IP的连接数及限流(令牌或水桶)
demo工程下载地址
如果觉得有用,麻烦点个赞,加个收藏