This document outlines the various business logic operations handled by a chat server, including essential functionalities like heartbeats, user registration, login, friend management, profile updates, group operations, and real-time messaging.
Data Packet Structure
All data packets are prefixed with a business id followed by a sequence number, m_seq.
Heartbeat
The heartbeat mechanism is used to verify the online status of connected clients. It requires no additional data beyond the standard packet structure.
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_HEARTBEAT); // Assuming MSG_TYPE_HEARTBEAT is defined elsewhere
stream.Write(sequenceNumber);
std::string emptyData;
stream.Write(emptyData.c_str(), emptyData.length());
stream.Flush();
// LOG_INFO << "Responding to client: cmd=" << MSG_TYPE_HEARTBEAT << ", sessionId=" << sessionID;
Send(outgoingBuffer);
User Registration
User registration involves validating incoming JSON data for format and required fields (username, nickname, password). If valid, the system checks for existing usernames. If a username is unique, a new user is added to the UserManager; otherwise, a registration failure message is returned.
void ClientSession::HandleRegistration(const std::string& jsonData, const std::shared_ptr& connection)
{
// Example JSON: { "username": "13917043329", "nickname" : "balloon", "password" : "123" }
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON format: " << jsonData << ", sessionId = " << sessionID << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (!rootObject["username"].isString() || !rootObject["nickname"].isString() || !rootObject["password"].isString()) {
// LOG_WARN << "Missing required fields in JSON: " << jsonData << ", sessionId = " << sessionID << ", client: " << connection->peerAddress().toIpPort();
return;
}
User newUser;
newUser.username = rootObject["username"].asString();
newUser.nickname = rootObject["nickname"].asString();
newUser.password = rootObject["password"].asString(); // Password should ideally be hashed
std::string responseMessage;
User existingUser;
existingUser.userId = 0; // Initialize with a non-valid user ID
if (Singleton<usermanager>::GetInstance().FindUserByUsername(newUser.username, existingUser)) {
responseMessage = "{\"code\": 101, \"msg\": \"Username already registered\"}";
} else {
if (!Singleton<usermanager>::GetInstance().AddUser(newUser)) {
responseMessage = "{\"code\": 100, \"msg\": \"Registration failed\"}";
} else {
responseMessage = "{\"code\": 0, \"msg\": \"ok\"}";
}
}
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_REGISTER); // Assuming MSG_TYPE_REGISTER is defined
stream.Write(sequenceNumber);
stream.Write(responseMessage.c_str(), responseMessage.length());
stream.Flush();
// LOG_INFO << "Registration response: cmd=" << MSG_TYPE_REGISTER << ", userId=" << newUser.userId << ", data=" << responseMessage;
Send(outgoingBuffer);
}
</usermanager></usermanager>
User Login
Login proceeds by validating the incoming JSON. If valid, credentials (username, password) are extracted. The system queries the UserManager for the username. If the user doesn't exist, a error is returned. If the user exists, the password is compared. Mismatched passwords result in an error. Successful logins return user information and trigger the push of cached messages and online status notifications to friends.
void ClientSession::HandleLogin(const std::string& jsonData, const std::shared_ptr& connection)
{
// Example JSON: {"username": "13917043329", "password": "123", "clientType": 1, "status": 1}
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON for login: " << jsonData << ", sessionId = " << sessionID << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (!rootObject["username"].isString() || !rootObject["password"].isString() || !rootObject["clientType"].isInt() || !rootObject["status"].isInt()) {
// LOG_WARN << "Invalid login JSON fields: " << jsonData << ", sessionId = " << sessionID << ", client: " << connection->peerAddress().toIpPort();
return;
}
std::string username = rootObject["username"].asString();
std::string password = rootObject["password"].asString();
std::ostringstream responseStream;
User authenticatedUser;
authenticatedUser.userId = 0;
if (!Singleton<usermanager>::GetInstance().FindUserByUsername(username, authenticatedUser)) {
responseStream << "{\"code\": 102, \"msg\": \"User not registered\"}";
} else {
if (authenticatedUser.password != password) { // Password comparison should use hashing
responseStream << "{\"code\": 103, \"msg\": \"Incorrect username or password\"}";
} else {
// Store session-specific user info
sessionUserInfo.userId = authenticatedUser.userId;
sessionUserInfo.username = username;
sessionUserInfo.nickname = authenticatedUser.nickname;
sessionUserInfo.password = password; // Should not be stored here
sessionUserInfo.clientType = rootObject["clientType"].asInt();
sessionUserInfo.status = rootObject["status"].asInt();
// Construct success response with user details
responseStream << "{\"code\": 0, \"msg\": \"ok\", \"userId\": " << sessionUserInfo.userId << ",\"username\":\"" << authenticatedUser.username << "\", \"nickname\":\""
<< authenticatedUser.nickname << "\", \"faceType\": " << authenticatedUser.faceType << ", \"customFace\":\"" << authenticatedUser.customFace << "\", \"gender\":" << authenticatedUser.gender
<< ", \"birthday\":" << authenticatedUser.birthday << ", \"signature\":\"" << authenticatedUser.signature << "\", \"address\": \"" << authenticatedUser.address
<< "\", \"phoneNumber\": \"" << authenticatedUser.phoneNumber << "\", \"email\":\"" << authenticatedUser.email << "\"}";
}
}
// Send login response
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_LOGIN); // Assuming MSG_TYPE_LOGIN is defined
stream.Write(sequenceNumber);
stream.Write(responseStream.str().c_str(), responseStream.str().length());
stream.Flush();
// LOG_INFO << "Login response: cmd=" << MSG_TYPE_LOGIN << ", data=" << responseStream.str() << ", userId=" << sessionUserInfo.userId;
Send(outgoingBuffer);
// Push cached notifications
std::list<notifymsgcache> notificationCaches;
Singleton<msgcachemanager>::GetInstance().GetNotifications(sessionUserInfo.userId, notificationCaches);
for (const auto& cacheEntry : notificationCaches) {
Send(cacheEntry.notificationMessage);
}
// Push cached chat messages
std::list<chatmsgcache> chatCaches;
Singleton<msgcachemanager>::GetInstance().GetChatMessages(sessionUserInfo.userId, chatCaches);
for (const auto& cacheEntry : chatCaches) {
Send(cacheEntry.chatMessage);
}
// Notify friends about online status
std::list<user> friends;
Singleton<usermanager>::GetInstance().GetFriends(sessionUserInfo.userId, friends);
IMServer& imServer = Singleton<imserver>::GetInstance();
for (const auto& friendInfo : friends) {
std::shared_ptr<clientsession> friendSession;
if (imServer.FindSessionByUserId(friendSession, friendInfo.userId)) {
friendSession->NotifyUserStatusChange(sessionUserInfo.userId, ONLINE_STATUS_ONLINE);
}
}
}
</clientsession></imserver></usermanager></user></msgcachemanager></chatmsgcache></msgcachemanager></notifymsgcache></usermanager>
Get Friend List
This operation retrieves a user's friends from the UserManager. Each friend's information is formatted into a JSON object and sent back to the client.
void ClientSession::HandleGetFriendList()
{
std::list<user> friendList;
Singleton<usermanager>::GetInstance().GetFriends(sessionUserInfo.userId, friendList);
std::string friendInfoJsonArray;
IMServer& imServer = Singleton<imserver>::GetInstance();
for (const auto& friendUser : friendList) {
bool isOnline = imServer.IsUserOnline(friendUser.userId);
// Construct JSON for each friend
// Example: {"userId": 1,"username":"qqq", "nickname":"qqq", "faceType": 0, "customFace":"", "gender":0, "birthday":19900101, "signature":", "address": "", "phoneNumber": "", "email":", "clientType": 1, "status":1}
std::ostringstream friendDetailStream;
friendDetailStream << "{\"userId\": " << friendUser.userId << ",\"username\":\"" << friendUser.username << "\", \"nickname\":\"" << friendUser.nickname
<< "\", \"faceType\": " << friendUser.faceType << ", \"customFace\":\"" << friendUser.customFace << "\", \"gender\":" << friendUser.gender
<< ", \"birthday\":" << friendUser.birthday << ", \"signature\":\"" << friendUser.signature << "\", \"address\": \"" << friendUser.address
<< "\", \"phoneNumber\": \"" << friendUser.phoneNumber << "\", \"email\":\"" << friendUser.email << "\", \"clientType\": 1, \"status\":"
<< (isOnline ? 1 : 0) << "}";
friendInfoJsonArray += friendDetailStream.str();
friendInfoJsonArray += ",";
}
// Remove trailing comma if any
if (!friendInfoJsonArray.empty()) {
friendInfoJsonArray.pop_back();
}
std::ostringstream responseStream;
responseStream << "{\"code\": 0, \"msg\": \"ok\", \"friends\":[" << friendInfoJsonArray << "]}";
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_GETFRIENDLIST); // Assuming MSG_TYPE_GETFRIENDLIST is defined
stream.Write(sequenceNumber);
stream.Write(responseStream.str().c_str(), responseStream.str().length());
stream.Flush();
// LOG_INFO << "Get friend list response: cmd=" << MSG_TYPE_GETFRIENDLIST << ", data=" << responseStream.str() << ", userId=" << sessionUserInfo.userId;
Send(outgoingBuffer);
}
</imserver></usermanager></user>
Find User
This function searches for a user by username in the UserManager. If found, user details are returned; otherwise, an empty list or not-found indicator is sent.
void ClientSession::HandleFindUser(const std::string& jsonData, const std::shared_ptr& connection)
{
// Example JSON: { "searchType": 1, "username" : "zhangyl" }
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON for find user: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (!rootObject["searchType"].isInt() || !rootObject["username"].isString()) {
// LOG_WARN << "Invalid find user JSON fields: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
std::string searchUsername = rootObject["username"].asString();
std::string responseMessage;
// Currently supports finding a single user
User foundUser;
if (!Singleton<usermanager>::GetInstance().FindUserByUsername(searchUsername, foundUser)) {
responseMessage = "{ \"code\": 0, \"msg\": \"ok\", \"users\": [] }";
} else {
// Simplified user info for search result
char userInfoBuffer[256] = { 0 };
snprintf(userInfoBuffer, sizeof(userInfoBuffer), "{ \"code\": 0, \"msg\": \"ok\", \"users\": [{\"userId\": %d, \"username\": \"%s\", \"nickname\": \"%s\", \"faceType\":%d}] }",
foundUser.userId, foundUser.username.c_str(), foundUser.nickname.c_str(), foundUser.faceType);
responseMessage = userInfoBuffer;
}
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_FINDUSER); // Assuming MSG_TYPE_FINDUSER is defined
stream.Write(sequenceNumber);
stream.Write(responseMessage.c_str(), responseMessage.length());
stream.Flush();
// LOG_INFO << "Find user response: cmd=" << MSG_TYPE_FINDUSER << ", data=" << responseMessage << ", userId=" << sessionUserInfo.userId;
Send(outgoingBuffer);
}
</usermanager>
Friend Operations
Handles actions like adding, removing, or responding to friend requests. It differentiates between user operations and group operations (e.g., joining/leaving a group) based on the target ID. For friend requests, it establishes relationships and notifies relevant parties. Offline users' messages are cached.
void ClientSession::HandleFriendOperation(const std::string& jsonData, const std::shared_ptr& connection)
{
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON for friend operation: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (!rootObject["type"].isInt() || !rootObject["targetUserId"].isInt()) {
// LOG_WARN << "Invalid friend operation JSON fields: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
int operationType = rootObject["type"].asInt();
int32_t targetUserId = rootObject["targetUserId"].asInt();
// Group operations (targetUserId >= GROUP_ID_BOUNDARY)
if (targetUserId >= GROUP_ID_BOUNDARY) { // Assuming GROUP_ID_BOUNDARY is defined
if (operationType == OPERATION_LEAVE_GROUP) { // Assuming OPERATION_LEAVE_GROUP is defined (e.g., 4)
RemoveUserFromGroup(connection, targetUserId);
return;
}
// Add to group (auto-accept)
AddUserToGroup(targetUserId, connection);
return;
}
// User operations
char operationDataBuffer[256] = { 0 };
// Remove friend
if (operationType == OPERATION_REMOVE_FRIEND) { // Assuming OPERATION_REMOVE_FRIEND is defined (e.g., 4)
RemoveFriend(connection, targetUserId);
return;
}
// Send friend request
if (operationType == OPERATION_SEND_REQUEST) { // Assuming OPERATION_SEND_REQUEST is defined (e.g., 1)
// Example: {"targetUserId": 9, "type": 2, "fromUserId": "currentUserId", "fromUsername": "currentUsername"}
snprintf(operationDataBuffer, sizeof(operationDataBuffer), "{\"targetUserId\":%d, \"type\":%d, \"fromUserId\": %d, \"fromUsername\": \"%s\"}",
targetUserId, OPERATION_RECEIVE_REQUEST, sessionUserInfo.userId, sessionUserInfo.username.c_str()); // Assuming OPERATION_RECEIVE_REQUEST is defined (e.g., 2)
}
// Respond to friend request
else if (operationType == OPERATION_RESPOND_REQUEST) { // Assuming OPERATION_RESPOND_REQUEST is defined (e.g., 3)
if (!rootObject["accept"].isInt()) {
// LOG_WARN << "Invalid JSON for accept field: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
int acceptStatus = rootObject["accept"].asInt();
if (acceptStatus == 1) { // Accepted
int smallerId = sessionUserInfo.userId;
int greaterId = targetUserId;
if (smallerId > greaterId) {
std::swap(smallerId, greaterId);
}
if (!Singleton<usermanager>::GetInstance().EstablishFriendship(smallerId, greaterId)) {
// LOG_ERROR << "Failed to establish friendship: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
}
// Notify current user of the outcome
snprintf(operationDataBuffer, sizeof(operationDataBuffer), "{\"targetUserId\": %d, \"type\": %d, \"fromUserId\": %d, \"fromUsername\": \"%s\", \"accept\": %d}",
targetUserId, OPERATION_RESPOND_REQUEST, sessionUserInfo.userId, sessionUserInfo.username.c_str(), acceptStatus);
// Notify the target user
std::string outgoingBufferSelf;
yt::BinaryWriteStream3 streamSelf(&outgoingBufferSelf);
streamSelf.Write(MSG_TYPE_FRIENDOPERATION); // Assuming MSG_TYPE_FRIENDOPERATION is defined
streamSelf.Write(sequenceNumber);
streamSelf.Write(operationDataBuffer, strlen(operationDataBuffer));
streamSelf.Flush();
Send(outgoingBufferSelf);
// LOG_INFO << "Friend request response sent to self: cmd=" << MSG_TYPE_FRIENDOPERATION << ", data=" << operationDataBuffer << ", userId=" << sessionUserInfo.userId;
}
// Send response to the target user
std::string outgoingBufferTarget;
yt::BinaryWriteStream3 streamTarget(&outgoingBufferTarget);
streamTarget.Write(MSG_TYPE_FRIENDOPERATION);
streamTarget.Write(sequenceNumber);
streamTarget.Write(operationDataBuffer, strlen(operationDataBuffer));
streamTarget.Flush();
// Cache message if target is offline
std::shared_ptr<clientsession> targetSession;
if (!Singleton<imserver>::GetInstance().FindSessionByUserId(targetSession, targetUserId)) {
Singleton<msgcachemanager>::GetInstance().AddNotificationCache(targetUserId, outgoingBufferTarget);
// LOG_INFO << "Target user " << targetUserId << " offline, caching notification message.";
return;
}
targetSession->Send(outgoingBufferTarget);
// LOG_INFO << "Friend operation sent to target userId: " << targetUserId << ", data=" << operationDataBuffer;
}
// Helper function for removing a friend or leaving a group
void ClientSession::RemoveFriend(const std::shared_ptr& connection, int32_t friendOrGroupId) {
int32_t smallerId = friendOrGroupId;
int32_t greaterId = sessionUserInfo.userId;
if (smallerId > greaterId) {
std::swap(smallerId, greaterId);
}
if (!Singleton<usermanager>::GetInstance().ReleaseFriendship(smallerId, greaterId)) {
// LOG_ERROR << "Failed to remove friend/group member. friendId: " << friendOrGroupId << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
User friendInfo;
if (!Singleton<usermanager>::GetInstance().FindUserById(friendOrGroupId, friendInfo)) {
// LOG_ERROR << "Error retrieving friend info for removal. friendId: " << friendOrGroupId << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
// Notify the user initiating the removal
char removalData[256] = { 0 };
snprintf(removalData, sizeof(removalData), "{\"targetUserId\":%d, \"type\":%d, \"fromUserId\": %d, \"fromUsername\": \"%s\"}",
friendOrGroupId, OPERATION_REMOVE_NOTIFICATION, sessionUserInfo.userId, sessionUserInfo.username.c_str()); // Assuming OPERATION_REMOVE_NOTIFICATION is defined (e.g., 5)
std::string outgoingBufferSelf;
yt::BinaryWriteStream3 streamSelf(&outgoingBufferSelf);
streamSelf.Write(MSG_TYPE_FRIENDOPERATION);
streamSelf.Write(sequenceNumber);
streamSelf.Write(removalData, strlen(removalData));
streamSelf.Flush();
Send(outgoingBufferSelf);
// LOG_INFO << "Sent friend removal notification to self: cmd=" << MSG_TYPE_FRIENDOPERATION << ", data=" << removalData << ", userId=" << sessionUserInfo.userId;
// If it's a user and not a group
if (friendOrGroupId < GROUP_ID_BOUNDARY) {
// Notify the removed friend if they are online
std::shared_ptr<clientsession> targetSession;
if (Singleton<imserver>::GetInstance().FindSessionByUserId(targetSession, friendOrGroupId)) {
memset(removalData, 0, sizeof(removalData));
snprintf(removalData, sizeof(removalData), "{\"targetUserId\":%d, \"type\":%d, \"fromUserId\": %d, \"fromUsername\": \"%s\"}",
sessionUserInfo.userId, OPERATION_REMOVE_NOTIFICATION, friendOrGroupId, friendInfo.username.c_str());
outgoingBufferSelf.clear();
streamSelf.Clear();
streamSelf.Write(MSG_TYPE_FRIENDOPERATION);
streamSelf.Write(sequenceNumber);
streamSelf.Write(removalData, strlen(removalData));
streamSelf.Flush();
targetSession->Send(outgoingBufferSelf);
// LOG_INFO << "Sent friend removal notification to friend: cmd=" << MSG_TYPE_FRIENDOPERATION << ", data=" << removalData << ", userId=" << friendOrGroupId;
}
return;
}
// If it was a group, notify other group members about the change
std::list<user> groupMembers;
Singleton<usermanager>::GetInstance().GetGroupMembers(friendOrGroupId, groupMembers);
IMServer& imServer = Singleton<imserver>::GetInstance();
for (const auto& member : groupMembers) {
std::shared_ptr<clientsession> memberSession;
if (imServer.FindSessionByUserId(memberSession, member.userId)) {
memberSession->NotifyUserStatusChange(friendOrGroupId, USER_STATUS_GROUP_UPDATED); // Assuming USER_STATUS_GROUP_UPDATED is defined
}
}
}
// Helper function for joining a group
void ClientSession::AddUserToGroup(int32_t groupId, const std::shared_ptr& connection) {
if (!Singleton<usermanager>::GetInstance().EstablishFriendship(sessionUserInfo.userId, groupId)) {
// LOG_ERROR << "Failed to join group. groupId: " << groupId << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
User groupInfo;
if (!Singleton<usermanager>::GetInstance().FindUserById(groupId, groupInfo)) { // Assuming group info can be retrieved similarly to user info
// LOG_ERROR << "Failed to retrieve group info. groupId: " << groupId << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
// Notify the user they joined the group
char joinNotification[256] = { 0 };
snprintf(joinNotification, sizeof(joinNotification), "{\"targetUserId\": %d, \"type\": %d, \"fromUserId\": %d, \"fromUsername\": \"%s\", \"accept\": 1}", // Assuming accept value 1 for joining
groupInfo.userId, OPERATION_RESPOND_REQUEST, sessionUserInfo.userId, sessionUserInfo.username.c_str());
std::string outgoingBufferSelf;
yt::BinaryWriteStream3 streamSelf(&outgoingBufferSelf);
streamSelf.Write(MSG_TYPE_FRIENDOPERATION);
streamSelf.Write(sequenceNumber);
streamSelf.Write(joinNotification, strlen(joinNotification));
streamSelf.Flush();
Send(outgoingBufferSelf);
// LOG_INFO << "Sent group join confirmation to self: cmd=" << MSG_TYPE_FRIENDOPERATION << ", data=" << joinNotification << ", userId=" << sessionUserInfo.userId;
// Notify other group members about the new member
std::list<user> groupMembers;
Singleton<usermanager>::GetInstance().GetGroupMembers(groupId, groupMembers);
IMServer& imServer = Singleton<imserver>::GetInstance();
for (const auto& member : groupMembers) {
std::shared_ptr<clientsession> memberSession;
if (imServer.FindSessionByUserId(memberSession, member.userId)) {
memberSession->NotifyUserStatusChange(groupId, USER_STATUS_GROUP_UPDATED); // Notify group update
}
}
}
</clientsession></imserver></usermanager></user></usermanager></usermanager></clientsession></imserver></usermanager></user></imserver></clientsession></usermanager></usermanager></msgcachemanager></imserver></clientsession></usermanager>
Update User Information
Allows users to modify their profile details (nickname, avatar, signature, etc.). The UserManager is updated, and online friends are notified of the changes.
void ClientSession::HandleUpdateUserInfo(const std::string& jsonData, const std::shared_ptr& connection)
{
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON for update user info: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
// Validate required fields for update
if (!rootObject["nickname"].isString() || !rootObject["faceType"].isInt() ||
!rootObject["customFace"].isString() || !rootObject["gender"].isInt() ||
!rootObject["birthday"].isInt() || !rootObject["signature"].isString() ||
!rootObject["address"].isString() || !rootObject["phoneNumber"].isString() ||
!rootObject["email"].isString()) {
// LOG_WARN << "Invalid update user info JSON fields: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
User updatedInfo;
updatedInfo.nickname = rootObject["nickname"].asString();
updatedInfo.faceType = rootObject["faceType"].asInt();
updatedInfo.customFace = rootObject["customFace"].asString();
updatedInfo.gender = rootObject["gender"].asInt();
updatedInfo.birthday = rootObject["birthday"].asInt();
updatedInfo.signature = rootObject["signature"].asString();
updatedInfo.address = rootObject["address"].asString();
updatedInfo.phoneNumber = rootObject["phoneNumber"].asString();
updatedInfo.email = rootObject["email"].asString();
std::ostringstream responseStream;
if (!Singleton<usermanager>::GetInstance().UpdateUserInfo(sessionUserInfo.userId, updatedInfo)) {
responseStream << "{ \"code\": 104, \"msg\": \"Failed to update user information\" }";
} else {
// Update session info if necessary
sessionUserInfo.nickname = updatedInfo.nickname;
sessionUserInfo.faceType = updatedInfo.faceType;
// ... update other relevant session fields
// Construct success response with updated user details
responseStream << "{\"code\": 0, \"msg\": \"ok\", \"userId\": " << sessionUserInfo.userId << ",\"username\":\"" << sessionUserInfo.username
<< "\", \"nickname\":\"" << updatedInfo.nickname
<< "\", \"faceType\": " << updatedInfo.faceType << ", \"customFace\":\"" << updatedInfo.customFace
<< "\", \"gender\":" << updatedInfo.gender
<< ", \"birthday\":" << updatedInfo.birthday << ", \"signature\":\"" << updatedInfo.signature << "\", \"address\": \"" << updatedInfo.address
<< "\", \"phoneNumber\": \"" << updatedInfo.phoneNumber << "\", \"email\":\""
<< updatedInfo.email << "\"}";
}
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_UPDATEUSERINFO); // Assuming MSG_TYPE_UPDATEUSERINFO is defined
stream.Write(sequenceNumber);
stream.Write(responseStream.str().c_str(), responseStream.str().length());
stream.Flush();
Send(outgoingBuffer);
// LOG_INFO << "Update user info response: cmd=" << MSG_TYPE_UPDATEUSERINFO << ", data=" << responseStream.str() << ", userId=" << sessionUserInfo.userId;
// Notify online friends about the profile change
std::list<user> friends;
Singleton<usermanager>::GetInstance().GetFriends(sessionUserInfo.userId, friends);
IMServer& imServer = Singleton<imserver>::GetInstance();
for (const auto& friendInfo : friends) {
std::shared_ptr<clientsession> friendSession;
if (imServer.FindSessionByUserId(friendSession, friendInfo.userId)) {
friendSession->NotifyUserStatusChange(sessionUserInfo.userId, USER_STATUS_PROFILE_UPDATED); // Assuming USER_STATUS_PROFILE_UPDATED is defined
}
}
}
</clientsession></imserver></usermanager></user></usermanager>
Modify Password
Handles password changes. It verifies the old password before allowing the new password to be set via the UserManager.
void ClientSession::HandleModifyPassword(const std::string& jsonData, const std::shared_ptr& connection)
{
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON for password modification: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (!rootObject["oldPassword"].isString() || !rootObject["newPassword"].isString()) {
// LOG_WARN << "Invalid password modification JSON fields: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
std::string oldPassword = rootObject["oldPassword"].asString();
std::string newPassword = rootObject["newPassword"].asString();
std::string responseMessage;
User currentUser;
if (!Singleton<usermanager>::GetInstance().FindUserById(sessionUserInfo.userId, currentUser)) {
// LOG_ERROR << "Failed to retrieve user for password modification. userId: " << sessionUserInfo.userId << ", data: " << jsonData << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (currentUser.password != oldPassword) { // Use hashed password comparison
responseMessage = "{\"code\": 103, \"msg\": \"Incorrect old password\"}";
} else {
if (!Singleton<usermanager>::GetInstance().ModifyUserPassword(sessionUserInfo.userId, newPassword)) {
responseMessage = "{\"code\": 105, \"msg\": \"Password modification error\"}";
// LOG_ERROR << "Password modification failed. userId: " << sessionUserInfo.userId << ", data: " << jsonData << ", client: " << connection->peerAddress().toIpPort();
} else {
responseMessage = "{\"code\": 0, \"msg\": \"ok\"}";
// Update session password if needed, but consider security implications
// sessionUserInfo.password = newPassword;
}
}
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_MODIFYPASSWORD); // Assuming MSG_TYPE_MODIFYPASSWORD is defined
stream.Write(sequenceNumber);
stream.Write(responseMessage.c_str(), responseMessage.length());
stream.Flush();
Send(outgoingBuffer);
// LOG_INFO << "Password modification response: cmd=" << MSG_TYPE_MODIFYPASSWORD << ", data=" << responseMessage << ", userId=" << sessionUserInfo.userId;
}
</usermanager></usermanager>
Create Group
Allows users to create new groups. The group name is parsed, and the group is added via UserManager. The creator is automatically added to the group, and confirmation messages are sent.
void ClientSession::HandleCreateGroup(const std::string& jsonData, const std::shared_ptr& connection)
{
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON for group creation: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (!rootObject["groupName"].isString()) {
// LOG_WARN << "Invalid group creation JSON fields: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
std::ostringstream responseStream;
std::string groupName = rootObject["groupName"].asString();
int32_t newGroupId;
if (!Singleton<usermanager>::GetInstance().AddGroup(groupName, sessionUserInfo.userId, newGroupId)) {
// LOG_WARN << "Failed to add group. Data: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
responseStream << "{ \"code\": 106, \"msg\" : \"Group creation failed\"}";
} else {
// Creator automatically joins the group
if (!Singleton<usermanager>::GetInstance().EstablishFriendship(sessionUserInfo.userId, newGroupId)) {
// LOG_ERROR << "Failed to add creator to new group. Data: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
// Potentially rollback group creation or handle error
}
responseStream << "{\"code\": 0, \"msg\": \"ok\", \"groupId\":" << newGroupId << ", \"groupName\": \"" << groupName << "\"}";
}
// Respond with group creation success
std::string outgoingBufferCreate;
yt::BinaryWriteStream3 streamCreate(&outgoingBufferCreate);
streamCreate.Write(MSG_TYPE_CREATEGROUP); // Assuming MSG_TYPE_CREATEGROUP is defined
streamCreate.Write(sequenceNumber);
streamCreate.Write(responseStream.str().c_str(), responseStream.str().length());
streamCreate.Flush();
Send(outgoingBufferCreate);
// LOG_INFO << "Group creation response: cmd=" << MSG_TYPE_CREATEGROUP << ", data=" << responseStream.str() << ", userId=" << sessionUserInfo.userId;
// Respond with joining group success (type 3 for friend operation, accept value 1 for join)
char joinNotification[256] = { 0 };
snprintf(joinNotification, sizeof(joinNotification), "{\"targetUserId\": %d, \"type\": %d, \"fromUserId\": %d, \"fromUsername\": \"%s\", \"accept\": 1}",
newGroupId, OPERATION_RESPOND_REQUEST, sessionUserInfo.userId, sessionUserInfo.username.c_str());
std::string outgoingBufferJoin;
yt::BinaryWriteStream3 streamJoin(&outgoingBufferJoin);
streamJoin.Write(MSG_TYPE_FRIENDOPERATION); // Re-using friend operation type for join notification
streamJoin.Write(sequenceNumber);
streamJoin.Write(joinNotification, strlen(joinNotification));
streamJoin.Flush();
Send(outgoingBufferJoin);
// LOG_INFO << "Sent group join notification: cmd=" << MSG_TYPE_FRIENDOPERATION << ", data=" << joinNotification << ", userId=" << sessionUserInfo.userId;
}
</usermanager></usermanager>
Get Group Members
Similar to retrieving friend lists, this fetches members of a specified group from the UserManager.
void ClientSession::HandleGetGroupMembers(const std::string& jsonData, const std::shared_ptr& connection)
{
// Example JSON: {"groupId": groupID}
Json::Reader jsonParser;
Json::Value rootObject;
if (!jsonParser.parse(jsonData, rootObject)) {
// LOG_WARN << "Invalid JSON for get group members: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
if (!rootObject["groupId"].isInt()) {
// LOG_WARN << "Invalid get group members JSON fields: " << jsonData << ", userId: " << sessionUserInfo.userId << ", client: " << connection->peerAddress().toIpPort();
return;
}
int32_t groupId = rootObject["groupId"].asInt();
std::list<user> groupMembers;
Singleton<usermanager>::GetInstance().GetGroupMembers(groupId, groupMembers); // Assuming GetGroupMembers exists
std::string membersInfoJsonArray;
IMServer& imServer = Singleton<imserver>::GetInstance();
for (const auto& member : groupMembers) {
bool isOnline = imServer.IsUserOnline(member.userId);
// Construct JSON for each member
// Example: {"userId": 1,"username":"qqq", "nickname":"qqq", "faceType": 0, "customFace":"", "gender":0, "birthday":19900101, "signature":", "address": "", "phoneNumber": "", "email":", "clientType": 1, "status":1}
std::ostringstream memberDetailStream;
memberDetailStream << "{\"userId\": " << member.userId << ",\"username\":\"" << member.username << "\", \"nickname\":\"" << member.nickname
<< "\", \"faceType\": " << member.faceType << ", \"customFace\":\"" << member.customFace << "\", \"gender\":" << member.gender
<< ", \"birthday\":" << member.birthday << ", \"signature\":\"" << member.signature << "\", \"address\": \"" << member.address
<< "\", \"phoneNumber\": \"" << member.phoneNumber << "\", \"email\":\"" << member.email << "\", \"clientType\": 1, \"status\":"
<< (isOnline ? 1 : 0) << "}";
membersInfoJsonArray += memberDetailStream.str();
membersInfoJsonArray += ",";
}
if (!membersInfoJsonArray.empty()) {
membersInfoJsonArray.pop_back(); // Remove trailing comma
}
std::ostringstream responseStream;
responseStream << "{\"code\": 0, \"msg\": \"ok\", \"groupId\": " << groupId << ", \"members\":[" << membersInfoJsonArray << "]}";
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_GETGROUPMEMBERS); // Assuming MSG_TYPE_GETGROUPMEMBERS is defined
stream.Write(sequenceNumber);
stream.Write(responseStream.str().c_str(), responseStream.str().length());
stream.Flush();
// LOG_INFO << "Get group members response: cmd=" << MSG_TYPE_GETGROUPMEMBERS << ", data=" << responseStream.str() << ", userId=" << sessionUserInfo.userId;
Send(outgoingBuffer);
}
</imserver></usermanager></user>
Send User Status Change Message
Notifies other users about a user's status changes (online, offline, profile updates).
void ClientSession::NotifyUserStatusChange(int32_t affectedUserId, int statusType)
{
std::string statusData;
switch (statusType) {
case USER_STATUS_ONLINE: // Assuming USER_STATUS_ONLINE is 1
statusData = "{\"type\": 1, \"onlineStatus\": 1}";
break;
case USER_STATUS_OFFLINE: // Assuming USER_STATUS_OFFLINE is 2
statusData = "{\"type\": 2, \"onlineStatus\": 0}";
break;
case USER_STATUS_PROFILE_UPDATED: // Assuming USER_STATUS_PROFILE_UPDATED is 3
statusData = "{\"type\": 3}";
break;
case USER_STATUS_GROUP_UPDATED: // For group member changes
statusData = "{\"type\": 4}"; // Example type for group update
break;
default:
// LOG_WARN << "Unknown status type: " << statusType;
return;
}
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_USERSTATUSCHANGE); // Assuming MSG_TYPE_USERSTATUSCHANGE is defined
stream.Write(sequenceNumber);
stream.Write(statusData.c_str(), statusData.length());
stream.Write(affectedUserId); // The user whose status changed
stream.Flush();
Send(outgoingBuffer);
// LOG_INFO << "Sent user status change: cmd=" << MSG_TYPE_USERSTATUSCHANGE << ", data=" << statusData << ", affectedUserId=" << affectedUserId << ", fromUserId=" << sessionUserInfo.userId;
}
Send Chat Message
Handles sending both direct messages and group messages. Messages are saved to the database. If the recipient is offline, the message is cached. Online recipients receive the message directly.
void ClientSession::HandleChatMessage(int32_t targetId, const std::string& messageData, const std::shared_ptr& connection)
{
std::string outgoingBuffer;
yt::BinaryWriteStream3 stream(&outgoingBuffer);
stream.Write(MSG_TYPE_CHAT); // Assuming MSG_TYPE_CHAT is defined
stream.Write(sequenceNumber);
stream.Write(messageData.c_str(), messageData.length());
stream.Write(sessionUserInfo.userId); // Sender ID
stream.Write(targetId); // Receiver ID (user or group)
stream.Flush();
// Save message to database
UserManager& userMgr = Singleton<usermanager>::GetInstance();
if (!userMgr.SaveChatMessage(sessionUserInfo.userId, targetId, messageData)) {
// LOG_ERROR << "Failed to save chat message to DB. Sender: " << sessionUserInfo.userId << ", Target: " << targetId << ", Message: " << messageData;
}
IMServer& imServer = Singleton<imserver>::GetInstance();
MsgCacheManager& msgCacheMgr = Singleton<msgcachemanager>::GetInstance();
// Direct message
if (targetId < GROUP_ID_BOUNDARY) {
std::shared_ptr<clientsession> targetSession;
if (imServer.FindSessionByUserId(targetSession, targetId)) {
targetSession->Send(outgoingBuffer);
} else {
// Recipient offline, cache the message
msgCacheMgr.AddChatMessageCache(targetId, outgoingBuffer);
}
return;
}
// Group message
std::list<user> groupMembers;
userMgr.GetGroupMembers(targetId, groupMembers); // Assuming GetGroupMembers exists
for (const auto& member : groupMembers) {
// Skip sending to self if applicable, or handle sender logic within the loop
if (member.userId == sessionUserInfo.userId) continue;
std::shared_ptr<clientsession> memberSession;
if (imServer.FindSessionByUserId(memberSession, member.userId)) {
memberSession->Send(outgoingBuffer);
} else {
// Member offline, cache the message
msgCacheMgr.AddChatMessageCache(member.userId, outgoingBuffer);
}
}
}
</clientsession></user></clientsession></msgcachemanager></imserver></usermanager>