集群服务器项目(五)
本章主要实现客户端的需求
客户端主要的流程为:
客户端分 2 个线程,一个main线程,一个子线程,主线程主要负责接受用户的数据并传输给服务器以及从服务器接受数据,子线程主要是接受其他人聊天的数据。通过一个switch来接受用户的输入:
// 显示首页面菜单 登录、注册、退出
cout << "========================" << endl;
cout << "1. login" << endl;
cout << "2. register" << endl;
cout << "3. quit" << endl;
cout << "========================" << endl;
1 login
int id = 0;
char pwd[50] = {0};
cout << "userid:";
cin >> id;
cin.get(); // 读掉缓冲区残留的回车
cout << "userpassword:";
cin.getline(pwd, 50);
json js;
js["msgid"] = LOGIN_MSG;
js["id"] = id;
js["password"] = pwd;
string request = js.dump();
int len = send(clientfd, request.c_str(), strlen(request.c_str()) + 1, 0);
服务器通过接受到的request来判断是否登录成功,若登录成功进入主界面
// 进入聊天主菜单页面
isMainMenuRunning = true;
mainMenu(clientfd);
在进入聊天页面之前,客户端会获取当前用户的好友列表,群组列表,以及是否有离线消息,然后当用户登录时候会自动显示,其代码如下:
// 显示当前登录成功用户的基本信息
void showCurrentUserData()
{
cout << "======================login user======================" << endl;
cout << "current login user => id:" << g_currentUser.getId() << " name:" << g_currentUser.getName() << endl;
cout << "----------------------friend list---------------------" << endl;
if (!g_currentUserFriendList.empty())
{
for (User &user : g_currentUserFriendList)
{
cout << user.getId() << " " << user.getName() << " " << user.getState() << endl;
}
}
cout << "----------------------group list----------------------" << endl;
if (!g_currentUserGroupList.empty())
{
for (Group &group : g_currentUserGroupList)
{
cout << group.getId() << " " << group.getName() << " " << group.getDesc() << endl;
for (GroupUser &user : group.getUsers())
{
cout << user.getId() << " " << user.getName() << " " << user.getState()
<< " " << user.getRole() << endl;
}
}
}
cout << "======================================================" << endl;
}
然后进入聊天主页面程序,其中聊天主页面程序使用一个unordered_map来保存用户输入信息和对应的function,代码如下,当用户输入字符串时候,自动调用对应的函数,实现不同的功能,每个功能的实现在此不再详细说明,基本可以按照前面服务器编写的过程来编写对应的客户端程序。
// 注册系统支持的客户端命令处理
unordered_map<string, function<void(int, string)>> commandHandlerMap = {
{"help", help},
{"chat", chat},
{"addfriend", addfriend},
{"creategroup", creategroup},
{"addgroup", addgroup},
{"groupchat", groupchat},
{"loginout", loginout}};
2 register
和登录一样,用户按照规定格式进行输入,然后利用json进行解析
char name[50] = {0};
char pwd[50] = {0};
cout << "username:";
cin.getline(name, 50); // 没有用cin>> 因为这样遇到空格就会终止,比如输入zhang san,就只会得到zhang
cout << "userpassword:";
cin.getline(pwd, 50);
json js;
js["msgid"] = REG_MSG;
js["name"] = name;
js["password"] = pwd;
string request = js.dump();
子线程
子线程主要用于接受聊天内容,其主要代码为:
// 接收线程
void readTaskHandler(int clientfd)
{
for (;;)
{
char buffer[1024] = {0};
int len = recv(clientfd, buffer, 1024, 0); // 阻塞了
if (-1 == len || 0 == len)
{
close(clientfd);
exit(-1);
}
// 接收ChatServer转发的数据,反序列化生成json数据对象
json js = json::parse(buffer);
int msgtype = js["msgid"].get<int>();
if (ONE_CHAT_MSG == msgtype)
{
cout << js["time"].get<string>() << " [" << js["id"] << "]" << js["name"].get<string>()
<< " said: " << js["msg"].get<string>() << endl;
continue;
}
if (GROUP_CHAT_MSG == msgtype)
{
cout << "群消息[" << js["groupid"] << "]:" << js["time"].get<string>() << " [" << js["id"] << "]" << js["name"].get<string>()
<< " said: " << js["msg"].get<string>() << endl;
continue;
}
}
}
本项目可以利用不同的消息id来识别不同的聊天信息(一对一还是群组聊天),至此基本的客户端和服务端基本完成搭建,可以直接运行,后面主要利用redis和nginx来扩充该项目