项目网页聊天室

目录

 1.ubuntu导入sql文件

 2.用.c文件生成库文件

 3.序列化 反序列化测试

 4. 第一次用postman测试注册功能

 5.用postman测试用户登录成功后跳转页面 ---302

 6.项目网页聊天室总结


1.ubuntu导入sql文件

sql文件:

 使用命令mysql -uroot -p <im.sql导入

结果如下:

 2.用.c文件生成库文件

 3.序列化 反序列化测试

vi util.hpp:

#include<iostream>
#include<sstream>
#include<string>
#include<jsoncpp/json/json.h>

namespace im_sys{

  class JsonUtil{
   public:
       static bool Serialize(Json::Value &value,std::string *jsonstr)
       {
          std::stringstream ss;
          Json::StreamWriterBuilder swb;
          Json::StreamWriter *sw = swb.newStreamWriter();
          int ret = sw->write(value,&ss);
          if(ret !=0)
          {
           std::cout<<"json writer failed!\n";
           delete sw;
           return true;
          }
       *jsonstr = ss.str();
       delete sw;
       return true;
      }

      
      static bool UnSerialize(const std::string &jsonstr,Json::Value *value)
       {
         Json::CharReaderBuilder crb;
         Json::CharReader *cr = crb.newCharReader();
         std::string err;
         bool ret = cr->parse(jsonstr.c_str(),jsonstr.c_str() + jsonstr.size(),value,&err);
        if(ret == false)
         {
          delete cr;
          std::cout<<"json parse failed!"<<err<<std::endl;
          return false;
         }
        delete cr;
        return true;
     }
 };
}

vi json_test.cpp:

#include "util.hpp"
 int main()
 {
   std::string str = R"({"username":"wj","password":"1111"})";
   Json::Value val;
   im_sys::JsonUtil::UnSerialize(str,&val);
   std::cout<<val["username"].asString()<<std::endl;
   std::cout<<val["password"].asString()<<std::endl;

   Json::Value err;
   err["result"]=false;
   err["reason"] = "用户名已经被占用!";
   std::string buf;
   im_sys::JsonUtil::Serialize(err,&buf);
   std::cout<<buf<<std::endl;

   return 0;
}

测试结果:

4. 第一次用postman测试注册功能

server.hpp  ---/register功能的相应实现

#include "data.hpp"
#include "mongoose.h"
#include "util.hpp"
namespace im_sys{
#define SERVER_PORT 20000
 class Server
   {
    private:
         static struct mg_mgr *_mgr;
         static UserTable *_user;
   private:
           static void callback(struct mg_connection *c,int ev,void *ev_data,void *fn_data)
           {
             if(ev == MG_EV_HTTP_MSG)
             {
               struct mg_http_message *hm = (struct mg_http_message *)ev_data;
               if(mg_http_match_uri(hm,"/register"))
               {
                 // mg_http_reply(c,200,"","register success!");
                 // return;
                //拿到请求信息的正文,是json格式的用户信息,进行解析
                Json::Value user_info;
                JsonUtil::UnSerialize(hm->body.ptr,&user_info);
                std::string username = user_info["username"].asString();
                std::string password = user_info["password"].asString();
                 
         
                 //在数据库中进行查看用户名是否已经被占用
                 bool ret = _user->Exists(username);
                    if(ret == true)
                    {
                      Json::Value err;
                      err["result"] = false;
                      err["reason"] = "用户名已经被占用";
                      std::string body;
                      JsonUtil::Serialize(err,&body);
                      std::string header = "Content-Type:application/json\r\n";
                      mg_http_reply(c,400,header.c_str(),body.c_str());
                       return; 
                     }
                 //返回结果--注册成功或失败
                 ret = _user->Insert(username,password);
                    if(ret == false)
                    {
                       Json::Value err;
                       err["result"] = false;
                       err["reason"] = "用户插入数据库失败!";
                       std::string body;
                       JsonUtil::Serialize(err,&body);
                       std::string header = "Content-Type: application/json\r\n";
                       mg_http_reply(c,200,header.c_str(),body.c_str());
                       return;
                    }
                    Json::Value err;
                    err["result"] = true;
                    err["reason"] = "注册成功!";
                    std::string body;
                    JsonUtil::Serialize(err,&body);
                    std::string header = "Content-Type:application/json\r\n";
                    mg_http_reply(c,200,header.c_str(),body.c_str());
                       return;
           }
     }
  }

   
    public:
         Server()
         {
            _mgr = new struct mg_mgr();
            _user = new UserTable();
          }
 
