基本结构
从基本结构上,我主要封装了三个类,Protocol->TcpClient->MsgHandler,这三者依次从底层到应用层,主要分别处理数据与协议、tcp socket收/发、消息处理,下面简单说说。
Protocol
主要提供数据转换接口,并定义了协议。
数据转换接口主要是:StructToBytes和BytesToStruct,C#上的实现利用了Marshal。
协议由消息头和消息内容构成,消息头包含协议size和id,消息内容就是纯bytes。
TcpClient
这里具体处理了socket的连接/断开/发送/接收等。主要对外提供了Init/Connect/Disconnect/Send/Recv/IsConnect等接口。
MsgHandler
这是TCP模块对外的应用接口,开启一个消息处理线程,在后台处理消息收发。主要实现以下功能:
1. 初始化
2. 消息注册。基本的观察者模式
3. 打开连接/断开连接/重新连接等
4. 发送消息
5. 消息处理。处理具体的消息发/收,后面贴上这块的实现代码片段。
6. 心跳包。每隔5s自动发心跳包,检查网络连接。
7. 销毁。做清理工作。
消息处理
直接上代码片段,简单注释
// 初始化时开启消息处理线程
m_pobjMsgThread = new Thread(new ThreadStart(this.ProcessMsg));
m_pobjMsgThread.Name = "thread_msg";
m_pobjMsgThread.IsBackground = true;
m_pobjMsgThread.Start();
// 消息处理
void ProcessMsg() {
while (m_bFlagRun) {
if (m_bReconnect) { // 处理reconnect请求
this.DoDisconnect();
m_bReconnect = false;
}
if (!this.IsConnected() && m_bAutoConnect) {// 处理断线
this.DoConnect(m_strAddress, m_strPort);
Thread.Sleep(0);
if (!this.IsConnected())
Thread.Sleep(kReconnectionTime);
continue;
}
if (m_msgQue.Count() > 0) {// 消息发送,一次把所有消息缓存都发送
lock (m_msgQue) {
while (m_msgQue.Count() > 0) {
m_msgQueBuffer.Enqueue(m_msgQue.Dequeue());
}
}
while (m_bFlagSend && m_msgQueBuffer.Count > 0) {
Msg msg = m_msgQueBuffer.Dequeue();
this.m_pobjTcpClient.Send(msg.bytes, msg.length);
}
}
if (m_bFlagRecv) // 消息接收
m_pobjTcpClient.Recv();
Thread.Sleep(5);
}
}
优化
为了保证IO不卡顿,发送消息接口(Send)只是把消息丢到一个queue里,具体的发送是在单独的线程(ProcessMsg)里处理的。
消息接收(Recv)也可能会有IO瓶颈,这块的优化需要在业务逻辑里根据具体情况处理。比如我们原来一款RPG游戏,在主角进入人堆(比如城里摆摊、国战)或怪堆(比如副本里局部有很多小怪)里的时候,会瞬时收到几十条甚至数百条创建人/怪的消息,我也是采用了类似于前面消息发送的缓存机制,把创建人/怪的消息缓存到一个queue里,然后在游戏每一帧创建一个,大大改善了IO卡顿。
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-08 19:45:52