您的位置:首页 > 运维架构 > Linux

在linux下实现简单聊天系统(二)客户端的具体实现

2018-03-07 21:05 405 查看
客户端实现的具体内容:利用多线程实现,采取输入命令的方式让用户选择服务类型,根据不同的服务类型调用不同的处理函数,完成请求,利用json和自定义的上层协议完成和服务器端的数据的交互。在登陆成功之后启动一个线程用于接受服务器端的消息。
通过socket进行服务器连接,之后绑定信号处理函数,让用户选择“注册”,“登陆”,“推出”等操作。
其中操作具体实现都用json包进行发送,在服务器端接收后,进行解析,可以创建在客户端接收服务器端发回的消息,从而来确认数据的发送是否成功。
在“登陆”成功的操作中,启动一个线程用来专门接收服务器端的消息,并且在里面实现用户选择“一对一聊天”,“群聊”,“获取好友列表”,“退出”的操作。
其中在"一对一聊天"函数中,通过用json来输入聊天对象和聊天内容从而达到对聊天内容的打包,在函数中也可以创建一个buff用来接收发送过去的数据,来查看是否发送成功。"群聊"与"退出"的原理等同"一对一聊天"。
虽然我们完成了客户端基本功能的实现,但我们在现实聊天中,例如qq的退出方式大多数人都不一定选择手动退出,而是强制关闭,因此我们需要在客户端中加入一个信号处理函数,通过ctrl+c来强制结束聊天进程。因此我们的这个signal信号处理函数就要在链接服务器的阶段加入。
通过以上的框架,我们可以实现的代码如下:

cli.h#ifndef CLI_H
#define CLI_H
void run(char *ip,short port);

void do_register(int clifd);

void do_login(int clifd);

void *pth_run(void *arg);

void do_talk_one(int clifd);

void do_talk_group(int clifd);

void do_get_list(int clifd);

void do_exit(int clifd);

#endifpublic.h#ifndef PUBLIC_H
#define PUBLIC_H

enum type
{
REASON_TYPE_REGISTER,
REASON_TYPE_LOGIN,
REASON_TYPE_GETLIST,
REASON_TYPE_TALK_ONE,
REASON_TYPE_TALK_GROUP,
REASON_TYPE_EXIT
};

#endif
cli.cpp#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <json/json.h>
#include <event.h>
#include <string>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include "cli.h"
#include "public.h"
using namespace std;

void run(char *ip, short port)
{
//创建套接字
int cli_fd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cli_fd)
{
cerr<<"create cli_fd fail; errno:"<<errno<<endl;
return ;
}
cout<<"cli_fd = "<<cli_fd<<endl;

struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = inet_addr(ip);

//链接服务器
int res = connect(cli_fd, (struct sockaddr*)&saddr, sizeof(saddr));
if(-1 == res)
{
cerr<<"connect fail; errno:"<<errno<<endl;
return ;
}

//绑定信号处理函数
//SIG_DFL恢复默认行为,SIGINT默认情况下按Ctrl+c产生该符号
signal(SIGINT, SIG_DFL);

//让用户选择服务
while(1)
{
cout<<"1.register"<<endl;
cout<<"2.login"<<endl;
cout<<"3.exit"<<endl;

int a;
cin>>a;
switch(a)
{
case 1:
do_register(cli_fd);
break;
case 2:
do_login(cli_fd);
break;
case 3:
do_exit(cli_fd);
break;
}
}
}

void do_register(int cli_fd)
{
//提示用户输入注册名,密码
Json::Value val;
val["Msg_Type"] = REASON_TYPE_REGISTER;

char buff[128] = {0};
cout<<"please input username: ";
cin>>buff;
val["name"] = buff;

memset(buff, 0, 128);
cout<<"please input password: ";
cin>>buff;
val["pw"] = buff;

//将json包发送给服务器
if(0 >= send(cli_fd, val.toStyledString().c_str(),
strlen(val.toStyledString().c_str()), 0))
{
cerr<<"send fail; errno:"<<errno<<endl;
return ;
}

cout<<"send cli_fd json to ser, cli_fd = "<<cli_fd<<endl;
//接受服务器消息
memset(buff, 0, 128);
if(0 >= recv(cli_fd, buff, 127, 0))
{
cerr<<"cli register recv fail; errno:"<<errno<<endl;
return ;
}

Json::Value sval;
Json::Reader read;

if(-1 == read.parse(buff, sval))
{
cerr<<"cli register parse fail; errno:"<<errno<<endl;
return ;
}
cout<<sval["Responce"].asString()<<endl;
}

