Linux下聊天软件的实现

·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库呀。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值