解码websocket (c++)
来源:互联网 发布:linux jdk 安装bad 编辑:程序博客网 时间:2024/06/03 11:30
websocket 是web常用的协议,可用来支持自定义协议。
以下是用c++根据websocket握手协议websocket协议格式来处理websocket 协议解码
(1)第一步,websocket升级协议
判断是否是http协议,并且是websocket的升级握手协议
E_CODEC_STATUS CodecWebSocketJson::Decode(tagConnectionAttr* pConn,MsgHead& oMsgHead, MsgBody& oMsgBody){ if (eConnectStatus_init == pConn->ucConnectStatus)//必须以http请求初始化握手协议,否则不是WebSocketJson协议 { if (pConn->pRecvBuff->ReadableBytes() >= 5)//目前只支持Get post 的初始化握手协议 { //响应的是("HTTP/", 5) //请求的是("GET ", 4) ("POST ", 5) const char* pReadAddr = pConn->pRecvBuff->GetRawReadBuffer();//处理http请求 if ((memcmp(pReadAddr, "GET ", 4) == 0) || memcmp(pReadAddr, "POST ", 5) == 0) { LOG4_TRACE("%s() pBuff->ReadableBytes() = %u:%s", __FUNCTION__, pConn->pRecvBuff->ReadableBytes(), pConn->pRecvBuff->ToString().c_str()); HttpMsg oHttpMsg; E_CODEC_STATUS eCodecStatus = Decode(pConn->pRecvBuff, oHttpMsg); if (CODEC_STATUS_OK == eCodecStatus) { std::string upgrade; for (int i = 0; i < oHttpMsg.headers_size(); ++i) { if (std::string("x-cmd") == oHttpMsg.headers(i).header_name() || std::string("x-CMD") == oHttpMsg.headers(i).header_name() || std::string("x-Cmd") == oHttpMsg.headers(i).header_name()) { oMsgHead.set_cmd(atoi(oHttpMsg.headers(i).header_value().c_str())); } else if (std::string("x-seq") == oHttpMsg.headers(i).header_name() || std::string("x-SEQ") == oHttpMsg.headers(i).header_name() || std::string("x-Seq") == oHttpMsg.headers(i).header_name()) { oMsgHead.set_seq(strtoul(oHttpMsg.headers(i).header_value().c_str(),NULL, 10)); } else if (std::string("Upgrade") == oHttpMsg.headers(i).header_name()) { upgrade = oHttpMsg.headers(i).header_value(); } } if(strcasecmp(upgrade.c_str(),"websocket") == 0)//websocket升级握手消息 { pConn->ucConnectStatus = eConnectStatus_ok;//握手协议接收成功 oMsgBody.set_body(oHttpMsg.SerializeAsString()); oMsgHead.set_msgbody_len(oMsgBody.ByteSize()); } else { LOG4_DEBUG("%s() CodecWebSocketJson need to init connect status with http head Upgrade for websocket",__FUNCTION__); return CODEC_STATUS_ERR; } } return eCodecStatus; } } LOG4_DEBUG("%s() CodecWebSocketJson need to init connect status with http post or get request",__FUNCTION__); return CODEC_STATUS_ERR; } return Decode(pConn->pRecvBuff,oMsgHead,oMsgBody);}
(2)第二部,http协议
解码http
//把缓存pBuff 中的内容换序列化到oHttpMsgE_CODEC_STATUS CodecWebSocketJson::Decode(loss::CBuffer* pBuff, HttpMsg& oHttpMsg){ LOG4_TRACE("%s()", __FUNCTION__); if (pBuff->ReadableBytes() == 0) { LOG4_DEBUG("no data..."); return (CODEC_STATUS_PAUSE); } loss::CBuffer oHttpBuff; // 用于debug输出Decode前的http协议 oHttpBuff.Write(pBuff->GetRawReadBuffer(), pBuff->ReadableBytes()); oHttpBuff.WriteByte('\0'); LOG4_TRACE("%s", oHttpBuff.GetRawReadBuffer()); //反序列化时构造oHttpMsg m_parser_setting.on_message_begin = OnMessageBegin; m_parser_setting.on_url = OnUrl; m_parser_setting.on_status = OnStatus; m_parser_setting.on_header_field = OnHeaderField; m_parser_setting.on_header_value = OnHeaderValue; m_parser_setting.on_headers_complete = OnHeadersComplete; m_parser_setting.on_body = OnBody; m_parser_setting.on_message_complete = OnMessageComplete; m_parser_setting.on_chunk_header = OnChunkHeader; m_parser_setting.on_chunk_complete = OnChunkComplete; m_parser.data = &oHttpMsg; http_parser_init(&m_parser, HTTP_REQUEST); int iReadIdx = pBuff->GetReadIndex(); size_t uiReadableBytes = pBuff->ReadableBytes(); size_t uiLen = http_parser_execute(&m_parser, &m_parser_setting, pBuff->GetRawReadBuffer(), uiReadableBytes); if (!oHttpMsg.is_decoding()) { if (m_parser.http_errno != HPE_OK) { LOG4_ERROR("Failed to parse http message for cause:%s", http_errno_name((http_errno )m_parser.http_errno)); return (CODEC_STATUS_ERR); } pBuff->AdvanceReadIndex(uiLen); } else { LOG4_TRACE("decoding..."); return (CODEC_STATUS_PAUSE); } for (int i = 0; i < oHttpMsg.headers_size(); ++i) { if (std::string("Content-Encoding") == oHttpMsg.headers(i).header_name() && std::string("gzip") == oHttpMsg.headers(i).header_value()) { std::string strData; if (Gunzip(oHttpMsg.body(), strData)) { oHttpMsg.set_body(strData); } else { LOG4_ERROR("guzip error!"); return (CODEC_STATUS_ERR); } } else if ("X-Real-IP" == oHttpMsg.headers(i).header_name()) { LOG4_DEBUG("X-Real-IP: %s", oHttpMsg.headers(i).header_value().c_str()); } else if ("X-Forwarded-For" == oHttpMsg.headers(i).header_name()) { LOG4_DEBUG("X-Forwarded-For: %s", oHttpMsg.headers(i).header_value().c_str()); } } pBuff->Compact(8192); LOG4_DEBUG("%s", ToString(oHttpMsg).c_str()); return (CODEC_STATUS_OK);}
(3)第三步,解码websocket协议
websocket有自带消息头
websocket消息体中可以包含自定义消息头和自定义消息体
websocket协议格式如下:
前两个固定字节:
1)第一个字节
FIN:1位
表示这是消息的最后一帧(结束帧),一个消息由一个或多个数据帧构成。若消息由一帧构成,起始帧即结束帧。
RSV1,RSV2,RSV3:3位
如果未定义扩展,各位是0;如果定义了扩展,即为非0值。如果接收的帧此处非0,扩展中却没有该值的定义,那么关闭连接。
OPCODE:4位
解释PayloadData,如果接收到未知的opcode,接收端必须关闭连接。
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
2) 第二个字节
pay len :
如果值是127,则后面8个字节的无符号整型数的值是payload的真实长度。
如果值是126,则后面2个字节的无符号整型数的值是payload的真实长度。
如果其值在0-125,则是payload的真实长度
3)字节拓展长度
4)掩码
4字节掩码,
5)payload data
负载数据,接收的负载数据payload data 需要异或掩码中的字节才是真正是负载数据
解码websocket协议
const uint8 WEBSOCKET_OPCODE = (0x0F);
const uint8 WEBSOCKET_FRAME_CONTINUE = (0x0);
const uint8 WEBSOCKET_FRAME_TEXT = (0x1);
const uint8 WEBSOCKET_FRAME_BINARY = (0x2);
const uint8 WEBSOCKET_FRAME_CLOSE = (0x8);
const uint8 WEBSOCKET_FRAME_PING = (0x9);
const uint8 WEBSOCKET_FRAME_PONG = (0xA);
MASK & PAYLOAD_LEN
const uint8 WEBSOCKET_MASK = 0x80;
const uint8 WEBSOCKET_PAYLOAD_LEN = 0x7F;
const uint8 WEBSOCKET_PAYLOAD_LEN_UINT16 = 126;
const uint8 WEBSOCKET_PAYLOAD_LEN_UINT64 = 127;
E_CODEC_STATUS CodecWebSocketJson::Decode(loss::CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody){ if (pBuff->ReadableBytes() >= 2)//处理websocket { int iReadIdx = pBuff->GetReadIndex(); uint8 ucFirstByte = 0; uint8 ucSecondByte = 0; pBuff->Read(&ucFirstByte, 1); pBuff->Read(&ucSecondByte, 1); if (!(WEBSOCKET_MASK & ucSecondByte)) //0x80 客户端发来的必须包含掩码 { LOG4_ERROR("a masked frame MUST have the field frame-masked set to 1 when client to server!"); return (CODEC_STATUS_ERR); } if (0 == (WEBSOCKET_PAYLOAD_LEN & ucSecondByte)) //0x7F,Payload len长度为0,为ping pong 消息 { if (WEBSOCKET_FRAME_PING & ucFirstByte) //9 ping 消息 { oMsgHead.set_cmd(0); oMsgHead.set_seq(0); oMsgHead.set_msgbody_len(0); LOG4_TRACE("ping msg"); } else { oMsgHead.set_cmd(1); oMsgHead.set_seq(0); oMsgHead.set_msgbody_len(0); LOG4_TRACE("pong msg"); } return (CODEC_STATUS_PAUSE);//ping pong消息目前服务器不处理 } uint32 uiPayload = 0; char szMaskKey[4] = { 0 }; {//Payloadlen szMaskKey if (WEBSOCKET_PAYLOAD_LEN_UINT64 == (WEBSOCKET_PAYLOAD_LEN & ucSecondByte)) //127 == ucSecondByte & 0x7F { //如果值是127,则后面8个字节的无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。 uint64 ullPayload = 0; if (pBuff->ReadableBytes() <= 12) // 8 + 4 { pBuff->SetReadIndex(iReadIdx);//回退到原来的读坐标 return (CODEC_STATUS_PAUSE); } pBuff->Read(&ullPayload, 8); pBuff->Read(&szMaskKey, 4); uiPayload = (uint32) ntohll(ullPayload); } else if (WEBSOCKET_PAYLOAD_LEN_UINT16 == (WEBSOCKET_PAYLOAD_LEN & ucSecondByte)) //126 == ucSecondByte & 0x7F { //如果值是126,则后面2个字节的无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。 uint16 unPayload = 0; if (pBuff->ReadableBytes() <= 6) // 2 + 4 { pBuff->SetReadIndex(iReadIdx); return (CODEC_STATUS_PAUSE); } pBuff->Read(&unPayload, 2); pBuff->Read(&szMaskKey, 4); uiPayload = (uint32) ntohs(unPayload); } else { //如果其值在0-125,则是payload的真实长度 uint8 ucPayload = 0; if (pBuff->ReadableBytes() <= 4) // 4 { pBuff->SetReadIndex(iReadIdx); return (CODEC_STATUS_PAUSE); } ucPayload = WEBSOCKET_PAYLOAD_LEN & ucSecondByte; // payload len pBuff->Read(&szMaskKey, 4); uiPayload = ucPayload; } if (pBuff->ReadableBytes() < uiPayload) { pBuff->SetReadIndex(iReadIdx); LOG4_TRACE("wait for data.ReadableBytes:%u,Payload:%u", pBuff->ReadableBytes(),uiPayload); return (CODEC_STATUS_PAUSE); } LOG4_TRACE("uiPayload %llu", uiPayload); } {//payload data char cData; const char* pRawData = pBuff->GetRawReadBuffer(); for (int i = pBuff->GetReadIndex(), j = 0; j < uiPayload; ++i, ++j) { cData = pRawData[j] ^ szMaskKey[j % 4]; pBuff->SetBytes(&cData, 1, i); } } //后面开始处理自定义数据包头(如果有包头的则需要处理),和自定义消息包体 。。。。。。 } else { return (CODEC_STATUS_PAUSE); }}
- 解码websocket (c++)
- C#:PDU格式短信编解码(一)解码部分
- Websocket for Objective-C
- WebSocket in Objective-C
- LZW编解码算法(C实现)
- 哈夫曼编解码算法(C实现)
- WebSocket(5)-- WebSocket Server
- WebSocket(5)-- WebSocket Server
- WebSocket(5)-- WebSocket Server
- WebSocket(5)-- WebSocket Server
- C/C++websocket握手协议
- C HTTP URL 解码
- C++BASE64加解码
- Base64编解码(C)
- C#--编码解码
- ffplay.c解码分析
- C#Base64编码解码
- Base64_解码 C
- Java垃圾回收机制
- linux 安全加固及系统优化
- CCNA第五天(某市工商管理局信息网络)
- POJ 2431
- 关于Java反射机制的总结
- 解码websocket (c++)
- hadoop启动之“hadoop-daemon.sh”详解
- Java并发编程:Thread类的使用
- 重装系统常见问题
- xshell不显示vim配色
- Android Studio Cmake & OpenCV3.2环境
- linux初学者-输出输入管理
- Hadoop YARN安装部署初探
- HTTP 请求头中的 X-Forwarded-For