第一个项目:基于TCP的聊天项目
来源:互联网 发布:英国脱欧最新进展 知乎 编辑:程序博客网 时间:2024/06/01 18:26
主要功能实现:
Register Login TalkToOne TalkToGroup GetList Exit。
使用工具:
Json打包数据 Libevent实现I/O复用 mysql数据库存储用户相关信息及离线消息。
项目框架:
Cli:
提供用户选择服务:Register、Login、Geilist、Talk to one、Talk to group、exit。
用户需选择服务,输入相关信息
根据不同服务调用相应功能函数
利用json打包数据发送给服务器
接收到服务器的回应后,若登陆成功,需要启动线程接收消息并打印
用户再次选择之后的服务。
Ser:
利用半同步半反应堆完成线程间任务分工
主线程接收连接,将套接字分给当前较为空闲的子线程监听
通过socket_pair实现父子线程间信息交互
子线程通过libevent实现I/O复用,当有事件来的时候通过MVC处理
根据请求类型的不同,使用相应的视图来处理。
感谢老师梳理的框架030
项目实现:
Cli部分代码:
//fun.cpp
string myname;
void *pth_run(void *arg)
{
int c = (int) arg;
char buff[1024] = {0};
int n = recv(c,buff,1023,0);
while(n != 0)
{
cout<<"\033[36m"<<buff<<"\n\033[0m"<<endl;
memset(buff,0,1024);
n = recv(c,buff,1023,0);
}
}
void do_register(int fd)
{
string name;
string pw;
//让用户输入name pw
cout<<"--> your name: "<<endl;
cin>>name;
cout<<"--> your password: "<<endl;
cin>>pw;
//JSON 打包数据
Json::Value val;
val["reason_type"] = MSG_TYPE_REGISTER;
val["name"] = name;
val["pw"] = pw;
//发送到服务器
if(-1 == send(fd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
{
cerr<<"//send reason fail;errno:"<<errno<<endl;
return;
}
//接受服务器消息(判断success/error)
char buff[256] = {0};
if(0 < recv(fd,buff,255,0))
{
if(strncmp(buff,"register sucess!",16) == 0)
{
cout<<"-----register sucess!-----"<<endl;
return;
}
}
cerr<<"//register fail;"<<endl;
}
void do_login(int fd)
{
bool success = false;
string name;
string pw;
//让用户输入name pw
cout<<"--> your name: "<<endl;
cin>>name;
cout<<"--> your password: "<<endl;
cin>>pw;
//Json打包数据
Json::Value val;
val["reason_type"] = MSG_TYPE_LOGIN;
val["name"] = name;
val["pw"] = pw;
//发送
if(-1 == send(fd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
{
cerr<<"send reason fail;errno:"<<errno<<endl;
return;
}
//接受服务器回应(判断success/error)
char buff[256] = {0};
if(0 < recv(fd,buff,255,0))
{
if(strncmp(buff,"login sucess!",13) == 0)
{
cout<<"----- \033[35m"<<name<<"\033[0m login sucess!-----"<<endl;
success = true;
}
else cout<<"//login fail!"<<endl;
}
if(success)
{
//启动一个线程(专门接受数据和打印数据)
myname = name;
while(1)
{
cout<<"\033[31m1. get list 2. talk to one 3.talk to group 4.exit\n\033[0m"<<endl;
cout<<"--------------------------------------------------"<<endl;
pthread_t id;
pthread_create(&id,NULL,pth_run,(void *)fd);
int c;
cin>>c;
switch(c)
{
case 1:
do_get_list(fd);
break;
case 2:
do_talk_to_one(fd);
break;
case 3:
do_talk_to_group(fd);
break;
case 4:
do_exit(fd);
break;
default:
break;
}
}
}
}
void do_get_list(int fd)
{
Json::Value val;
val["reason_type"] = MSG_TYPE_GET_LIST;
val["name"] = myname;
//发送
if(-1 == send(fd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
{
cerr<<"send reason fail;errno:"<<errno<<endl;
return;
}
pthread_t id;
pthread_create(&id,NULL,pth_run,(void *)fd);
}
void do_talk_to_one(int fd)
{
string hername;
cout<<"--> choose a friend:";
cin>>hername;
char sendbuf[256] = {0};
cout<<"--> input:";
cin.ignore();
cin.getline(sendbuf,256);
Json::Value val;
val["reason_type"] = MSG_TYPE_TALK_TO_ONE;
val["name"] = myname;
val["hername"] = hername;
val["message"] = sendbuf;
if(-1 == send(fd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
{
cerr<<"send reason fail;errno:"<<errno<<endl;
return;
}
}
void do_talk_to_group(int fd)
{
char hername[128] = {0};
char sendbuf[256] = {0};
cin.ignore();
cout<<"--> choose friends use ';' to divide:";
cin.getline(hername,128);
cout<<"--> input:";
cin.getline(sendbuf,256);
Json::Value val;
val["reason_type"] = MSG_TYPE_TALK_TO_GROUP;
val["name"] = myname;
val["hername"] = hername;
val["message"] = sendbuf;
if(-1 == send(fd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
{
cerr<<"send reason fail;errno:"<<errno<<endl;
return;
}
}
void do_exit(int fd)
{
Json::Value val;
val["reason_type"] = MSG_TYPE_EXIT;
val["name"] = myname;
//发送
if(-1 == send(fd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
{
cerr<<"send reason fail;errno:"<<errno<<endl;
return;
}
exit(0);
close(fd);
}
Ser部分代码:
//control.cpp
Control::Control()
{
_map.insert(make_pair(MSG_TYPE_REGISTER,new Register));
_map.insert(make_pair(MSG_TYPE_LOGIN,new Login));
_map.insert(make_pair(MSG_TYPE_GET_LIST,new Get_list));
_map.insert(make_pair(MSG_TYPE_TALK_TO_ONE,new Talk_one));
_map.insert(make_pair(MSG_TYPE_TALK_TO_GROUP,new Talk_group));
_map.insert(make_pair(MSG_TYPE_EXIT,new Exit));
}
void Control::process(int fd,string json)
{
cout<<"control_sever.process(fd,buff)"<<endl;
//解析json,获取消息类型
Json::Value root;
Json::Reader read;
if(-1 == read.parse(json,root))
{
cout<<"json parse fail;"<<endl;
return;
}
else
cout<<" scan success"<<endl;
map<int ,View*>::iterator it =_map.find(root["reason_type"].asInt());
if( it == _map.end())
{
cout<<"not find"<<endl;
}
//cout<<root["reason_type"].asString()<<endl;
//根据消息类型在map中查找
/*
map<int,View*>::iterator it =_map.find(atoi(root["reason_type"].asString().c_str()));
//判断是否找到
//
if(it == _map.end())
{
cerr<<"not find!"<<endl;
return;
}
else
{
cout<<"message type can find "<<endl;
}*/
/*
map<int ,View*>::iterator it = _map.begin();
for( ; it!=_map.end() ; ++it)
{
if( it->first == atoi(root["reason_type"].asString().c_str()) )
{
break;
}
}
cout<< it->first <<endl;
*/
it->second->process(fd,json);
it->second->response();
}
//mysql.cpp
Mysql::Mysql()
{
//初始化
_mpcon = mysql_init((MYSQL *)0);
if(NULL == _mpcon)
{
cerr<<"mpcon = NULL"<<endl;
}
//连接数据库
if(!mysql_real_connect(_mpcon,"127.0.0.1","root","123456",NULL,3306,NULL,0))//成功返回0
{
cerr<<"mysql connect fail;"<<endl;
}
//选择database
if(mysql_select_db(_mpcon,"chat"))
{
cerr<<"database select fail;"<<endl;
}
}
Mysql::~Mysql()
{
if(NULL != _mp_res)
{
mysql_free_result(_mp_res);
}
//关闭数据库
mysql_close(_mpcon);
}
Mysql Mysql_sever;
//register.cpp
extern Mysql Mysql_sever;
void Register::process(int fd,string json)
{
//解析 json
//name pw
_fd = fd;
bool success = false;
Json::Value root;
Json::Reader read;
if(-1 == read.parse(json,root))
{
cerr<<"json parse fail;"<<endl;
return;
}
char name[20] = {0};
strcpy(name,root["name"].asString().c_str());
char pw[20] = {0};
strcpy(pw,root["pw"].asString().c_str());
//在数据库中查找name有没有重复
char cmd[100] = "SELECT * FROM user WHERE name='";
strcat(cmd,name);
strcat(cmd,"';");
mysql_real_query(Mysql_sever._mpcon,cmd,strlen(cmd));
Mysql_sever._mp_res = mysql_store_result(Mysql_sever._mpcon);
if(!(Mysql_sever._mp_row =mysql_fetch_row(Mysql_sever._mp_res) ))//无重复
{
success = true;
}
if(success)
{
//将name pw 加入到数据库的user表
char cmd2[100] = "INSERT INTO user VALUE('";
strcat(cmd2,name);
strcat(cmd2,"','");
strcat(cmd2,pw);
strcat(cmd2,"');");
if(mysql_real_query(Mysql_sever._mpcon,cmd2,strlen(cmd2)))
{
cerr<<"insert 1 fail;"<<endl;
return;
}
_str = "register sucess!";
}
else
{
_str= "register fail!";
}
}
void Register::response()
{
//发送
send(_fd,_str.c_str(),strlen(_str.c_str()),0);
}
//getlist.cpp
extern Mysql Mysql_sever;
void Get_list::process(int fd,string json)
{
_fd = fd;
Json::Value root;
Json::Reader read;
if(-1 == read.parse(json,root))
{
cerr<<"json parse fail;"<<endl;
return;
}
char myname[20] = {0};
strcpy(myname,root["name"].asString().c_str());
char cmd[100]="SELECT name FROM online WHERE name!='";
strcat(cmd,myname);
strcat(cmd,"';");
char name[1000] = "name:";
mysql_real_query(Mysql_sever._mpcon,cmd,strlen(cmd));
Mysql_sever._mp_res = mysql_store_result(Mysql_sever._mpcon);
while(Mysql_sever._mp_row=mysql_fetch_row(Mysql_sever._mp_res))
{
strcat(name,Mysql_sever._mp_row[0]);
strcat(name,",");
}
_str = name;
}
void Get_list::response()
{
//发送
send(_fd,_str.c_str(),strlen(_str.c_str()),0);
}
//tcpserver.cpp
void listen_cb(int fd,short event,void *arg)
{
//接受用户链接
Tcpsever *mythis = (Tcpsever*)arg;
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
int cli_fd = accept(fd,(struct sockaddr*)&caddr,&len);
if(-1 == cli_fd)
{
cerr<<"accept fail"<<endl;
return;
}
//查找当前监听数量最少的子线程
int min = 65536;// 存储最小监听数
int tmpfd = 0;//存储最小监听数对应的fd
map<int,int>::iterator it = mythis->_pth_work_num.begin();
//查找当前监听数量最少的子线程
for( ; it != mythis->_pth_work_num.end() ; ++it )
{
if( it->second < min )
{
min = it->second;
tmpfd = it->first;
}
}
//将主线程里的 客户端套接字 通过 socktpair 发给子线程
char abuff[16] = {0};
sprintf( abuff,"%d",cli_fd);
if( 0 > write(tmpfd, abuff, sizeof(abuff)) )
{
cout<<"write error"<<endl;
}
else
{
cout<<"write success"<<endl;
}
}
void sock_pair_cb(int fd,short event,void *arg)
{
Tcpsever *mythis = (Tcpsever *)arg;
int num = 0;
char buff[10] = {0};
if( 0 > read(fd,buff,9))
{
cout<<"read error"<<endl;
}
else
{
cout<<"read success"<<endl;
}
num = atoi(buff);
//更新到map表_pth_work_num ----->fd
map<int,int>::iterator it2 = mythis->_pth_work_num.begin();
for( ; it2!= mythis-> _pth_work_num.end() ; ++it2)
{
if( it2->first == fd)
{
break;
}
}
it2->second = num;
}
Tcpsever::Tcpsever(char *ip,short port,int pth_num)
{
///创建服务器
_pth_num = pth_num;
_listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == _listen_fd)
cout<<"_listen_fd creat error"<<endl;
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = inet_addr(ip);
cout<<"saddr init success"<<endl;
if(-1 == bind(_listen_fd,(struct sockaddr*)&saddr,sizeof(saddr)))
{
cout<<"bind fail"<<endl;
}
if(-1 == listen(_listen_fd,20))
{
cout<<"listen fail"<<endl;
}
//给libevent申请空间
_base = event_base_new();
//创建事件,绑定监听套接子的回调函数(listen_cb)
struct event* ev = event_new(_base,_listen_fd,EV_READ|EV_PERSIST,listen_cb,this);
if(ev != NULL)
{
cout<<"ev create success!"<<endl;
}
else
{
cout<<"event new fail"<<endl;
}
//将事件添加到事件列表
event_add(ev,NULL);
//循环监听
//event_base_dispatch(_base);
cout<<"tcpsever listen cb already carry out"<<endl;
}
void Tcpsever::run()
{
//申请socketpair(函数自查)
get_sock_pair();
//创建线程 //规定 int arr[2] arr[0]<=>主线程占用 arr[1]<=>子线程占用
get_pthread();
//为主线程的socktpair创建事件,绑定回调函数(sock_pair_cb)
for(int i = 0 ; i< _pth_num ; ++i)//由 子线程的读事件 触发
{
struct event* ev = event_new(_base,_socket_pair[i].arr[0],EV_READ|EV_PERSIST,sock_pair_cb,this);
if(NULL == ev)
{
return ;
}
else
event_add(ev,NULL);
}
cout<<"run() for already carry out"<<endl;
//event_base_dispatch(_base);
event_base_dispatch(_base);
}
void Tcpsever::get_sock_pair()
{
for(int i = 0;i < _pth_num;i++ )
{
//申请双向管道
int pair[2];
if( socketpair(AF_UNIX,SOCK_STREAM,0,pair) == -1 )
{
cout<<"socketpair error"<<endl;
return;
}
pipe pi(pair);
_socket_pair.push_back(pi); //将双向管道加入到_sock_pair.push_back();
_pth_work_num.insert(make_pair(pi.arr[0],0));
}
}
void Tcpsever::get_pthread()
{
//开辟线程
for(int i = 0; i< _pth_num; i++)
{
_pthread.push_back(new Pthread(_socket_pair[i].arr[1]));
}
}
- 第一个项目:基于TCP的聊天项目
- 基于Struts的第一个项目
- 我的第一个项目
- 偶的第一个项目
- 我的第一个项目
- 我的第一个项目
- 第一个实用的项目
- 第一个接触的项目
- IOS的第一个项目
- 第一个6513的项目
- 第一个项目的感受
- 第一个项目的感受
- 第一个项目的收获
- 第一个项目的心得
- 第一个项目的感受
- 第一个项目的总结
- Django 的第一个项目
- 我的第一个项目
- 【模板】【数论】高精度
- java面试总结-2 hibernate
- opencv(c++)随机生成器和文本
- HTML和CSS
- linux下安装rocketMQ双Master集群
- 第一个项目:基于TCP的聊天项目
- 机器学习基石-07-2-VC Dimension of Perceptrons
- Android开发-eclipse环境搭建
- neo4j常用命令
- python 记录
- Spring3:AOP
- PAT 单链表逆转
- 推荐 MySQL事务内幕与ACID
- 换个格式输出整数