mongoose集成在项目中,主要还是因为轻量,而且方便。一个.h和一个.c文件就OK。其次,mongoose的封装也不错,使用简单。 本文以mongoose websocket client 及Qt- QWebSocketServer 为例。
server端:
#include <QCoreApplication>
#include <QtWebSockets/QWebSocketServer>
#include <QDebug>
#include <QWebSocket>
struct A{
int a;
double b;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QWebSocketServer server(QString("WSSERVER"),QWebSocketServer::NonSecureMode);
server.listen(QHostAddress::Any,8000);
QObject::connect(&server,&QWebSocketServer::newConnection,[&server](){
QWebSocket *pSocket = server.nextPendingConnection();
qDebug() << "Client connected:" << pSocket->peerName() << pSocket->origin();
QObject::connect(pSocket, &QWebSocket::textMessageReceived,
[](const QString &message){
qDebug() << __FUNCTION__ << __LINE__ << message;
});
QObject::connect(pSocket, &QWebSocket::binaryMessageReceived,
[](const QByteArray &message){
qDebug() << "----------------------------------------";
qDebug() << __FUNCTION__ << __LINE__ << message;
struct A* obja = (A*)message.data();
qDebug() << obja->a << obja->b << Qt::endl;
});
QObject::connect(pSocket, &QWebSocket::disconnected,
[](){
qDebug() << "client disconnected;";
});
});
return a.exec();
}
不得不说,基于Qt的QWebsocketServer 创建 ws server端也是很方便。
client 端
#include <QCoreApplication>
#include <QString>
#include "mongoose.h"
static const char *s_url = "ws://localhost:8000/websocket";
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_ERROR) {
LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
} else if (ev == MG_EV_WS_OPEN) {
char buf[] = "11111111111111111111111111111111111111111111111111111111"
"22222222222222222222222222222222222222222222222222222222"
"33333333333333333333333333333333333333333333333333333333"
"44444444444444444444444444444444444444444444444444444444"
"55555555555555555555555555555555555555555555555555555555"
"66666666666666666666666666666666666666666666666666666666"
"77777777777777777777777777777777777777777777777777777777"
"s888888888888888";
char msg[65535] = {0};
for(int i = 0;i<150;++i){
strcat(msg,buf);
}
printf(" strlen of msg : strlen = %d %s\n",strlen(msg),msg);
mg_ws_send(c, msg, strlen(msg), WEBSOCKET_OP_TEXT);
::Sleep(1000);
struct A{
int a;
double b;
};
A* obj_a = new A;
obj_a->a = 10;
obj_a->b = 13.2;
mg_ws_send(c,(char*)obj_a,sizeof(A),WEBSOCKET_OP_BINARY);
} else if (ev == MG_EV_WS_MSG) {
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
printf("GOT ECHO REPLY: [%.*s]\n", (int) wm->data.len, wm->data.ptr);
}
if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE || ev == MG_EV_WS_MSG) {
*(bool *) fn_data = true;
}
}
int main(void) {
struct mg_mgr mgr;
bool done = false;
struct mg_connection *c;
mg_mgr_init(&mgr);
c = mg_ws_connect(&mgr, s_url, fn, &done, NULL);
while (c && done == false) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
return 0;
}
mongoose websocket client 端的实现也很简单,mg_ws_connect 注册消息回调事件,mg_ws_send 可以用于发送指定的文本或者二进制数据。
plus Protobuf
上一篇 Protobuf编译 中记录了protobuf的编译方法,下面我们说说protobuf的使用。
1、编写 .proto 文件
syntax = "proto3";
package WS;
option optimize_for = LITE_RUNTIME;
message req_login
{
string username = 1;
string password = 2;
}
2、编译.proto文件,生成对应的.h 和.cpp 文件
protoc --cpp_out=./ *.proto
3、集成到项目中
以Qt为例。在pro文件中增加如下配置:
INCLUDEPATH += . \
.. \
install/include
HEADERS += \
install/bin/ubasestruct.pb.h
SOURCES += \
install/bin/ubasestruct.pb.cc \
# 添加protobuf 静态库文件
LIBS += -L$$PWD/install/lib/ -llibprotobufd
LIBS += -L$$PWD/install/lib/ -llibprotocd
LIBS += -L$$PWD/install/lib/ -llibprotobuf-lited
INCLUDEPATH += $$PWD/install/lib
DEPENDPATH += $$PWD/install/lib
对应的server 端接收数据反序列化 例如:
#include <QCoreApplication>
#include <QtWebSockets/QWebSocketServer>
#include <QDebug>
#include <QWebSocket>
#include "install/bin/ubasestruct.pb.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QWebSocketServer server(QString("WSSERVER"),QWebSocketServer::NonSecureMode);
server.listen(QHostAddress::Any,8000);
QObject::connect(&server,&QWebSocketServer::newConnection,[&server](){
QWebSocket *pSocket = server.nextPendingConnection();
qDebug() << "Client connected:" << pSocket->peerName() << pSocket->origin();
QObject::connect(pSocket, &QWebSocket::textMessageReceived,
[](const QString &message){
qDebug() << __FUNCTION__ << __LINE__ << message;
});
QObject::connect(pSocket, &QWebSocket::binaryMessageReceived,
[](const QByteArray &message){
qDebug() << __FUNCTION__ << __LINE__ << message;
WS::req_login rsp2{};
if (!rsp2.ParseFromArray(message,message.count())) {
std::cout << "parse error\n";
return;
}
std::cout << rsp2.username() << " <=====> " << rsp2.password() << std::endl;
});
QObject::connect(pSocket, &QWebSocket::disconnected,
[](){
qDebug() << "client disconnected;";
});
});
return a.exec();
}
mongoose 客户端数据进行序列化:
#include <QCoreApplication>
#include <QString>
#include "install/bin/ubasestruct.pb.h"
#include "mongoose.h"
static const char *s_url = "ws://localhost:8000/websocket";
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_ERROR) {
LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
} else if (ev == MG_EV_WS_OPEN) {
char buf[] = "11111111111111111111111111111111111111111111111111111111"
"22222222222222222222222222222222222222222222222222222222"
"33333333333333333333333333333333333333333333333333333333"
"44444444444444444444444444444444444444444444444444444444"
"55555555555555555555555555555555555555555555555555555555"
"66666666666666666666666666666666666666666666666666666666"
"77777777777777777777777777777777777777777777777777777777"
"s888888888888888";
char msg[65535] = {0};
for(int i = 0;i<150;++i){
strcat(msg,buf);
}
printf(" strlen of msg : strlen = %d %s\n",strlen(msg),msg);
mg_ws_send(c, msg, strlen(msg), WEBSOCKET_OP_TEXT);
WS::req_login req;
req.set_username(std::string(msg));
req.set_password("12222222");
int sizeArray = req.ByteSizeLong();
QByteArray * qbArray = new QByteArray[sizeArray];
req.SerializeToArray(qbArray,sizeArray);
mg_ws_send(c,(char*)qbArray,sizeArray,WEBSOCKET_OP_BINARY);
} else if (ev == MG_EV_WS_MSG) {
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
printf("GOT ECHO REPLY: [%.*s]\n", (int) wm->data.len, wm->data.ptr);
}
if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE || ev == MG_EV_WS_MSG) {
*(bool *) fn_data = true;
}
}
常用的序列化和反序列化API :
说明 | 序列化 | 反序列化 |
---|
数组的序列化和反序列化 | bool ParseFromArray(const void* data, int size); | bool SerializeToArray(void* data, int size) const; | C++ String的序列化和反序列化 | bool SerializeToString(string* output) const; | bool ParseFromString(const string& data); | 文件描述符序列化和反序列化 | bool SerializeToFileDescriptor(int file_descriptor) const; | bool ParseFromFileDescriptor(int file_descriptor); | C++ stream 序列化和反序列化 | bool SerializeToOstream(ostream* output) const; | bool ParseFromIstream(istream* input); | code stream的序列化和反序列化 | SerializeToCodedStream | ParseFromCodedStream |
|