4 ClientUser实现

我们为客户端定义了一个代理类ClientUser,该类继承于基类库中的LinkUser,头文件实现如下

#pragma once
#include "LinkUser.h"
#include <string>
#include "ChatProtocol.h"

class ClientUser : public CLinkUser
{
public:
    ClientUser();

public:
    virtual bool OnMsg(char* msg, int nLen);

    virtual bool OnBreak();
    
public:
    bool OnRequestSessionID();

    bool OnCreateRoom();

    bool OnJoinRoom(PHYHEADER pHeader);

    bool OnChatMsg(PHYHEADER pHeader);

    int64_t GetSesID(){return m_sesID;}

public:
    std::string m_strNickName;
};

基类库的TCPSession在会话创立后,根据消息来源创建,调用服务的CreateUser函数,创建了User对象,并将其和Session绑定,后续ClientUser可以通过这个会话和客户端进行消息通信。

OnMsg函数用于处理客户端发来的消息,根据消息类型的不同,分别由下面的OnXXX函数处理。

给客户端发送消息,调用基类函数SendMsg(char* msg, int nLen);

和客户端连接状态异常,端开连接后,会调用函数OnBreak,函数中可以处理一些清理操作。

最后,定义了一个成员变量nickName,用于记录聊天用户的昵称。

下面看看OnMsg函数的具体实现:

ClientUser收到的消息,其消息格式为HYHEADER打头,这个是由基类保证的,业务层可以直接转换使用。

pHeader的wType参数,标识了消息类型,每个消息定义了一个类型。统一定义在ChatProtocol头文件中

先看获取会话ID的请求,因为会话ID在会话创建时已经自动分配,此处可以直接回复客户端

通过自定义的CHYBuffer,拼接了消息头和消息内容。

首先SetHeaderLen设置消息头长度,但此时未分配空间。后面执行AppendFormatString的时候,才真正分配空间,并在数据前面预留出消息头的数据长度。

消息内容是一个json字符串,因为比较简单,直接进行字符串拼接,内容拼接后为{"sesID":"1"}。

消息头是HYHEADER格式,dwLength是消息内容的长度,此处使用了GetDataLen()函数,是指除了SetHeaderLen函数之后的数据长度,也就是我们发送的内容长度。后面再SendMsg时,传入的参数是GetBufLen(),此时获取的长度是整个buf的数据长度,包含了消息头和消息内容。这两个函数略有差异。

wOrigine设置了消息来源,这个后期可以整合到基类中,避免每次设置,有兴趣的同学可以进行优化。

wType设置了回复消息的类型,这里使用了HY_ACK或上请求类型,wType定义的类型为2个字节,因此最大表示范围到65535.

最后,直接在函数中调用基类的SendMsg方法,将消息回复给客户端。

下面再讲一个略微复杂的请求,客户端请求发送消息的处理

请求发送消息的请求,是携带数据内容的,数据紧跟再Header后面,为json格式。

此处引入了github上的json解析类nlohmann/json.hpp,用于解析客户端上传的json数据。

首先将指针偏移到消息头后面,取char指针作为数据起始,解析json中的msg和roomID。

这里直接使用了服务的指针,调用服务中相应的处理方法,当然这里也可以使用其他方式达到相同的目的,并且解除ClientUser和ChatService之间的耦合,不过直接调用指针也没有太坏的影响,且更为直观,如此处理即可。

同步调用了服务的方法,得到返回结果,作为status的参数,发送给客户端,消息发送方式与获取SessionID的相同,此处不再赘述。

CreateRoom和JoinRoom的请求处理,与上面基本相同,不再详细描述,附上代码

Last updated