网络游戏TCP长连接基本设计

基本结构

从基本结构上,我主要封装了三个类,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卡顿。

展开阅读全文

没有更多推荐了,返回首页