        ~Server()
        {
          mg_mgr_free(_mgr);
          delete _mgr;
          delete _user;

        }
        bool RunModule(int port = SERVER_PORT)
        {
          std::string addr = "0.0.0.0:";
          addr += std::to_string(port);
          auto res = mg_http_listen(_mgr,addr.c_str(),callback,_mgr);
          if(res ==NULL)
           {
                std::cout<<"init http listen failed!\n";
                return false;
           }
           
            while(1)
            {
              mg_mgr_poll(_mgr,1000);
            }
             return true;
            
         }
   
         
        
   };
  struct mg_mgr *Server::_mgr = NULL;
  UserTable * Server::_user = NULL;

 }

makefile:

main:data.hpp main.cpp
	g++ -std=c++11 $^ -o $@ -L/usr/lib/mysql  -lmysqlclient -L./lib -lmongoose -ljsoncpp

main.cpp:

#include "data.hpp"
#include "server.hpp"
#define OFFLINE 0
#define ONLINE 1
void DataTest()
{
   im_sys::UserTable * _user = new im_sys::UserTable();

  //插入用户 
  // _user->Insert("王五","1111");
  

  //测试用户是否存在
  // bool ret = _user->Exists("王五");
  // std::cout<<ret<<std::endl;


  // 判断用户是否在线
  // int status = _user->Status("王五");
  // if(status ==ONLINE)
  // {
  //  std::cout<<"online!\n";
  // }
  // else if(status == OFFLINE)
  // {
  // std::cout<<"offline!\n";
  // }
 
  //修改用户状态
  _user->UpdateStatus("王五",OFFLINE);
  
  int status = _user->Status("王五");
   if(status ==ONLINE)
   {
     std::cout<<"online!\n";
   }
   else if(status == OFFLINE)
   {
      std::cout<<"offline!\n";
   }

  //检验密码
 // bool ret = _user->UserPassCheck("王五","1111");
 // if(ret == false)
 // {
 // std::cout<<"用户名密码错误!\n";
 // }
 // else 
 // {
 // std::cout<<"login success!\n";
 // }
 //}

 //修改密码
 // _user->UpdatePasswd("王五","1111");
 // int ret =_user->UserPassCheck("王五","1111");
  
 // if(ret == false)
 // {
 //  std::cout<<"用户名密码错误!\n";
 // }
 //  else 
 // {
 //  std::cout<<"login success!\n";
 // }
 }



void ServerTest()
{
  im_sys::Server *server = new im_sys::Server();
  server->RunModule();
  return;
}

int main()
{
 // DataTest();
 ServerTest();
 return 0;
}

data.hpp:

#ifndef __M_IM_DATA_H__
#define __M_IM_DATA_H__
#include<iostream>
#include<string>
#include<cstdlib>
#include<mutex>
#include<mysql/mysql.h>

namespace im_sys{
#define DB_HOST "127.0.0.1"
#define DB_USER "root"
#define DB_PASS "1111"
#define DB_NAME "db_96"
  static MYSQL *MysqlInit()
  {
    MYSQL *mysql = mysql_init(NULL);
    if(mysql == NULL)
    {
      std::cout<<"mysql init failed!\n";
      return NULL;
    }
    if(mysql_real_connect(mysql,DB_HOST,DB_USER,DB_PASS,DB_NAME,0,NULL,0) == NULL)
    {
     std::cout<<"connect mysql server failed:"<<mysql_error(mysql)<<std::endl;
     mysql_close(mysql);
     return NULL;
    }
     if(mysql_set_character_set(mysql,"utf8") != 0)
    {
     std::cout<<"set mysql client character failed!:"<<mysql_error(mysql)<<std::endl;
     mysql_close(mysql);
     return NULL;
    }
  return mysql;
}
  static void MysqlDestory(MYSQL *mysql)
  {
    if(mysql)
    {
      mysql_close(mysql);
    }
    return;
  }

