【项目实践】群聊即时通信工具

IM是一款使用开源websocket框架mongoose编写网页版本,http+
mongoose+session+mysql+jsoncpp的技术构成的群聊即时通信工具。

  1. http && websocket协议
    现在,很多网站为了实现推送技术,所用的技术都是轮询。在特定的时间间隔(如每一秒),由浏览器对服务器发起HTTP请求,然后由服务器返回数据给浏览器。由于HTTP协议是惰性的,只有客户端发起请求,服务器才会返回数据。轮询技术实现的前提条件同样是基于这种机制。而WebSocket属于服务端推送技术,本质是一种应用层协议,可以实现持久连接的全双工双向通信。
    WebSocket是一种协议,与HTTP协议一样位于应用层,都是TCP/IP协议的子集。HTTP协议是单向通信协议,只有客户端发起HTTP请求,服务端才会返回数据。而WebSocket协议是双向通信协议,在建立连接之后,客户端和服务器都可以主动向对方发送或接受数据。WebSocket协议建立的前提需要借助HTTP协议,建立连接之后,持久连接的双向通信就与HTTP协议无关了。

  2. mongoose
    Mongoose是c语言写成的网络库。它为TCP、UDP、HTTP、WebSocket、CoAP、MQTT实现了事件驱动型的非阻塞api。
    特性:

  • 跨平台:可在linux/unix macos QNX eCos Windows Android Iphone FreeRtos上运行
  • 原生支持PicoTCP的嵌入式tcp/ip协议栈,支持LWIP嵌入式tcp/ip协议栈
  • 单线程,异步,非阻塞核心与简单的基于事件的API

在运行和占用很小的内存,源代码符合ISO C 和ISO C++规范,使用时,仅需要包含 mongoose.c mongoose.h 到你的工程即可完成整合。Mongoose使嵌入式网络编程快速,健壮,轻松。

基本逻辑:
首先,设计基本框架
然后,先完成基本的聊天逻辑
在能够让我们访问数据库的基础上,访问我们之前所建立的表
最后,完成登录逻辑

struct session{
	//登录用户的session信息数据结构,有部分内容是要写到客户端cookie中的
};
//M
class IM_MysqlClient{
	//访问数据库客户端,使用C connect封装
};
//C
class IM_Controller{
	//控制器
};
//V
class IM_Server{
	//服务器功能
};

核心代码:
//ImServer.cc

#include "ImServer.hpp"

int main()
{
    //MysqlClient *mc = new MysqlClient();
    //mc->ConnectMysql();
    //mc->InsertUser("赵六", "4321");
    //delete mc;
    ImServer *im = new ImServer();
    im->InitServer();
    im->Start();
    return 0;
}

//ImServer.hpp

#pragma once

#include 
#include 
#include "Util.hpp"
#include "mongoose.h"
#include "mysql.h"

#define IM_DB "im_db"
#define MY_PORT 3306

struct mg_serve_http_opts s_http_server_opts;

//model
class MysqlClient{
    private:
        MYSQL *my;

        bool ConnectMysql()
        {
            my = mysql_init(NULL);
            mysql_set_character_set(my, "utf8");
            if(!mysql_real_connect(my, "localhost",\
                        "root", "", IM_DB, MY_PORT, NULL, 0)){
                cerr << "connect mysql error" << endl;
                return false;
            }
            cout << "connect mysql success" << endl;

            return true;
        }
    public:
        MysqlClient(){
        }
        bool InsertUser(string name, string passwd)
        {
            ConnectMysql();
            string sql = "INSERT INTO user (name, passwd) values(\"";
            sql += name;
            sql += "\",\"";
            sql += passwd;
            sql += "\")";
            cout << sql << endl;
            if(0 == mysql_query(my, sql.c_str())){
                return true;
            }
            mysql_close(my);
            return false;

        }
        bool SelectUser(string name, string passwd)
        {
            ConnectMysql();
            string sql = "SELECT * FROM user WHERE name=\"";
            sql += name;
            sql += "\" AND passwd=\"";
            sql += passwd;
            sql += "\"";
            cout << sql << endl;
            if(0 != mysql_query(my, sql.c_str())){
                //return false;
            }
            cout << "select done" << endl;
            //judge result not empty
            mysql_close(my);
            return true;
        }
        ~MysqlClient(){
        }
};

