在介绍网页版群聊系统之前,首先介绍一下此项目中用到的两个非常重要的工具:websocket协议和mongoose框架。
一个客户端发送消息之后,需要服务器主动把消息广播给所有的在线用户,但是http协议是惰性的,哪个客户端向服务器发起http请求,此客户端才会收到服务器的响应,这种机制不能满足群聊的要求,所以我们利用websocket协议来实现这个目的,websocket协议是全双工通信的,建立连接之后,客户端和服务器都可以主动接收或发送数据,下面我先介绍一下websocket协议:
websocket协议位于应用层,目标是在一个独立的持久连接上提供全双工双向通信,创建websocket协议之后,会有一个HTTP请求发给浏览器,服务器响应之后,建立的连接会利用HTTP协议将协议升级转换为websocket协议,随后再进行通信。也就是说websocket协议的建立需要先借助HTTP协议,在服务器返回101状态码之后,就可以进行websocket全双工通信了,此后就和HTTP没关系了。
协议如何升级:
HTTP请求的报头中的Upgrade设置为websocket可将协议升级为websocket协议。
现在开始进入正题啦~~~
准备工作
cd jsoncpp-1.8.3
mkdir build
cd build
cmake ..
make
sudo make install
网页版群聊系统是基于websocket协议和mongoose框架实现的一个即时通信工具,利用session和cookie管理用户状态,用户注册账号并登录之后即可进行群聊。
C++11 STL、HTTP协议、websocket协议、mongoose框架、session、cookie、mysql c connect、jsoncpp
服务器初始化
void InitServer()
{
signal(SIGPIPE, SIG_IGN);
mg_mgr_init(&mgr,NULL);
nc = mg_bind(&mgr, port.c_str(), EventHandler);
//注册对登录页面的处理动作
mg_register_http_endpoint(nc, "/LH", LoginHandler);
mg_register_http_endpoint(nc, "/RH", RegisterHandler);
//设置监听连接为websocket
mg_set_protocol_http_websocket(nc);
s_http_server_opts.document_root = "web";
mg_set_timer(nc, mg_time()+SESSION_CHECK_INTERVAL);
}
启动服务器
void Start()
{
int timeout = 1000000;
while(!quit)
{
mg_mgr_poll(&mgr, timeout);
// cout << "time out" << endl;
}
}
回调函数
//回调函数
static void EventHandler(mg_connection* nc, int ev, void* data)
{
switch(ev)
{
case MG_EV_HTTP_REQUEST:
{
cout << "http request" << endl;
//获取整个http请求信息
struct http_message* hm = (struct http_message*)data;
string uri = Util::MgStrToString(&hm->uri);
cout << "debug: " << uri << endl;
if(uri.empty() || uri == "/" || uri == "/index.html")
{
if(sn.IsLogin(hm))
{
mg_serve_http(nc, hm, s_http_server_opts);
}
else
{
mg_http_send_redirect(nc, 302, mg_mk_str("/login.html"), mg_mk_str(NULL));
}
}
else
{
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;
//提取出发出去的websocket数据
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:
// cout << "link quit..." << endl;
break;
case MG_EV_TIMER:
sn.CheckSession();
mg_set_timer(nc, mg_time()+SESSION_CHECK_INTERVAL);
default:
// cout << "other ev:" << ev << endl;
break;
}
}
广播功能的实现
static void Broadcast(struct mg_connection* nc, string msg)
{
struct mg_connection *c;
//链表头部赋给c
for(c = mg_next(nc->mgr, NULL);c != NULL; c = mg_next(nc->mgr, c))
{
//向特定连接发送消息
mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, msg.c_str(), msg.size());
}
}
以上就是代码主要功能点的介绍,详细代码请参考https://github.com/sunshineyam/project