  static bool MysqlQuery(MYSQL *mysql,const std::string &sql)
  {
   int ret = mysql_query(mysql,sql.c_str());
   if(ret != 0)
   {
     std::cout<<sql<<std::endl;
     std::cout<<"query failed:"<<mysql_error(mysql)<<std::endl;
     return false;
   }
  return true;
 }

  class UserTable{

    private:
         MYSQL *_mysql;
         std::mutex _mutex;
    public:
        UserTable():_mysql(NULL)
        {
         _mysql = MysqlInit();
         if(_mysql == NULL)
         {
          exit(-1);
         }
        }

        ~UserTable()
        {
           MysqlDestory(_mysql);
        }
        bool Insert(const std::string &name,const std::string &pass)
        {
         #define USER_INSERT "insert im_user values(null,'%s',MD5('%s'),0,now(),now());"
        char sql[4096] = {0};
        sprintf(sql,USER_INSERT,name.c_str(),pass.c_str());
        return MysqlQuery(_mysql,sql);

        }

     bool Exists(const std::string &name)
     {
       #define USER_EXISTS "select id from im_user where name='%s';"
       char sql[4096] = {0};
       sprintf(sql,USER_EXISTS,name.c_str());
       bool ret = MysqlQuery(_mysql,sql);
       if(ret == false)
       {
        return false;
       }
       MYSQL_RES *res = mysql_store_result(_mysql);
       int num = mysql_num_rows(res);
       if(num!=0)
        {
          mysql_free_result(res);
          return true;
        }
       mysql_free_result(res);
      return false;
    }
  
      bool UserPassCheck(const std::string &name,const std::string &pass)
     {
         #define USER_CHECK "select id from im_user where name='%s' and pass=MD5('%s');"
        char sql[4096] = {0};
        sprintf(sql,USER_CHECK,name.c_str(),pass.c_str());
        bool ret = MysqlQuery(_mysql,sql);
        if(ret == false)
         {
          return false;
         }
          MYSQL_RES *res = mysql_store_result(_mysql);
         int num = mysql_num_rows(res);
         if(num!=0)
          {
            mysql_free_result(res);
            return true;
          }
        mysql_free_result(res);
           return false;
     }
      int  Status(const std::string &name)
     {
      #define USER_STATUS "select status from im_user where name='%s';"
       char sql[4096]={0};
       sprintf(sql,USER_STATUS,name.c_str());
       bool ret = MysqlQuery(_mysql,sql);
       if(ret == false)
       {
         return false;
       }
       MYSQL_RES *res = mysql_store_result(_mysql);
       int num = mysql_num_rows(res);
       if(num == 0)
       {
         mysql_free_result(res);
          return -1;
       }
       MYSQL_ROW row = mysql_fetch_row(res);
       int status = std::stoi(row[0]);
       mysql_free_result(res);
       return status;
      }
     bool UpdateStatus(const std::string &name,int status)
     {
        #define USER_UPDATE_STATUS "update im_user set status=%d,stime=now() where name='%s';"
       char sql[4096] = {0};
       sprintf(sql,USER_UPDATE_STATUS,status,name.c_str());
       return MysqlQuery(_mysql,sql);
     }
    
   
    bool UpdatePasswd(const std::string &name,const std::string &pass)
     {
       #define USER_UPDATE_PASS "update im_user set pass=MD5('%s') where name='%s';"
      char sql[4096] = {0};
      sprintf(sql,USER_UPDATE_PASS,pass.c_str(),name.c_str());
      return MysqlQuery(_mysql,sql);
   }


     };

}

#endif  

结果如下:

5.用postman测试用户登录成功后跳转页面 ---302

server.hpp:

#include "data.hpp"
#include "mongoose.h"
#include "util.hpp"
#include <unordered_map>
#include <time.h>
namespace im_sys{
#define SERVER_PORT 20000
#define OFFLINE 0
#define ONLINE 1
#define WWWROOT "./wwwroot/"

struct IMSession
{
   int status;
   std::string session_id;//系统时间
   std::string username;
   time_t ctime;//会话创建时间
   time_t ltime;//最后操作时间
   struct mg_connection *conn;//当前用户对一个的mongoose连接
};



