聊天服务器——ClientSession

首先是二进制的读写器

BinaryReader

class有两个成员变量

string m_buffer;
uint32_t m_index;

第一个m_buffer是存string类型的buffer,第二个是指针index。根据业务需求,讲数据赋给m_buffer,然后开始

读数据,并移动指针m_index。

前两个函数是用来输出打印的

static void dump(const string& buf) {
            dump(buf.c_str(), buf.size());
}
​
static void dump(const char* buf, size_t size) {
    for (size_t i = 0; i < size; i++) 
    {
        if (i != 0 && ((i % 16) == 0))printf("\r\n");
        printf("%02X ", (unsigned)(buf[i]) & 0xFF);
    }
        printf("\r\n");
}

构造函数与赋值构造函数、析构函数

BinaryReader() { m_index = 0; }
BinaryReader(const BinaryReader& reader) {
    m_buffer = reader.m_buffer;
    m_index = reader.m_index;
}
BinaryReader(const string& buffer) {
    m_index = 0;
    m_buffer = buffer;
}
~BinaryReader() = default;

更新buffer

void UpdateBuffer(const string& buffer) {
    m_buffer = buffer;
    m_index = 0;
}

下面是重点,从m_buffer中读取数据,这个读取的数据会根据数据类型来读取。

读取int32

bool ReadInt32(int32_t& data) {
    //如果读取int32超过了m_buffer的
    if (m_index + sizeof(int32_t) > m_buffer.size())return false;
    //把读取到的int32转移到data上
    memcpy(&data, m_buffer.c_str() + m_index, sizeof(int32_t));
    //指针偏移int32的大小
    m_index += sizeof(int32_t);
    return true;
}

读取其他类型的数据

在class内设计一个模板函数,模板是class T,T表示数据类型。然后把T转换成char*类型

template<class T>
bool ReadData(T& data) {
    if (m_index + sizeof(T) > m_buffer.size())return false;
    char* pData = (char*)&data;
    for (size_t i = 0; i < sizeof(T); i++)
    {
        pData[i] = *(m_buffer.c_str() + m_index + sizeof(T) - (i + 1));
    }
    m_index += sizeof(T);
    return true;
}

BinaryWriter

成员变量

string m_buffer;
uint32_t m_index;

写数据的操作

template<class T>
bool WriteData(const T& data) {
    m_buffer.resize(m_index + sizeof(T));
    memcpy((char*)m_buffer.c_str() + m_index, &data, sizeof(data));
    m_index += sizeof(T);
    return true;
}
uint32_t Size() { return m_index; }
string toString()const { return m_buffer; }
void Clear() {
    m_index = 0;
    m_buffer.clear();
}

压缩操作

一个char占一个字节

无符号整数:一个字节可以存储0~255之间的无符号整数。

带符号整数:一个字节可以存储-128~127的整数。

0x7f 是 0111 1111

size_t 是无符号整数类型,意味着它只能表示非负整数,包括0。它不存储负数值。

一般情况下,在 32 位系统中 size_t 是 4 字节的,而在 64 位系统中,size_t 是 8 字节的,这样利用该类型可以增强程序的可移植性。

void Compress(size_t len, string& out) {
    char c = 0;
    if (len < 128) {
        c = (char)len & 0x7F;
        out += c;
        return;
    }
    //假定这个长度不会太长,百兆以内
    //输出的时候不会超过5个字节,32个bit能够表达
    for (int i = 4; i >= 0; i--) {
        c = (len >> (7 * i)) & 0x7F;
        //目前还没有发现有效数据,都是零
        if (c == 0 && out.size() == 0)
            continue;
        if (i > 0)//说明不是最后7位
            c |= 0x80;
        out += c;
    }
}

template<>
bool BinaryReader::ReadData(string& data) {
    const char* pcur = m_buffer.c_str() + m_index;
    int length = 0;
    size_t i = 0;
    for (; i < m_buffer.size() - m_index; i++) {
        length <<= 7;
        length |= pcur[i] & 0x7F;
        if ((pcur[i] & 0x80) == 0)break;
    }
    m_index += i + 1;
    data.assign(m_buffer.c_str() + m_index, length);
    m_index += length;
    return true;
}
template<>
bool BinaryWriter::WriteData(const string& data) {
    string out;
    Compress(data.size(), out);
    m_buffer.append(out.c_str(), out.size());
    m_index += out.size();
    if (data.size() > 0) {
        m_buffer.append(data.c_str(), data.size());
        m_index += data.size();
    }
    return true;
}

TcpSession

class TcpSession
{
public:
    TcpSession() = default;
    ~TcpSession() = default;
    //把包发送出去
    void Send(const TcpConnectionPtr& conn, BinaryWriter& writer) {
        string out = writer.toString();
        writer.Clear();
        int len = (int)out.size();//获取包长度
        writer.WriteData(len + 6);
        writer.WriteData(htonl(len));
        writer.WriteData(htons(0));
        out = writer.toString() + out;
        if (conn != NULL) {
            BinaryReader::dump(out);
            conn->send(out.c_str(), out.size());
        }
    }
};

