当前位置: 首页 > article >正文

集群聊天服务器(11)客户端开发

目录

  • 首页面功能开发
  • 添加好友和聊天
    • 帮助和添加好友
    • 聊天功能
    • 创建群组
    • 添加群组
    • 群组聊天
    • 退出
  • 测试问题
    • 一对一聊天
    • 第一次发送两个离线消息只收到一个
    • 创建和加入群组

首页面功能开发

#include "json.hpp"
#include <iostream>
#include <thread>
#include <string>
#include <vector>
#include <chrono>
#include <ctime>
using namespace std;
using json=nlohmann::json;

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "group.hpp"
#include "user.hpp"
#include "public.hpp"

//记录当前系统登录的用户信息
User g_currentUser;
//记录当前登录用户的好友列表信息
vector<User> g_currentUserFriendList;
//记录当前登录用户的群组列表信息
vector<Group> g_currentUserGroupList;
//显示当前登录成功用户的基本信息
void showCurrentUserData();

//接收线程
void readTaskHandler(int clientfd);
//获取系统时间(聊天信息需要添加时间信息)
string getCurrentTime();
//主聊天页面程序
void mainMenu();


//聊天客户端程序实现,main线程用作发送线程,子线程用作接收线程
int main(int argc,char **argv)
{
    if(argc<3)
    {
        cerr<<"command invalid! example: ./ChatClient 127.0.0.1 6000"<<endl;
        exit(-1);
    }

    //解析通过命令行参数传递的ip和port
    char *ip=argv[1];
    uint16_t port=atoi(argv[2]);

    //创建client端的socket
    int clientfd=socket(AF_INET,SOCK_STREAM,0);
    if(-1==clientfd)
    {
        cerr<<"socket create error"<<endl;
        exit(-1);
    }

    //填写client需要连接的server信息ip+port
    sockaddr_in server;
    memset(&server,0,sizeof(sockaddr_in));

    server.sin_family=AF_INET;
    server.sin_port=htons(port);
    server.sin_addr.s_addr=inet_addr(ip);

    //client和server进行连接
    if(-1==connect(clientfd,(sockaddr *)&server,sizeof(sockaddr_in)))
    {
        cerr<<"connect server error"<<endl;
        close(clientfd);
        exit(-1);
    }

    //main线程用于接收用户输入,负责发送数据
    for(;;)
    {
        //显示首页面菜单 登录、注册、退出
        cout<<"========================="<<endl;
        cout<<"1.login"<<endl;
        cout<<"2.register"<<endl;
        cout<<"3.quit"<<endl;
        cout<<"========================="<<endl;
        cout<<"choice:";
        int choice=0;
        cin>>choice;
        cin.get();//读掉缓冲区残留的回车

        switch(choice)
        {
        case 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);
            if(len==-1)
            {
                cerr<<"send login msg error:"<<request<<endl;
            }
            else{
                char buffer[1024]={0};
                len =recv(clientfd,buffer,1024,0);
                if(-1==len)
                {
                    cerr<<"recv login response error"<<endl;
                }
                else{
                    json responsejs =json::parse(buffer);
                    if(0!=responsejs["errno"].get<int>())//登录失败
                    {
                        cerr<<responsejs["errmsg"]<<endl;
                    }
                    else//登录成功
                    {
                        //记录当前用户的id和name
                        g_currentUser.setId(responsejs["id"].get<int>());
                        g_currentUser.setName(responsejs["name"]);

                        //记录当前用户的好友列表信息
                        if(responsejs.contains("friends"))
                        {
                            vector<string> vec=responsejs["friends"];
                            for (string &str:vec)
                            {
                                json js=json::parse(str);
                                User user;
                                user.setId(js["id"].get<int>());
                                user.setName(js["name"]);
                                user.setState(js["state"]);
                                g_currentUserFriendList.push_back(user);
                            }
                        }

                        //记录当前用户的群组列表信息
                        if(responsejs.contains("groups"))
                        {
                            vector<string> vec1=responsejs["groups"];
                            for(string &groupstr:vec1)
                            {
                                json grpjs=json::parse(groupstr);
                                Group group;
                                group.setId(grpjs["id"].get<int>());
                                group.setName(grpjs["groupname"]);
                                group.setDesc(grpjs["groupdesc"]);

                                vector<string> vec2=grpjs["users"];
                                for (string &userstr:vec2)
                                {
                                    GroupUser user;
                                    json js=json::parse(userstr);
                                    user.setId(js["id"].get<int>());
                                    user.setName(js["name"]);
                                    user.setState(js["state"]);
                                    user.setRole(js["role"]);
                                    group.getUsers().push_back(user);
                                }

                                g_currentUserGroupList.push_back(group);
                            }
                        }
                        //显示登录用户的基本信息
                        showCurrentUserData();

                        //显示当前用户的离线消息 个人聊天或者群组消息
                        if(responsejs.contains("offlinemsg"))
                        {
                            vector<string> vec=responsejs["offlinemsg"];
                            for(string &str:vec)
                            {
                                json js=json::parse(str);
                                //time+[id]+name+"said:"+xxx
                                cout<<js["time"]<<"["<<js["id"]<<"]"<<js["name"]<<"said:"<<js["msg"]<<endl;
                            }
                        }
                        //登录成功
                        std::thread readTask(readTaskHandler,clientfd);
                        readTask.detach();
                        //进入聊天主菜单页面
                        mainMenu();
                    }
                }
            }

        }
        break;
        case 2://register业务
        {
            char name[50]={0};
            char pwd[50]={0};
            cout<<"username:";
            cin.getline(name,50);
            cout<<"userpassword:";
            cin.getline(pwd,50);

            json js;
            js["msgid"]=REG_MSG;
            js["name"]=name;
            js["password"]=pwd;
            string request =js.dump();

            int len=send(clientfd,request.c_str(),strlen(request.c_str())+1,0);
            if(len==-1)
            {
                cerr<<"send reg msg error:"<<request<<endl;
            }
            else{
                char buffer[1024]={0};
                len=recv(clientfd,buffer,1024,0);
                if(-1==len)
                {
                    cerr<<"recv reg response error"<<endl;
                }
                else{
                    json responsejs =json::parse(buffer);
                    if(0!=responsejs["errno"].get<int>())//注册失败
                    {
                        cerr<<name<<" is already exist,register error!"<<endl;
                    }
                    else//注册成功
                    {
                        cout<<name<<"register success,userid is"<<responsejs["id"]<<",do not forget it!"<<endl;
                    }
                    
                }
            }
        }
        break;
        case 3://quit业务
            close(clientfd);
            exit(0);
        default:
            cerr<<"invalid input!"<<endl;
            break;
        }
    }

    return 0;

}