 class Server
   {
    private:
         static struct mg_mgr *_mgr;
         static UserTable *_user;
         static std::unordered_map<std::string,IMSession> _session;
   private:
         static std::string ConResp(bool res,const std::string &info)
          {
           Json::Value err;
           err["result"] = res;
           err["reason"] = info;
           std::string body;
           JsonUtil::Serialize(err,&body);
           return body;
           }

         static  std::string CreateIMSession(struct mg_connection *c,const std::string &username)
           {
             struct IMSession s;
             s.conn = c;
             s.username = username;
             s.status = ONLINE;
             s.ctime = time(NULL);
             s.ltime = time(NULL);
             s.session_id = std::to_string(time(NULL));
             _session[s.session_id] = s;
             return s.session_id;
           }


           static void Login(struct mg_connection *c,struct mg_http_message *hm)
          {
             //拿到请求正文,进行json反序列化得到用户名和密码
             Json::Value user_info;
             JsonUtil::UnSerialize(hm->body.ptr,&user_info);
             std::string username = user_info["username"].asString();
             std::string password = user_info["password"].asString();
            //在数据库中验证用户名和密码
             bool ret = _user->UserPassCheck(username,password);
            if(ret == false)
              {
                std::string body = ConResp(false,"用户名密码错误");
                std::string header = "Content-Type: application/json\r\n";
                mg_http_reply(c,400,header.c_str(),body.c_str());
                return;
             }

            //判断用户是否已经在线,若已经在线,则不能重复登录
               ret = _user->Status(username);
               if(ret == ONLINE)
              {
                std::string body = ConResp(false,"用户已登录");
                std::string header = "Content-Type: application/json\r\n";
                mg_http_reply(c,400,header.c_str(),body.c_str());
                return;
               }
            //没在线,则修改用户为在线状态,并且返回登录成功,设置SetCookie包括用户名、用户状态
            std::string ssid =  CreateIMSession(c,username);
            ret = _user->UpdateStatus(username,ONLINE);
            if(ret == false)
            {
              std::string body = ConResp(false,"修改用户状态失败");
              std::string header = "Content-Type: application/json\r\n";
              mg_http_reply(c,500,header.c_str(),body.c_str());
               return;
            }

            //登录成功需要让客户端去请求一个聊天页面
            std::stringstream headers;
            headers<< "Location: /chat.html\r\n";
            headers <<"Set-Cookie: SSID=" <<ssid<<";Path=/\r\n";
           
            std::string body = ConResp(true,"登录成功");
            mg_http_reply(c,302,headers.str().c_str(),body.c_str());
            return;
      }

           static void  Register(struct mg_connection *c,struct mg_http_message *hm)
          {
     
                //拿到请求信息的正文,是json格式的用户信息,进行解析
                Json::Value user_info;
                JsonUtil::UnSerialize(hm->body.ptr,&user_info);
                std::string username = user_info["username"].asString();
                std::string password = user_info["password"].asString();
                 
         
                 //在数据库中进行查看用户名是否已经被占用
                 bool ret = _user->Exists(username);
                    if(ret == true)
                    {
                      Json::Value err;
                      err["result"] = false;
                      err["reason"] = "用户名已经被占用";
                      std::string body;
                      JsonUtil::Serialize(err,&body);
                      std::string header = "Content-Type:application/json\r\n";
                      mg_http_reply(c,400,header.c_str(),body.c_str());
                       return; 
                     }
                 //返回结果--注册成功或失败
                 ret = _user->Insert(username,password);
                    if(ret == false)
                    {
                       Json::Value err;
                       err["result"] = false;
                       err["reason"] = "用户插入数据库失败!";
                       std::string body;
                       JsonUtil::Serialize(err,&body);
                       std::string header = "Content-Type: application/json\r\n";
                       mg_http_reply(c,200,header.c_str(),body.c_str());
                       return;
                    }
                    Json::Value err;
                    err["result"] = true;
                    err["reason"] = "注册成功!";
                    std::string body;
                    JsonUtil::Serialize(err,&body);
                    std::string header = "Content-Type:application/json\r\n";
                    mg_http_reply(c,200,header.c_str(),body.c_str());
                       return;
          }