ClientSession

class ClientSession :public TcpSession
{
public:
    ClientSession(const TcpConnectionPtr& conn);
    //为了控制生命周期,防止提前销毁,或者销毁之后,重复销毁
    ClientSession(const ClientSession&) = delete;
    ClientSession& operator=(const ClientSession&) = delete;
    ~ClientSession();
​
    operator std::string() {
        return m_sessionid;
    }
​
    void OnRead(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time);
​
    //业务函数
    bool Process(const TcpConnectionPtr& conn, string msgbuff);
protected:
    void OnHeartbeatResponse(const TcpConnectionPtr& conn, const string& data);
    void OnRegisterResponse(const TcpConnectionPtr& conn, const string& data);
    void OnLoginResponse(const TcpConnectionPtr& conn, const string& data);
private:
    std::string m_sessionid;
    int m_seq;//会话的序号
};
​
typedef std::shared_ptr<ClientSession> ClientSessionPtr;

ClientSession::ClientSession(const TcpConnectionPtr& conn)
{
    m_seq = 0;
    //uuid_generate(m_sessionid);
    //此处就是两个括号,第一个表示构造对象
    //第二个表示()运算符重载
    stringstream ss;
    ss << (void*)conn.get();
    m_sessionid = ss.str();// to_string(random_generator()());
    TcpConnectionPtr* client = const_cast<TcpConnectionPtr*>(&conn);
    //*const_cast<std::string*>(&conn->name()) = m_sessionid;
    (*client)->setMessageCallback(std::bind(&ClientSession::OnRead, this, _1, _2, _3));
}
​
ClientSession::~ClientSession()
{
​
}

void ClientSession::OnRead(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)
{
    cout << __FILE__ << "(" << __LINE__ << ")\r\n";
    //每次从buf里先取出来int32放入到BufferReader,这个是包的大小。跳过包的大小,创建string msgbuff,再把数据包里的东西放入到msgbuff,这个是真数据。就是数据包大小为什么减6,这个还要再研究一下。视频说的这个减6是因为格式会多6个字节。
    //然后把真数据放入到Process里去处理。
    while (buf->readableBytes() >= sizeof(int32_t)) {
        int32_t packagesize = 0;
        BinaryReader::dump(buf->peek(), buf->readableBytes());
        packagesize = *(int32_t*)buf->peek();
        if (buf->readableBytes() < sizeof(int32_t) + packagesize)
            return;
        buf->retrieve(sizeof(int32_t));
        string msgbuff;
        cout << __FILE__ << "(" << __LINE__ << ")" << packagesize << "\r\n";
        msgbuff.assign(buf->peek() + 6, packagesize - 6);
        BinaryReader::dump(msgbuff);
        buf->retrieve(packagesize);
        if (Process(conn, msgbuff) != true) {
            cout << "process error,close connection!\r\n";
            conn->forceClose();
        }
    }
}

处理函数process。先是把传入进来的string msgbuff放入到 BinaryReader reader里,使用 BinaryReader 来

解析指令。解析分三步,第一步解析指令cmd,第二步解析序列号m_seq, 第三步解析数据data。

bool ClientSession::Process(const TcpConnectionPtr& conn, string msgbuff)
{
    BinaryReader reader(msgbuff);
    int cmd = -1;
    if (reader.ReadData<decltype(cmd)>(cmd) == false)return false;
    if (reader.ReadData<int>(m_seq) == false)return false;
    string data;
    if (reader.ReadData(data) == false)return false;
    cout << __FILE__ << "(" << __LINE__ << ")" << cmd << "\r\n";
    cout << __FILE__ << "(" << __LINE__ << ")" << m_seq << "\r\n";
    cout << __FILE__ << "(" << __LINE__ << ")" << data.size() << "\r\n";
    
    switch (cmd) {
    case msg_type_heartbeart://心跳包
        OnHeartbeatResponse(conn, data);
        break;
    case msg_type_register://注册消息
        OnRegisterResponse(conn, data);
        break;
    case msg_type_login://登录消息
        OnLoginResponse(conn, data);
        break;
    case msg_type_getofriendlist://获取好友列表
        break;
    case msg_type_finduser://查找用户
        break;
    case msg_type_operatefriend://操作好友
        break;
    case msg_type_updateuserinfo://更新用户信息
        break;
    case msg_type_modifypassword://修改密码
        break;
    case msg_type_creategroup://创建群组
        break;
    case msg_type_getgroupmembers://获取群成员
        break;
    case msg_type_chat://聊天消息
        break;
    case msg_type_multichat://群发消息
        break;
    default:
        break;
    }
    return true;
}

