·ChatClient.cc
#include<iostream>
#include"ChatClient.hpp"
static void Usage(std::string proc)
{
std::cout<<"Usage: "<<proc<<"peer_ip"<<std::endl;
}
void Menu(int &s)
{
std::cout<<"############################"<<std::endl;
std::cout<<"#1. Register 2. Login##"<<std::endl;
std::cout<<"# 3. Exit ##"<<std::endl;
std::cout<<"############################"<<std::endl;
std::cout<<"Please Select:>";
std::cin>>s;
}
int main(int argc,char* argv[])
{
if(argc!=2)
{
Usage(argv[0]);
exit(1);
}
ChatClient *cp=new ChatClient(argv[1]);
cp->InitClient();
int select=0;
while(1)
{
Menu(select);
switch(select)
{
case 1:
cp->Register();
break;
case 2:
if(cp->Login())
{
cp->Chat();
}
break;
case 3:
exit(0);
break;
default:
exit(1);
break;
}
}
}
·ChatClient.hpp
#pragma once
#include<iostream>
#include<string>
#include"ProtocolUtil.hpp"
#include"Message.hpp"
#define TCP_PORT 8080
#define UDP_PORT 8888
class ChatClient
{
private:
int tcp_sock;
int udp_sock;
std::string peer_ip;
unsigned int id;
std::string passwd;
std::string nick_name;
std::string school;
struct sockaddr_in server;
public:
ChatClient(std::string ip_):peer_ip(ip_)
{
id=0;
tcp_sock=-1;
udp_sock=-1;
server.sin_family=AF_INET;
server.sin_port=htons(UDP_PORT);
server.sin_addr.s_addr=inet_addr(peer_ip.c_str());
}
void InitClient()
{
tcp_sock=SocketApi::Socket(SOCK_STREAM);
udp_sock=SocketApi::Socket(SOCK_DGRAM);
}
bool ConnectServer()
{
return SocketApi::Connect(tcp_sock,peer_ip,TCP_PORT);
}
bool Register()
{
if(Util::RegisterEnter(nick_name,school,passwd)&&ConnectServer())
{
Request rq;
rq.method="REGISTER\n";
Json::Value root;
root["name"]=nick_name;
root["school"]=school;
root["passwd"]=passwd;
Util::Seralizer(root,rq.text);
rq.content_length="Content_Length";
rq.content_length+=Util::IntToString((rq.text).size());
rq.content_length+="\n";
SocketApi::SendRequest(tcp_sock,rq);
recv(tcp_sock,&id,sizeof(id),0);
bool res=false;
if(id>=10000)
{
std::cout<<"Register Success!Your Login ID is :"<<id<<std::endl;
res=true;
}
else
{
std::cout<<"Register Failed! Code is:"<<id<<std::endl;
}
close(tcp_sock);
return res;
}
}
bool Login()
{
if(Util::LoginEnter(id,passwd) && ConnectServer())
{
Request rq;
rq.method="LOGIN\n";
Json::Value root;
root["id"]=id;
root["passwd"]=passwd;
Util::Seralizer(root,rq.text);
rq.content_length="Content_Length";
rq.content_length+=Util::IntToString((rq.text).size());
rq.content_length+="\n";
SocketApi::SendRequest(tcp_sock,rq);
unsigned int result=0;
recv(tcp_sock,&result,sizeof(result),0);
bool res=false;
if(result>=10000)
{
res=true;
std::cout<<"Login Success!"<<std::endl;
}
else
{
std::cout<<"Login Failed! Code is:"<<result<<std::endl;
}
close(tcp_sock);
return res;
}
}
void Chat()
{
std::string n_;
std::string s_;
std::string text_;
std::cout<<"Please Enter Your Name:";
std::cin>>n_;
std::cout<<"Please Enter Your School";
std::cin>>s_;
while(1)
{
std::string text_;
std::cout<<"Please Enter:";
std::cin>>text_;
Message msg(n_,s_,text_,id);
std::string sendString;
msg.ToSendString(sendString);
Util::SendMessage(udp_sock,sendString,server);
std::string recvString;
struct sockaddr_in peer;
Util::RecvMessage(udp_sock,recvString,peer);
std::cout<<"debug: "<<recvString<<std::endl;
msg.ToRecvValue(recvString);
std::cout<<"name:"<<msg.NickName()<<std::endl;
std::cout<<"schoo:"<<msg.School()<<std::endl;
std::cout<<"tex:"<<msg.Text()<<std::endl;
std::cout<<"id:"<<msg.Id()<<std::endl;
}
}
~ChatClient()
{}
};
·ChatServer.cc
#include "ChatServer.hpp"
#include<iostream>
// ./ChatServer tcp_port udp_port
//
static void Usage(std::string proc)
{
std::cout<<"Usage: "<<proc<<"tcp_port udp_port"<<std::endl;
}
void *RunProduct(void *arg)
{
pthread_detach(pthread_self());
ChatServer *sp=(ChatServer*)arg;
for(;;)
{
sp->Product();
}
}
void *RunConsume(void *arg)
{
pthread_detach(pthread_self());
ChatServer *sp=(ChatServer*)arg;
for(;;)
{
sp->Consume();
}
}
int main(int argc,char *argv[])
{
if(argc!=3)
{
Usage(argv[0]);
exit(1);
}
int tcp_port=atoi(argv[1]);
int udp_port=atoi(argv[2]);
ChatServer *sp=new ChatServer(tcp_port,udp_port);
sp->InitServer();
pthread_t c,p;
pthread_create(&p,NULL,RunProduct,(void*)sp);
pthread_create(&p,NULL,RunConsume,(void*)sp);
sp->Start();
return 0;
}
·ChatServer.hpp
#pragma once
#include<iostream>
#include<pthread.h>
#include"ProtocolUtil.hpp"
#include"UserManager.hpp"
#include"Log.hpp"
#include"Message.hpp"
#include"DataPool.hpp"
class ChatServer;
class Param{
public:
ChatServer *sp;
int sock;
std::string ip;
int port;
public:
Param(ChatServer *sp_,int &sock_,const std::string &ip_,const int &port_)
:sp(sp_),sock(sock_),ip(ip_),port(port_)
{}
~Param()
{}
};
class ChatServer
{
private:
int tcp_listen_sock;
int tcp_port;
int udp_work_sock;
int udp_port;
UserManager um;
DataPool pool;
public:
ChatServer(int tcp_port_=8080,int udp_port_=8888):
tcp_port(tcp_port_),
tcp_listen_sock(-1),
udp_port(udp_port_),
udp_work_sock(-1)
{}
void InitServer()
{
tcp_listen_sock=SocketApi::Socket(SOCK_STREAM);
udp_work_sock=SocketApi::Socket(SOCK_DGRAM);
SocketApi::Bind(tcp_listen_sock,tcp_port);
SocketApi::Bind(udp_work_sock,udp_port);
SocketApi::Listen(tcp_listen_sock);
}
unsigned int RegisterUser(const std:: string &name,\
const std::string &school,\
const std::string &passwd)
{
return um.Insert(name,school,passwd);
}
unsigned int LoginUser(const unsigned int &id,const std::string passwd,\
const std::string &ip,int port)
{
unsigned int result=um.Check(id,passwd);
if(result>=10000)
{
//um.MoveToOnline(id,ip,port);
}
return result;
}
void Product()
{
std::string message;
struct sockaddr_in peer;
Util::RecvMessage(udp_work_sock,message,peer);
std::cout<<"debug:recv message:"<<message<<std::endl;
if(!message.empty())
{
pool.PutMessage(message);
Message m;
m.ToRecvValue(message);
um.AddOnlineUser(m.Id(),peer);
}
}
void Consume()
{
std::string message;
pool.GetMessage(message);
std::cout<<"debug:send message:"<<message<<std::endl;
auto online = um.OnlineUser();
for(auto it=online.begin();it!=online.end();it++)
{
Util::SendMessage(udp_work_sock,message,it->second);
}
}
static void* HandlerRequest(void *arg)
{
Param *p=(Param*)arg;
int sock=p->sock;
ChatServer *sp=p->sp;
std::string ip=p->ip;
int port=p->port;
delete p;
pthread_detach(pthread_self());
Request rq;
Util::RecvRequest(sock,rq);
Json::Value root;
LOG(rq.text,NORMAL);
Util::UnSeralizer(rq.text,root);
if(rq.method == "REGISTER")
{
Json::Value root;
Util::UnSeralizer(rq.text,root);
std::string name=root["name"].asString();
std::string school=root["school"].asString();
std::string passwd=root["passwd"].asString();
unsigned int id=sp->RegisterUser(name,school,passwd);//sp->RegisterUser(name,school,passwd);
send(sock,&id,sizeof(id),0);
}
else if(rq.method=="LOGIN")
{
unsigned int id=root["id"].asInt();
std::string passwd=root["passwd"].asString();
unsigned int result=sp->LoginUser(id,passwd,ip,port);//sp->LoginUser(id,passwd);
send(sock,&result,sizeof(result),0);
}
else
{
}
//recv:
//any && handler
//response
close(sock);
}
void Start()
{
std::string ip;
int port;
for(;;)
{
int sock=SocketApi::Accept(tcp_listen_sock,ip,port);
if(sock>0)
{
std::cout<<"get a new client"<<ip<<":"<<port<<std::endl;
Param *p=new Param(this,sock,ip,port);
pthread_t tid;
pthread_create(&tid,NULL,HandlerRequest,p);
}
}
}
~ChatServer()
{}
};
·DataPool.hpp
#pragma once
#include<iostream>
#include<vector>
#include<string>
#include<semaphore.h>
class DataPool
{
private:
std::vector<std::string>pool;
int cap;
sem_t data_sem;
sem_t blank_sem;
int product_step;
int consume_step;
public:
DataPool(int cap_=512):cap(cap_),pool(cap_)
{
sem_init(&data_sem,0,0);
sem_init(&blank_sem,0,cap);
product_step=0;
consume_step=0;
}
void PutMessage(const std::string &msg)
{
sem_wait(&blank_sem);
pool[product_step]=msg;
product_step++;
product_step%=cap;
sem_post(&data_sem);
}
void GetMessage(std::string &msg)
{
sem_wait(&data_sem);
msg=pool[consume_step];
consume_step++;
consume_step%=cap;
sem_post(&blank_sem);
}
~DataPool()
{
sem_destroy(&data_sem);
sem_destroy(&blank_sem);
}
};
·Log.hpp
#pragma once
#include<iostream>
#include<string>
#define NORMAL 0
#define WARNING 1
#define ERROR 2
const char *log_level[]={
"Normal",
"Warning",
"Error",
NULL,
};
void Log(std::string msg,int level,std::string file,int line)
{
std::cout<<'['<<msg<<']'<<'['<<log_level[level]<<']'<<":"<<\
file<<":"<<line<<std::endl;
}
#define LOG(msg,level) Log(msg,level,__FILE__,__LINE__)
·Message.hpp
#pragma once
#include<iostream>
#include<string>
#include"json/json.h"
#include"ProtocolUtil.hpp"
class Message
{
private:
std::string nick_name;
std::string school;
std::string text;
unsigned int id;
public:
Message()
{}
Message(const std::string &n_, const std::string &s_,const std::string &t_, const unsigned int &id_)
:nick_name(n_),school(s_),text(t_),id(id_)
{
}
void ToSendString(std::string &sendString)
{
Json::Value root;
root["name"]=nick_name;
root["school"]=school;
root["text"]=text;
root["id"]=id;
Util::Seralizer(root,sendString);
}
void ToRecvValue(std::string &recvString)
{
Json::Value root;
Util::UnSeralizer(recvString,root);
nick_name=root["name"].asString();
school=root["school"].asString();
text=root["text"].asString();
id=root["id"].asInt();
}
const std::string &NickName()
{
return nick_name;
}
const std::string &School()
{
return school;
}
const std::string &Text()
{
return text;
}
const unsigned int &Id()
{
return id;
}
~Message()
{
}
};
·ProtocolUtil.hpp
#pragma once
#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include"Log.hpp"
#include<unistd.h>
#include<stdlib.h>
#include<json/json.h>
#include<sstream>
#define BACKLOG 5
#define MESSAGE_SIZE 1024
class Request
{
public:
std::string method;
std::string content_length;
std::string blank;
std::string text;
public:
Request():blank("\n")
{}
~Request()
{}
};
class Util
{
public:
static bool RegisterEnter(std::string &n_,std::string &s_,std::string &passwd)
{
std::cout<<"Please Enter Nick Name: ";
std::cin>>n_;
std::cout<<"Please Enter School: ";
std::cin>>s_;
std::cout<<"Please Enter Passwd: ";
std::cin>>passwd;
std::string again;
std::cout<<"Please Enter Passwd Again: ";
std::cin>>again;
if(passwd==again)
{
return true;
}
return false;
}
static bool LoginEnter(unsigned int id,std::string &passwd)
{
std::cout<<"Please Enter Your ID:";
std::cin>>id;
std::cout<<"Please Enter Your Passwd:";
std::cin>>passwd;
}
static void Seralizer(Json::Value &root,std::string &outString)
{
Json::FastWriter w;
outString=w.write(root);
}
static void UnSeralizer(std::string &inString,Json::Value &root)
{
Json::Reader r;
r.parse(inString,root,false);
}
static std::string IntToString(int x)
{
std::stringstream ss;
ss<<x;
return ss.str();
}
static int StringToInt(std::string &str)
{
std::stringstream ss(str);
int x;
ss>>x;
return x;
}
static void RecvOneLine(int sock,std::string &outString)
{
char c='x';
while(c!='\n')
{
ssize_t s=recv(sock,&c,1,0);
if(s>0)
{
if(c=='\n')
{
break;
}
outString.push_back(c);
}
else
{
break;
}
}
}
static void RecvRequest(int sock,Request &rq)
{
RecvOneLine(sock,rq.method);
RecvOneLine(sock,rq.content_length);
RecvOneLine(sock,rq.blank);
RecvOneLine(sock,rq.text);
std::string &cl=rq.content_length;
std::size_t pos=cl.find(":");
if(std::string::npos==pos)
{
return;
}
std::string sub=cl.substr(pos+2);
int size=StringToInt(sub);
char c;
for(auto i=0;i<size;i++)
{
recv(sock,&c,1,0);
(rq.text).push_back(c);
}
}
static void RecvMessage(int sock,std::string &message,struct sockaddr_in &peer)
{
char msg[MESSAGE_SIZE];
socklen_t len=sizeof(peer);
ssize_t s=recvfrom(sock,msg,sizeof(msg)-1,0,\
(struct sockaddr*)&peer,&len);
if(s<=0)
{
LOG("recvfrom message error",WARNING);
}
else
{
message=msg;
}
}
static void SendMessage(int sock,const std::string &message,\
struct sockaddr_in &peer)
{
sendto(sock,message.c_str(),message.size(),0,\
(struct sockaddr*)&peer,sizeof(peer));
}
};
class SocketApi
{
public:
static int Socket(int type)
{
int sock=socket(AF_INET,type,0);
if(sock<0)
{
LOG("socket error!",ERROR);
exit(2);
}
}
static void Bind(int sock,int port)
{
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_addr.s_addr=htonl(INADDR_ANY);
local.sin_port=htons(port);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
LOG("Bind error",ERROR);
exit(3);
}
}
static void Listen(int sock)
{
if(listen(sock,BACKLOG)<0)
{
LOG("listen error",ERROR);
exit(4);
}
}
static int Accept(int listen_sock,std::string &out_ip,int &out_port)
{
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
int sock=accept(listen_sock,(struct sockaddr*)&peer,&len);
if(sock<0)
{
LOG("accept error",WARNING);
return -1;
}
out_ip=inet_ntoa(peer.sin_addr);
out_port=htons(peer.sin_port);
return sock;
}
static bool Connect(const int &sock,std::string peer_ip,const int &port)
{
struct sockaddr_in peer;
peer.sin_family=AF_INET;
peer.sin_addr.s_addr=inet_addr(peer_ip.c_str());
peer.sin_port=htons(port);
if(connect(sock,(struct sockaddr*)&peer,sizeof(peer))<0)
{
LOG("connect error",WARNING);
return false;
}
return true;
}
static void SendRequest(int sock,Request &rq)
{
std::string &m_=rq.method;
std::string &cl_=rq.content_length;
std::string &b_=rq.blank;
std::string &text_=rq.text;
send(sock,m_.c_str(),m_.size(),0);
send(sock,cl_.c_str(),cl_.size(),0);
send(sock,b_.c_str(),b_.size(),0);
send(sock,text_.c_str(),text_.size(),0);
}
};
·UserManager.hpp
#pragma once
#include<iostream>
#include<string>
#include<unordered_map>
#include<pthread.h>
class User
{
private:
std::string nick_name;
std::string school;
std::string passwd;
public:
User(const std::string &n_,const std::string &s_,\
const std::string pwd_):
nick_name(n_),school(s_),passwd(pwd_)
{
}
bool IsPasswdOK(const std::string &passwd_)
{
return passwd==passwd_ ? true:false;
}
~User()
{}
};
class UserManager
{
private:
unsigned int assgin_id;
std::unordered_map<unsigned int,User> users;
std::unordered_map<unsigned int,struct sockaddr_in> online_users;
pthread_mutex_t lock;
void Lock()
{
pthread_mutex_lock(&lock);
}
void Unlock()
{
pthread_mutex_unlock(&lock);
}
public:
UserManager():assgin_id(10000)
{
pthread_mutex_init(&lock,NULL);
}
unsigned int Insert(const std::string &n_,const std:: string &s_,const std::string &p_)
{
Lock();
unsigned int id=assgin_id++;
User u(n_,s_,p_);
if(users.find(id)==users.end())
{
users.insert({id,u});
Unlock();
return id;
}
Unlock();
return 1;
}
unsigned int Check(const unsigned int &id,const std::string &passwd)
{
Lock();
auto it=users.find(id);
if(it!=users.end())
{
User &u=it->second;
if(u.IsPasswdOK(passwd))
{
Unlock();
return id;
}
}
Unlock();
return 2;
}
void AddOnlineUser(unsigned int id,struct sockaddr_in &peer)
{
Lock();
auto it =online_users.find(id);
if(it==online_users.end())
{
online_users.insert({id,peer});
}
Unlock();
}
std::unordered_map<unsigned int,struct sockaddr_in> OnlineUser()
{
Lock();
auto online=online_users;
Unlock();
return online;
}
~UserManager()
{
pthread_mutex_destroy(&lock);
}
};
记得装好json库呀。