//MVC
class ImServer{
    private:
        string port;
        struct mg_mgr mgr;
        struct mg_connection *nc;
        volatile bool quit;
        static MysqlClient mc;
    public:
        ImServer(string _port = "8080"):port(_port),quit(false)
        {
        }
        static void Broadcast(struct mg_connection *nc, string msg)
        {
            struct mg_connection *c;
            for (c = mg_next(nc->mgr, NULL); \
                    c != NULL; c = mg_next(nc->mgr, c)) {
                //if (c == nc) continue; /* Don't send to the sender. */
                mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT,\
                        msg.c_str(), msg.size());
            }
        }
        static void RegisterHandler(mg_connection *nc, int ev, void *data)
        {

        }
        static void LoginHandler(mg_connection *nc, int ev, void *data)
        {
            struct http_message *hm = (struct http_message*)data;
            string method = Util::mgStrToString(&(hm->method));
            if(method == "POST"){
                string body = Util::mgStrToString(&hm->body);
                std::cout << "login handler: " << body << std::endl;
                string name, passwd;
                if(Util::GetNameAndPasswd(body, name, passwd)){
                    if(mc.SelectUser(name, passwd)){
                        string test = "{\"result\":12}";
                        nc->flags |= MG_F_SEND_AND_CLOSE; //相应完毕,完毕链接
                        mg_printf(nc, "HTTP/1.1 200 OK\r\n");
                        mg_printf(nc, "Content-Length: %d\r\n\r\n", test.size());
                        mg_printf(nc, test.c_str());
                    }
                }
                //mg_serve_http(nc, hm, s_http_server_opts);
            }
            else{
                mg_serve_http(nc, hm, s_http_server_opts);
            }
        }
        static void EventHandler(mg_connection *nc, int ev, void *data)
        {
            switch(ev){
                case MG_EV_HTTP_REQUEST:{
                        struct http_message *hm = (struct http_message*)data;
                        mg_serve_http(nc, hm, s_http_server_opts);
                        //nc->flags |= MG_F_SEND_AND_CLOSE;
                    }
                    break;
                case MG_EV_WEBSOCKET_HANDSHAKE_DONE:{
                        Broadcast(nc, "some body join...");
                    }
                    break;
                case MG_EV_WEBSOCKET_FRAME:{
                        struct websocket_message *wm = (struct websocket_message*)data;
                        struct mg_str ms = {(const char*)wm->data, wm->size};
                        string msg = Util::mgStrToString(&ms);
                        Broadcast(nc, msg);
                    }
                    break;
                case MG_EV_CLOSE:
                    std::cout << "close link" << std::endl;
                    break;
                default:
                    cout << "other ev: " << ev << endl;
                    break;
            }
        }
        void InitServer()
        {
            mg_mgr_init(&mgr, NULL);
            nc = mg_bind(&mgr, port.c_str(), EventHandler);

            mg_register_http_endpoint(nc, "/login", LoginHandler);
            mg_register_http_endpoint(nc, "/register", RegisterHandler);

            mg_set_protocol_http_websocket(nc);
            s_http_server_opts.document_root = "web";
        }
        void Start()
        {
            int timeout = 1000000;
            while(!quit){
                mg_mgr_poll(&mgr, timeout);
                cout << "time out ..." << endl;
            }
        }
        ~ImServer()
        {
            mg_mgr_free(&mgr);
        }
};
MysqlClient ImServer::mc;

//Makefile

cc=g++
bin=ImServer
src=ImServer.cc mongoose/mongoose.c
include=-Imongoose/ -Imysql/include
lib=-Lmysql/lib
lib_name=-lmysqlclient -ljsoncpp #-lpthread -ldl -static

$(bin):$(src)
	$(cc) -Wall -o $@ $^ -std=c++11 $(include) $(lib) $(lib_name)
.PHONY:clean
clean:
	rm -f $(bin)

你可能感兴趣的:(项目)