void do_login(int cli_fd)
{
//提示用户输入用户名,密码
char buff[128] = {0};
Json::Value val;
val["Msg_Type"] = REASON_TYPE_LOGIN;
cout<<"Please input username:"<<" ";
cin>>buff;
val["name"] = buff;

memset(buff, 0, 128);
cout<<"Please input password:"<<" ";
cin>>buff;
val["pw"] = buff;

//给服务器发送数据
if(0 >= send(cli_fd, val.toStyledString().c_str(),
strlen(val.toStyledString().c_str()), 0))
{
cerr<<"cli login send fail; errno:"<<errno<<endl;
return ;
}

cout<<"send cli_fd json_login to ser; cli_fd = "<<cli_fd<<endl;
memset(buff, 0, 128);
//接收服务器端的消息
if(0 >= recv(cli_fd, buff, 127, 0))
{
cout<<"cli login recv fail; errno:"<<errno<<endl;
return ;
}

Json::Value sval;
Json::Reader read;

if(-1 == read.parse(buff, sval))
{
cerr<<"cli login parse fail; errno:"<<errno<<endl;
return ;
}

cout<<"sval[Responce] = "<<sval["Responce"].asString()<<endl;

char buf[128] = {0};
string responce = sval["Responce"].asString();
copy(responce.begin(), responce.end(), buf);
cout<<"buf : "<<buf<<endl;
if(strncmp(buf, "OK", 2) == 0)
{
//启动一个线程专门接收服务端的消息
pthread_t id;
int s = pthread_create(&id, NULL, pth_run, (void*)cli_fd);
if(0 != s)
{
cout<<"pthread create fail; errno:"<<errno<<endl;
return ;
}

while(1)
{
//提示用户选择服务
cout<<"1.get list"<<endl;
cout<<"2.talk to one"<<endl;
cout<<"3.talk to group"<<endl;
cout<<"4.exit"<<endl;

int a;
cin>>a;
switch(a)
{
case 1:
do_get_list(cli_fd);
case 2:
do_talk_one(cli_fd);
case 3:
do_talk_group(cli_fd);
case 4:
do_exit(cli_fd);
}
}
}
}

void *pth_run(void *arg)
{
int cli_fd = (int)arg;
cout<<"cli pth_run"<<endl;
while(1)
{
//接受服务器端消息
char buff[128] = {0};
int n = recv(cli_fd, buff, 127, 0);
//输出
if(0 < n)
{
cout<<buff<<endl;
}
}
cout<<"pth exit"<<endl;
pthread_exit(NULL);
}

void do_get_list(int cli_fd)
{
Json::Value val;
val["Msg_Type"] = REASON_TYPE_GETLIST;

if(-1 == send(cli_f
4000
d, val.toStyledString().c_str(), strlen(val.toStyledString().c_str()), 0))
{
cerr<<"cli send exit fail; errno:"<<errno<<endl;
return ;
}
}

void do_talk_one(int cli_fd)
{
//打包
Json::Value val;
val["Msg_Type"] = REASON_TYPE_TALK_ONE;
//输入聊天对象
char buff[1024] = {0};
cin>>buff;
val["Friend"] = buff;

//输入聊天内容
memset(buff, 0, 1023);
cin>>buff;
val["Message"] = buff;
//发送
if(-1 == send(cli_fd, val.toStyledString().c_str(), strlen(val.toStyledString().c_str()), 0))
{
cerr<<"cli send exit fail; errno:"<<errno<<endl;
return ;
}
}

void do_talk_group(int cli_fd)
{
Json::Value val;
val["Msg_Type"] = REASON_TYPE_TALK_GROUP;
//输入好友
char buff[1024] = {0};
cin>>buff;
val["Friends"] = buff;

memset(buff, 0, 1023);

//输入聊天内容
cin>>buff;
val["Message"] = buff;

//发送
if(-1 == send(cli_fd, val.toStyledString().c_str(), strlen(val.toStyledString().c_str()), 0))
{
cerr<<"cli send exit fail; errno:"<<errno<<endl;
return ;
}
}

void do_exit(int cli_fd)
{
//给服务器发送退出请求
Json::Value val;
val["Msg_Type"] = REASON_TYPE_EXIT;
if(-1 == send(cli_fd, val.toStyledString().c_str(), strlen(val.toStyledString().c_str()), 0))
{
cerr<<"cli send exit fail; errno:"<<errno<<endl;
return ;
}
}
main.cpp#include <iostream>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include "cli.h"
using namespace std;

int main(int argc, char **argv)
{
//main函数参数 服务器的ip地址 端口号port
if(argc < 3)
{
cout<<"arg not enough!"<<endl;
return 0;
}

//参数解析,解析出ip和port
char ip[256] = {0};
strcat(ip, argv[1]);
short port = atoi(argv[2]);
/*int i = 0;
for(i=1; i<argc; ++i)
{
if(6 > strlen(argv[i]))
{
port = (short)(atoi(argv[i]));
}
strcat(ip, argv[i]);
}*/

run(ip, port);
return 0;
}
在以上的代码变编写完成后,为了时我们每次的链接方便,因此我们写一个makefile文件从而进行自动编译。
makefile.cppmain:main.o cli.o
g++ -o main main.o cli.o -ljson -lpthread
main.o:main.cpp
g++ -c main.cpp
cli.o:cli.cpp cli.h public.h
g++ -c cli.cpp
clean:
rm *.o main
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  json 聊天 linux