websocket握手实现第一步,需要服务器有解析HTTP协议的能力,因为websocket的握手报文是通过HTTP协议来进行数据交互的。
1、解决HTTP的数据包正确解析。
int checkHttpRequest()
{
//查找http消息结束标记
char* temp = strstr(_recvBuff.data(), "\r\n\r\n");
//未找到表示消息还不完整
if (!temp)
return 0;
//偏移到消息结束位置
//4=strlen("\r\n\r\n")
temp += 4;
//计算http请求消息的请求行+请求头长度
_headerLen = temp - _recvBuff.data();
//判断请求类型是否支持
temp = _recvBuff.data();
if (temp[0] == 'G' &&
temp[1] == 'E' &&
temp[2] == 'T')
{
_requestType = HttpClientS::GET;
}
else if (
temp[0] == 'P' &&
temp[1] == 'O' &&
temp[2] == 'S' &&
temp[3] == 'T')
{
_requestType = HttpClientS::POST;
//POST需要计算请求体长度
char* p1 = strstr(_recvBuff.data(), "Content-Length: ");
//未找到表示格式错误
//返回错误码或者直接关闭客户端连接
if (!p1)
return -2;
//Content-Length: 1024\r\n
//16=strlen("Content-Length: ")
p1 += 16;
char* p2 = strchr(p1, '\r');
if (!p2)
return -2;
//计算数字长度
int n = p2 - p1;
//6位数 99万9999 上限100万字节, 就是1MB
//我们目前是靠接收缓冲区一次性接收
//所以数据上限是接收缓冲区大小减去_headerLen
if (n > 6)
return -2;
char lenStr[7] = {};
strncpy(lenStr, p1, n);
_bodyLen = atoi(lenStr);
//数据异常
if(_bodyLen < 0)
return -2;
//消息数据超过了缓冲区可接收长度
if (_headerLen + _bodyLen > _recvBuff.buffSize())
return -2;
//消息长度>已接收的数据长度,那么数据还没接收完
if (_headerLen + _bodyLen > _recvBuff.dataLen())
return 0;
}
else {
_requestType = HttpClientS::UNKOWN;
return -1;
}
return _headerLen;
}
2、当解析完成后,将HTTP请求的 协议版本、请求方法、字段信息。装载进对应的数据结构中。
int _headerLen = 0;
int _bodyLen = 0;
std::map<KeyString, char*> _header_map;
std::map<KeyString, char*> _args_map;
RequestType _requestType = HttpClientS::UNKOWN;
char* _method;
char* _url;
char* _url_path;
char* _url_args;
char* _httpVersion;
bool _keepalive = true;
3、拿到了客户端请求升级为websocket的报文,服务端开启握手的流程,也就是回应一个HTTP报文给客户端。
bool handshake()
{
auto strUpgrade = this->header_getStr("Upgrade", "");
if (0 != strcmp(strUpgrade, "websocket"))
{
CELLLog_Error("WebSocketClientS::handshake, not found Upgrade:websocket");
return false;
}
auto cKey = this->header_getStr("Sec-WebSocket-Key", nullptr);
if (!cKey)
{
CELLLog_Error("WebSocketClientS::handshake, not found Sec-WebSocket-Key");
return false;
}
std::string sKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
sKey = cKey + sKey;
unsigned char strSha1[20] = {};
SHA1_String((const unsigned char*)sKey.c_str(), sKey.length(), strSha1);
std::string sKeyAccept = Base64Encode(strSha1, 20);
// 组装好握手报文回复给客户端
char resp[256] = {};
strcat(resp, "HTTP/1.1 101 Switching Protocols\r\n");
strcat(resp, "Connection: Upgrade\r\n");
strcat(resp, "Upgrade: websocket\r\n");
strcat(resp, "Sec-WebSocket-Accept: ");
strcat(resp, sKeyAccept.c_str());
strcat(resp, "\r\n\r\n");
this->SendData(resp, strlen(resp));
return true;
}