项目名称:chat_room群聊系统
背景知识与主要技术:
熟悉Linux基本指令的使用(ls,cd,make,mkdir,top,basename,pwd,cp,mv,rm,touch)
熟悉linux开发环境,熟练使用vi/vim ,gcc/g++,gdb,make,makefile
了解网络,熟悉tcp ip udp协议的使用
熟练掌握C/C++,熟练使用C++STL中的容器
熟悉套接字编程
熟悉线程的使用
熟悉shell脚本的简单编写
使用的开源/系统库:
1.jsoncpp:基于c++的json库实现数据的序列化与反序列化的功能。
2.ncurses: 控制屏幕终端显示的库,项目中用于实现客户端界面。
3.pthread:项目中使用该库提供线程支持。
主要功能模块:
1.server模块:基于udp协议完成用户与服务器之间的通信。
2.comm模块:基于jsoncpp库对通讯数据进行序列化
3.data_pool模块:基于vector与信号量对数据进行存储
4.window模块:基于ncurses库实现客户端界面(输入框,输出框,标题,好友列表)
5.lib模块:项目所需要的动态库
6.log模块:打印项目的错误信息
7.client模块:采用上述模块实现界面,与数据的发送与接收。
8.plugin模块:服务起停与程序编译的控制脚本
思想图解
主要模块代码
server端
#ifndef _SERVER_H_
#define _SERVER_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"data_pool.h"
#include"log.h"
#include"data.h"
using namespace std;
class server
{
public:
server(string ip,int port);
int sock_init();
int recvmsg(string &out);
int sendmsg(const struct sockaddr_in &client,const string &in);
int broadcast();
void *handler(void *arg);
void add_online_usr(const struct sockaddr_in *client);
void del_online_usr(const struct sockaddr_in *client);
~server();
private:
string _ip;
int _port;
int _sock;
map<int,struct sockaddr_in> _onlineusr;
pool data_pool;
};
#endif
#include"udp_server.h"
server::server(string ip,int port)
:_ip(ip)
,_port(port)
,_sock(-1)
,data_pool(256)
{}
server::~server()
{
if(_sock > 0)
close(_sock);
}
int server::sock_init()
{
_sock = socket(AF_INET,SOCK_DGRAM,0);
if(_sock < 0)
{
print_log("socket error",ERROR);
return -1;
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = inet_addr(_ip.c_str());
if(bind(_sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
print_log("bind error",ERROR);
return -1;
}
return 0;
}
int server::recvmsg(string &out)
{
struct sockaddr_in client;
char buf[1024];
socklen_t len = sizeof(client);
ssize_t s = recvfrom(_sock,buf,sizeof(buf)-1,0,\
(struct sockaddr*)&client,&len);
if(s > 0)//recv success
{
buf[s] = 0;
out = buf;
data_pool.put_data(out);
add_online_usr(&client);
data _data;
_data.val_to_unserialize(out);
if(strcasecmp(_data.cmd.c_str(),"QUIT") == 0)
{
del_online_usr(&client);
}
return 0;
}
return -1;
}
int server::sendmsg(const struct sockaddr_in &client,const string &in)
{
//const char *msg = in.c_str();
if(sendto(_sock,in.c_str(),in.size(),0,(struct sockaddr*)&client,sizeof(client)))
{
return 0;
}
return -1;
}
int server::broadcast()
{
string in;
data_pool.get_data(in);
map<int,struct sockaddr_in>::iterator iter = _onlineusr.begin();
while(iter != _onlineusr.end())
{
sendmsg(iter->second,in);
iter++;
}
return 0;
}
void server::add_online_usr(const struct sockaddr_in* client)
{
_onlineusr.insert(std::pair<int,struct sockaddr_in>(client->sin_addr.s_addr,*client));
}
void server::del_online_usr(const struct sockaddr_in *client)
{
map<int,struct sockaddr_in>::iterator iter = _onlineusr.find(client->sin_addr.s_addr);
if(iter == _onlineusr.end())//can not find
{
return;
}
else
{
_onlineusr.erase(iter);
}
}
#include"udp_server.h"
void *handler(void *arg)
{
server *ser = (server*)arg;
while(1)
{
ser->broadcast();
}
}
int main(int argc,char *argv[])
{
if(argc != 3)
{
std::cout << "Usage :" << argv[0] << "[ip]" << "[port]" << std::endl;
return -1;
}
server ser(argv[1],atoi(argv[2]));
ser.sock_init();
pthread_t id;
pthread_create(&id,NULL,handler,(void*)&ser);
data _data;
while(1)
{
string out;
ser.recvmsg(out);
std::cout << "client say#:" << out << std::endl;
}
}
comm端
#ifndef _JSON_H_
#define _JSON_H_
#include"json/json.h"
#include
int serialize(Json::Value &_v,std::string &str);
int unserialize(Json::Value &_v,std::string &str);
#endif
#include"base_json.h"
int serialize(Json::Value &_v,std::string &str)
{
Json::StyledWriter _w;
str = _w.write(_v);
return 0;
}
int unserialize(Json::Value &_v,std::string &str)
{
Json::Reader _r;
if(_r.parse(str,_v,false))
{
return 0;
}
return -1;
}
#ifndef _DATA_H_
#define _DATA_H_
#include
#include
#include"base_json.h"
class data
{
public:
data();
~data();
void val_to_serialize(std::string &str);
void val_to_unserialize(std::string &str);
public:
std::string nick_name;
std::string school;
std::string msg;
std::string cmd;
};
#endif
#include"data.h"
data::data()
{}
data::~data()
{}
void data::val_to_serialize(std::string &str)
{
Json::Value _v;
_v["nick_name"] = nick_name;
_v["school"] = school;
_v["msg"] = msg;
_v["cmd"] = cmd;
serialize(_v,str);
}
void data::val_to_unserialize(std::string &str)
{
Json::Value _v;
unserialize(_v,str);
nick_name = _v["nick_name"].asString();
school = _v["school"].asString();
msg = _v["msg"].asString();
cmd = _v["cmd"].asString();
}
/////以下为测试代码
//int main()
//{
// data d;
// while(1)
// {
//
// std::cin >> d.nick_name;
// std::cin >> d.school;
// std::cin >> d.msg ;
// d.cmd = "";
// std::string str;
// d.val_to_serialize(str);
// std::cout << str << std::endl;
// d.val_to_unserialize(str);
// std::cout << d.nick_name << std::endl;
// std::cout << d.school << std::endl;
// std::cout << d.msg << std::endl;
//
// }
//
// return 0;
//}
window端—界面实现
#include
#include
#include
class window
{
public:
window();
~window();
void create_win(WINDOW *& _win,int _h,int _w,int _y,int _x);
void create_header();
void create_output();
void create_input();
void create_friends_list();
void put_str_to_window(WINDOW *_win,int _y,int _x,std::string str);
void get_str_window(WINDOW *_win,std::string &msg);
void fflush(WINDOW *_win);
public:
WINDOW * _header;
WINDOW * _output;
WINDOW * _input;
WINDOW * _friends_list;
};
#include"window.h"
window::window()
{
initscr();
}
window::~window()
{
delwin(_header);
delwin(_output);
delwin(_input);
delwin(_friends_list);
endwin();
}
void window::create_win(WINDOW *&_win,int _h,int _w,int _y,int _x)
{
_win = newwin(_h,_w,_y,_x);
box(_win,0,0);
}
void window::create_header()
{
int _h = LINES/5;
int _w = COLS;
int _x = 0;
int _y = 0;
create_win(_header,_h,_w,_y,_x);
}
void window::create_output()
{
int _h = (LINES*3)/5;
int _w = (COLS*3)/4;
int _x = 0;
int _y = LINES/5;
create_win(_output,_h,_w,_y,_x);
}
void window::create_input()
{
int _h = LINES/5;
int _w = COLS;
int _x = 0;
int _y = (LINES*4)/5;
create_win(_input,_h,_w,_y,_x);
}
void window::create_friends_list()
{
int _h = (LINES*3)/5;
int _w = COLS/4;
int _x = (COLS*3)/4;
int _y = LINES/5;
create_win(_friends_list,_h,_w,_y,_x);
}
void window::put_str_to_window(WINDOW *_win,int _y,int _x,std::string str)
{
mvwaddstr(_win,_y,_x,str.c_str());
}
void window::get_str_window(WINDOW *_win,std::string &msg)
{
char buf[1024];
wgetnstr(_win,buf,sizeof(buf));
msg = buf;
}
void window::fflush(WINDOW *_win)
{
wrefresh(_win);
}
//以下为本模块测试代码
//int main()
//{
// window win;
// win.create_header();
// wrefresh(win._header);
// usleep(2000000);
// win.create_output();
// wrefresh(win._output);
// usleep(2000000);
// win.create_friends_list();
// wrefresh(win._friends_list);
// usleep(2000000);
// win.create_input();
// std::string str = "Please Entry#";
// mvwaddstr(win._input,1,1,str.c_str());
// wrefresh(win._input);
// usleep(2000000);
// int j = 1;
// int _y;
// int _x;
// while(1)
// {
// std::string str = "Welcome to chat_room!";
// win.create_header();
// mvwaddstr(win._header,1,j++,str.c_str());
// if(str.size() >= _x-j-1)
// j = 1;
// wrefresh(win._header);
// getmaxyx(win._header,_y,_x);
// j%=(_x-1);
// wclrtoeol(win._header);
// usleep(100000);
//
// }
// int i = 1;
// int y;
// int x;
// while(1)
// {
// std::string str = "Please Entry#";
// mvwaddstr(win._output,i++,1,str.c_str());
// wrefresh(win._output);
// getmaxyx(win._output,y,x);
// i%=(y-1);
// if(i == 0)
// {
// i = 1;
// wclrtoeol(win._output);
// win.create_output();
//
// }
// usleep(1000000);
// }
//
// return 0;
//}
client端
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include"window.h"
#include"data.h"
#include"log.h"
#include
#include
using namespace std;
class client
{
public:
client(string ip,int port);
~client();
int sock_init();
int recvmsg(string &out);
int sendmsg(const string &out);
private:
string _ip;
int _port;
int _sock;
//map _onlineusr;
};
#endif
#include"udp_client.h"
client::client(string ip,int port)
:_ip(ip)
,_port(port)
,_sock(-1)
{}
client::~client()
{
if(_sock > 0)
close(_sock);
}
int client::sock_init()
{
_sock = socket(AF_INET,SOCK_DGRAM,0);
if(_sock < 0)
{
print_log("socket error",ERROR);
return -1;
}
return 0;
}
int client::recvmsg(string &out)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
char buf[1024];
ssize_t s = recvfrom(_sock,buf,sizeof(buf)-1,0,\
(struct sockaddr*)&peer,&len);
if(s > 0)//recv success
{
buf[s] = 0;
out = buf;
return 0;
}
return -1;
}
int client::sendmsg(const string &in)
{
struct sockaddr_in peer;
peer.sin_family = AF_INET;
peer.sin_port = htons(_port);
peer.sin_addr.s_addr = inet_addr(_ip.c_str());
if(sendto(_sock,in.c_str(),in.size(),0,(struct sockaddr*)&peer,sizeof(peer)))
{
return 0;
}
return -1;
}
#include"udp_client.h"
string name;
string school;
client *pcli;
vector<string> flist;
int flag = 1;
typedef struct win_cli
{
client *_cli;
window *_win;
} win_cli,*pwin_cli;
void add_user(string friends)
{
int i = 0;
for(; i < flist.size();++i)
{
if(flist[i] == friends)
{
flag = 0;
return;
}
}
flist.push_back(friends);
flag = 1;
}
void del_user(string friends)
{
vector<string>::iterator iter = flist.begin();
while(iter != flist.end())
{
if(*iter == friends)
{
iter = flist.erase(iter);
flag = 1;
break;
}
iter++;
}
}
void quit(int sig)
{
data _data;
_data.nick_name = name;
_data.school = school;
_data.msg = "";
_data.cmd = "QUIT";
string in;
_data.val_to_serialize(in);
pcli->sendmsg(in);
endwin();
exit(0);
}
void *show_header(void *arg)
{
pwin_cli wc = (pwin_cli)arg;
window *win = wc->_win;
int j = 1;
int _y;
int _x;
while(1)
{
string str = "Welcome to chat_room!";
win->create_header();
mvwaddstr(win->_header,1,j++,str.c_str());
if(str.size() >= _x-j-1)
j = 1;
wrefresh(win->_header);
getmaxyx(win->_header,_y,_x);
j%=(_x-1);
wclrtoeol(win->_header);
usleep(100000);
}
}
void *show_output(void *arg)
{
pwin_cli wc = (pwin_cli)arg;
client *cli = wc->_cli;
window *win = wc->_win;
int i = 1;
int j = 1;
int y;
int x;
int fy;
int fx;
win->create_output();
wrefresh(win->_output);
win->create_friends_list();
wrefresh(win->_friends_list);
string out;
string friends;
data _d;
string str;
while(1)
{
cli->recvmsg(out);
_d.val_to_unserialize(out);
str=_d.nick_name;
str+="-";
str+=_d.school;
friends = str;
str+="#";
str+=_d.msg;
if(strcmp(_d.cmd.c_str(),"QUIT") == 0)
{
del_user(friends);
wclrtoeol(win->_friends_list);
wrefresh(win->_friends_list);
win->create_friends_list();
wrefresh(win->_friends_list);
}
else
{
win->put_str_to_window(win->_output,i++,1,str);
wrefresh(win->_output);
add_user(friends);
getmaxyx(win->_output,y,x);
i%=(y-1);
if(i == 0)
{
i = 1;
wclrtoeol(win->_output);
win->create_output();
wrefresh(win->_output);
}
}
if(flag)
{
int k = 0;
getmaxyx(win->_friends_list,fy,fx);
for(; k < flist.size();++k)
{
mvwaddstr(win->_friends_list,j++,1,flist[k].c_str());
wrefresh(win->_friends_list);
j %= (fy-1);
if(j == 0)
{
j = 1;
wclrtoeol(win->_friends_list);
wrefresh(win->_friends_list);
win->create_friends_list();
wrefresh(win->_friends_list);
}
}
j = 1;
}
}
}
void *show_input(void *arg)
{
pwin_cli wc = (pwin_cli)arg;
client *cli = wc->_cli;
window *win = wc->_win;
data _data;
_data.nick_name = name;
_data.school = school;
while(1){
win->create_input();
wrefresh(win->_input);
string ptr = "Please Entry#";
mvwaddstr(win->_input,1,1,ptr.c_str());
wrefresh(win->_input);
win->get_str_window(win->_input,_data.msg);
wrefresh(win->_input);
_data.cmd = "";
string in;
_data.val_to_serialize(in);
cli->sendmsg(in);
}
}
int main(int argc,char *argv[])
{
if(argc != 3)
{
cout << "Usage :" << argv[0] << "[ip]" << "[port]" << std::endl;
return -1;
}
client cli(argv[1],atoi(argv[2]));
cout << "Please Enteryour nick_name#";
cin >> name;
cout << "Please Enter your school#";
cin >> school;
cli.sock_init();
window win;
win_cli _wc = {&cli,&win};
pcli = &cli;
signal(2,quit);
pthread_t header;
pthread_t output;
pthread_t input;
pthread_create(&input,NULL,show_input,(void*)&_wc);
pthread_create(&header,NULL,show_header,(void*)&_wc);
pthread_create(&output,NULL,show_output,(void*)&_wc);
pthread_join(header,NULL);
pthread_join(output,NULL);
pthread_join(input,NULL);
}
//以下为本模块测试代码
//int main(int argc,char *argv[])
//{
// if(argc != 3)
// {
// cout << "Usage :" << argv[0] << "[ip]" << "[port]" << std::endl;
// return -1;
// }
// client cli(argv[1],atoi(argv[2]));
// cli.sock_init();
// data _data;
// cout << "Please Enteryour nick_name#";
// cin >> _data.nick_name;
// cout << "Please Enter your school#";
// cin >> _data.school;
// while(1)
// {
// cout << "please input*";
// cin >> _data.msg;
// _data.cmd = "";
// string in;
// _data.val_to_serialize(in);
// cli.sendmsg(in);
// string out;
// cli.recvmsg(out);
// cout << "out:" << out << endl;
// _data.val_to_unserialize(out);
// string str;
// str+=_data.nick_name;
// str+="-";
// str+=_data.school;
// str+="# ";
// str+=_data.msg;
// cout << str << endl;
//
// }
//}
makefile编写
ROOT=$(shell pwd)
LOG=${ROOT}/log
POOL=${ROOT}/data_pool
SERVER=${ROOT}/server
CLIENT=${ROOT}/client
SERVER_BIN=chat_system
CLIENT_BIN=chat_client
BIN=${ROOT}/lib
DATA=${ROOT}/comm
WINDOW=${ROOT}/window
CONF=${ROOT}/conf
PLUGIN=${ROOT}/plugin
INCLUDE=-I$(LOG) -I$(POOL) -I$(BIN)/include -I$(DATA) -I$(WINDOW)
SERVER_OBJ=$(shell ls $(SERVER)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
SERVER_OBJ+=$(shell ls $(LOG)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
SERVER_OBJ+=$(shell ls $(DATA)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
SERVER_OBJ+=$(shell ls $(POOL)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
CLIENT_OBJ=$(shell ls $(CLIENT)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
CLIENT_OBJ+=$(shell ls $(LOG)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
CLIENT_OBJ+=$(shell ls $(DATA)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
CLIENT_OBJ+=$(shell ls $(WINDOW)/ | grep -E '\.cpp$$' | sed 's/\.cpp/\.o/')
cc=g++
FLAGS=-lpthread
.PHONY:all
all:${SERVER_BIN} ${CLIENT_BIN}
${SERVER_BIN}:${SERVER_OBJ}
@${cc} -o $@ $^ $(FLAGS) -L$(BIN)/lib -ljsoncpp
@echo "linking [$^] [$@] ....done"
${CLIENT_BIN}:${CLIENT_OBJ}
@${cc} -o $@ $^ -L$(BIN)/lib -ljsoncpp -lncurses
@echo "linking [$^] [$@] ....done"
%.o:$(SERVER)/%.cpp
@$(cc) -c $< $(INCLUDE)
@echo "comping [$^] [$@] ....done"
%.o:$(LOG)/%.cpp
@$(cc) -c $<
@echo "comping [$^] [$@] ....done"
%.o:$(POOL)/%.cpp
@$(cc) -c $< $(INCLUDE)
@echo "comping [$^] [$@] ....done"
%.o:$(CLIENT)/%.cpp
@$(cc) -c $< $(INCLUDE)
@echo "comping [$^] [$@] ....done"
%.o:$(DATA)/%.cpp
@$(cc) -c $< $(INCLUDE)
@echo "comping [$^] [$@] ....done"
%.o:$(WINDOW)/%.cpp
@$(cc) -c $< $(INCLUDE)
@echo "comping [$^] [$@] ....done"
.PHONY:output
output:
@mkdir -p outut/server
@mkdir -p output/server/log
@mkdir -p output/client
@cp $(SERVER_BIN) output/server/
@cp $(CLIENT_BIN) output/client/
@cp $(PLUGIN)/ctl_server.sh output/server/
@cp -rf $(CONF) output/server
.PHONY:clean
clean:
@rm -rf *.o $(SERVER_BIN) $(CLIENT_BIN) output