第一个项目:基于TCP的聊天项目

来源:互联网 发布:英国脱欧最新进展 知乎 编辑:程序博客网 时间:2024/06/01 18:26
  1. 主要功能实现:

    Register   Login  TalkToOne  TalkToGroup  GetList   Exit。

  2. 使用工具:

    Json打包数据  Libevent实现I/O复用  mysql数据库存储用户相关信息及离线消息。

  3. 项目框架:

    Cli:

    提供用户选择服务:Register、Login、Geilist、Talk to one、Talk to group、exit。

    用户需选择服务,输入相关信息

    根据不同服务调用相应功能函数

    利用json打包数据发送给服务器

    接收到服务器的回应后,若登陆成功,需要启动线程接收消息并打印

    用户再次选择之后的服务。

     

    Ser:

    利用半同步半反应堆完成线程间任务分工

    主线程接收连接,将套接字分给当前较为空闲的子线程监听

    通过socket_pair实现父子线程间信息交互

    子线程通过libevent实现I/O复用,当有事件来的时候通过MVC处理

    根据请求类型的不同,使用相应的视图来处理。

    感谢老师梳理的框架030 ser框架如图

  4. 项目实现:

    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]));

    }

    }

     

     

     

原创粉丝点击