          static bool GetSession(struct mg_connection *c,IMSession *session)
           {
              auto it = _session.begin();
              for(;it!=_session.end();++it)
              {
                if(it->second.conn == c)
                  {
                    *session = it->second;
                     return true;
                   }
               }
            return false;
           }

           static bool DelSession(IMSession &session)
           {
             auto it = _session.find(session.session_id);
             if(it == _session.end())
             {
               std::cout<<"not find session\n";
               return false;
             }
            _session.erase(it);
            return true;
            }



            static void ConnClose(struct mg_connection *c)
           {
             //找到连接对应的session
              IMSession session;
             bool ret = GetSession(c,&session);
             if(ret == false)
             {
                std::cout<<"have no session info!\n";
                return;
             }
             //修改数据库中的用户状态
              ret = _user->UpdateStatus(session.username,OFFLINE);
              if(ret == false)
              {
               std::cout<<"update status offline failed!\n";
               return;
              }
             //删除会话信息
              DelSession(session);
              } 

           static void callback(struct mg_connection *c,int ev,void *ev_data,void *fn_data)
           {
             if(ev == MG_EV_HTTP_MSG)
             {
               struct mg_http_message *hm = (struct mg_http_message *)ev_data;
                 if(mg_http_match_uri(hm,"/register"))
                 {
                   // mg_http_reply(c,200,"","register success!");
                   // return;
                   Register(c,hm);
                 }
                 else if(mg_http_match_uri(hm,"/login"))
                 {
                   //登录请求
                     Login(c,hm);
                 }
                 else if(mg_http_match_uri(hm,"/cws"))
                 {
                   //协议切换请求
                 }
                 else
                 {
                    //静态页面请求
                      struct mg_http_serve_opts opts = {.root_dir = WWWROOT};
                      mg_http_serve_dir(c,hm,&opts);
                 }
            }
            else if(ev == MG_EV_WS_OPEN)
              {
                //websocket握手成功

              }
            else if(ev == MG_EV_WS_MSG)
              {
                //收到聊天消息
              }
            else if(ev == MG_EV_CLOSE)
              {
                //表示连接断开
                 ConnClose(c); 
              }

 


  }

   
    public:
         Server()
         {
            _mgr = new struct mg_mgr();
            _user = new UserTable();
          }
 
        ~Server()
        {
          mg_mgr_free(_mgr);
          delete _mgr;
          delete _user;

        }
        bool RunModule(int port = SERVER_PORT)
        {
          std::string addr = "0.0.0.0:";
          addr += std::to_string(port);
          auto res = mg_http_listen(_mgr,addr.c_str(),callback,_mgr);
          if(res ==NULL)
           {
                std::cout<<"init http listen failed!\n";
                return false;
           }
           
            while(1)
            {
              mg_mgr_poll(_mgr,1000);
            }
             return true;
            
         }
   
         
        
   };
  struct mg_mgr *Server::_mgr = NULL;
  UserTable * Server::_user = NULL;
  std::unordered_map<std::string,IMSession>Server::_session;

 }

 makefile:

main:data.hpp main.cpp
	g++ -std=c++11 $^ -o $@ -L/usr/lib/mysql  -lmysqlclient -L./lib -lmongoose -ljsoncpp

结果如下:

 6.项目网页聊天室总结

环境搭建:

 1)ubuntu安装mysql    

https://blog.youkuaiyun.com/weixin_42209572/article/details/98983741https://blog.youkuaiyun.com/weixin_42209572/article/details/98983741

2)ubuntu安装mongoose http库   

链接:https://pan.baidu.com/s/1kMaSz5rduAr5Cs0a88LhrQ  提取码:1111 

      ---主要用到了mongoose.cmongoose.h两个文件

3)ubuntu安装jsoncpp库 

(65条消息) Ubuntu16.04 jsoncpp 的安装_只此冒泡君的博客-优快云博客_ubuntu安装jsoncpphttps://blog.youkuaiyun.com/qing310820/article/details/84283650

4)打开VM虚拟机,通过ifconfig获取主机名,在项目chat.html中修改ws_init方法中的主机名,端口号不用修改。

结果:

登录三个会话进行聊天,测试结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值