//接收线程
void readTaskHandler(int clientfd)
{

}
//主聊天页面程序
void mainMenu()
{

}

//显示当前登录成功用户的基本信息
void showCurrentUserData()
{
    cout<<"==========================login user==========================="<<endl;
    cout<<"current login user=> id:"<<g_currentUser.getId()<<"name:"<<g_currentUser.getName();
    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;
}

string getCurrentTime()
{
    auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    struct tm *ptm = localtime(&tt);
    char date[60] = {0};
    sprintf(date, "%d-%02d-%02d %02d:%02d:%02d",
            (int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,
            (int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec);
    return std::string(date);
}

在这里插入图片描述

添加好友和聊天

其实就是之前只做服务器时,需要自己写json请求给服务器反序列化,而写客户端就是自己制作json,获取用户输入然后json序列化然后发给服务器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

帮助和添加好友

在这里插入图片描述

聊天功能

在这里插入图片描述

创建群组

在这里插入图片描述

添加群组

在这里插入图片描述

群组聊天

在这里插入图片描述

退出

在这里插入图片描述

测试问题

发现我发消息时报段错误,和类型相关的
在这里插入图片描述
然后我发现是两边的json不对等,一个是toid,一个是to
在这里插入图片描述

在这里插入图片描述
所以说客户端制作的js和服务器端一定要一样
这样就可以接收离线消息
在这里插入图片描述

一对一聊天

在这里插入图片描述

第一次发送两个离线消息只收到一个

在这里插入图片描述
一个是数据库表设计离线消息表的时候,不能将userid设为主键,这样的话只会出现一个消息,离线时同时一个user发几个也只会有一个
在这里插入图片描述
对于我自己来说,是服务器db那边代码用中文乱码报错了,没有转成utf-8
现在好了
然后也能够体会到,在服务器开发的时候,一定要有日志,错误提示,这样遇到问题很好定位

创建和加入群组

在这里插入图片描述


http://www.kler.cn/a/401009.html

相关文章:

  • 【东莞石碣】戴尔R740服务器维修raid硬盘问题
  • TON商城与Telegram App:生态融合与去中心化未来的精彩碰撞
  • 5.4.2-1 编写Java程序在HDFS上创建文件
  • AWD脚本编写_1
  • Debezium-MySqlConnectorTask
  • 智慧社区平台系统提升物业管理效率与居民生活质量
  • 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04
  • 小程序-基于java+SpringBoot+Vue的实习生管理系统设计与实现
  • 网络安全等级测评师
  • vue3的setup、ref、reactive
  • RDD转换算子:join方面的算子(leftOuterJoin、rightOuterJoin、fullOuterJoin)
  • Spring Boot汽车资讯:驾驭信息新浪潮
  • 【网络安全 | 漏洞挖掘】在重置密码流程利用请求头实现ATO
  • 软件测试丨Java常用库
  • webgis与纯前端相比如何?
  • 网络传输:网卡、IP、网关、子网掩码、MAC、ARP、路由器、NAT、交换机
  • 【pytest】pytest注解使用指南
  • AI 技术在旅游和酒店行业的应用前景
  • 如何利用WebSockets实现高效的实时通信应用
  • docker 安装之 windows安装
  • MySQL深度剖析-索引原理由浅入深
  • Qt-QWidget中的属性和方法
  • 【Oracle篇】掌握SQL Tuning Advisor优化工具:从工具使用到SQL优化的全方位指南(第六篇,总共七篇)
  • 【第六课】Rust所有权系统(二)
  • 【网络安全面经】技术性问题3
  • 基于Spring Boot+Vue的多媒体素材管理系统的设计与实现