首先是二进制的读写器
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); }