OnHeartbeatResponse(conn, data)

void ClientSession::OnHeartbeatResponse(const TcpConnectionPtr& conn, const string&)
{
    //包的长度 4字节 不能压缩的,固定格式
    //命令类型 4字节 不能压缩的,固定格式
    //包的序号 4字节 不能压缩的,固定格式
    //包的数据 包长度(4字节,可以压缩的)+包内容(长度由前面一项来定)
    BinaryWriter writer;
    int cmd = msg_type_heartbeart;
    writer.WriteData(htonl(cmd));
    writer.WriteData(htonl(m_seq));
    string empty;
    writer.WriteData(empty);
    Send(conn, writer);
}

OnRegisterResponse

先使用Json的Reader来解析数据data,如果解析失败,就会发送解析失败的信息过去。

解析成功,会调用UserManager,把用户信息添加到数据库上,并返回成功的信息。

void ClientSession::OnRegisterResponse(const TcpConnectionPtr& conn, const string& data)
{//{"username":"手机号","nickname":"昵称","password":"密码"}
    Json::Reader reader;
    Json::Value root, response;
    BinaryWriter writer;
    string result;
    int cmd = msg_type_register;
    writer.WriteData(htonl(cmd));
    writer.WriteData(htonl(m_seq));
    if (reader.parse(data, root) == false) {
        cout << "error json:" << data << endl;
        response["code"] = 101;
        response["msg"] = "json parse failed!";
        result = response.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
        return;
    }
    if (!root["username"].isString() ||
        !root["nickname"].isString() ||
        !root["password"].isString())
    {
        cout << "error type:" << data << endl;
        response["code"] = 102;
        response["msg"] = "json data type error!";
        result = response.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
        return;
    }
    User user;
    user.username = root["username"].asString();
    user.nickname = root["nickname"].asString();
    user.password = root["password"].asString();
​
    if (!Singleton<UserManager>::instance().AddUser(user))
    {
        cout << "add user failed!\r\n";
        response["code"] = 100;
        response["msg"] = "register failed!";
        result = response.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
        return;
    }
    else {
        root["code"] = 0;
        root["msg"] = "ok";
        result = root.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
    }
}

OnLoginResponse

登录

先使用Json的Reader来解析数据data,如果解析失败,就会发送解析失败的信息过去。

解析成功了,会得到用户输入的用户名和密码,然后把用户名和密码放入到数据库里去匹配,匹配正确,就会

返回正确的信息。如果匹配不正确,就会返回错误的信息。

void ClientSession::OnLoginResponse(const TcpConnectionPtr& conn, const string& data)
{//{"username":"用户名","password":"密码","clienttype":1,"status":1}
    BinaryWriter writer;
    string result;
    Json::Value root, response;
    Json::Reader reader;
    int cmd = msg_type_login;
    writer.WriteData(htonl(cmd));
    writer.WriteData(htonl(m_seq));
    if (reader.parse(data, root) == false) {
        cout << __FILE__ << "(" << __LINE__ << ")\r\n";
        response["code"] = 101;
        response["msg"] = "json parse failed!";
        result = response.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
        return;
    }
    if (!root["username"].isString() ||
        !root["password"].isString() ||
        !root["clienttype"].isInt() ||
        !root["status"].isInt())
    {
        cout << __FILE__ << "(" << __LINE__ << ")\r\n";
        response["code"] = 102;
        response["msg"] = "json data type error!";
        result = response.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
        return;
    }
    string username = root["username"].asString();
    string password = root["password"].asString();
    User user;
    if (Singleton<UserManager>::instance().GetUserInfoByUsername(username, user) == false)
    {
        cout << __FILE__ << "(" << __LINE__ << ")\r\n";
        response["code"] = 103;
        response["msg"] = "user is not exist or password is incorrect!";
        result = response.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
        return;
    }
​
    if (password != user.password) {
        cout << __FILE__ << "(" << __LINE__ << ")\r\n";
        response["code"] = 104;
        response["msg"] = "user is not exist or password is incorrect!";
        result = response.toStyledString();
        writer.WriteData(result);
        Send(conn, writer);
        return;
    }
​
​
    //如果成功,返回应答
    response["code"] = 0;
    response["msg"] = "ok";
    response["userid"] = user.userid;
    response["username"] = user.username;
    response["nickname"] = user.nickname;
    response["facetype"] = user.facetype;
    response["customface"] = user.customface;
    response["gender"] = user.gender;
    response["birthday"] = user.birthday;
    response["signature"] = user.signature;
    response["address"] = user.address;
    response["phonenumber"] = user.phonenumber;
    response["mail"] = user.mail;
    result = response.toStyledString();
    writer.WriteData(result);
    cout << __FILE__ << "(" << __LINE__ << ")\r\n";
    Send(conn, writer);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值