HTML5 使用WebSocket协议与服务器建立长连接。WebSocket连接时除了进行正常的Socket连接外,还会发一个握手协议字符串,如:
GET / HTTP/1.1
Pragma: no-cache
Cache-Control: no-cache
Host: 127.0.0.1:8686
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: KSO+hOFs1q5SkEnx8bvp6w==
Origin: http://127.0.0.1:8686
Sec-WebSocket-Protocol: default-protocol
Sec-WebSocket-Extensions:
Sec-WebSocket-Version: 13
服务端必须根据Sec-WebSocket-Key的值加上一个GUID字符串(258EAFA5-E914-47DA-95CA-C5AB0DC85B11),通过sha1和base64运算,生成一个新字符串对应到nSec-WebSocket-Accept中,然后配合其它一些固定文本发送到客户端。
根据“Sec-WebSocket-Key: KSO+hOFs1q5SkEnx8bvp6w==”生成一个返回字符串的函数,同Websocket客户端完成握手协议:
std::string GetHandshakeString(char* pReq, WORD wReqSize)
{
if (pReq == NULL)return false;
if (wReqSize >= 2048)return false;
CopyMemory(m_cbBuffer, pReq, wReqSize);
m_cbBuffer[wReqSize] = '\0';
std::istringstream strStream(m_cbBuffer);
std::string strRequest;
std::getline(strStream, strRequest);
if (strRequest[strRequest.size() - 1] == '\r'){
strRequest.erase(strRequest.end() - 1);
std::stringstream ss(strRequest);
ss >> m_strMethod;
ss >> m_strUri;
ss >> m_strVersion;
}
else return "";
std::string strHead;
std::string::size_type sEnd;
while (std::getline(strStream, strHead) && strHead != "\r"){
if (strHead[strHead.size() - 1] != '\r')continue;
else strHead.erase(strHead.end() - 1);
sEnd = strHead.find(": ", 0);
if (sEnd != std::string::npos){
std::string key = strHead.substr(0, sEnd);
std::string val = strHead.substr(sEnd + 2);
m_mapReqField[key] = val;
}
}
if (m_mapReqField.size() == std::string::npos)return "";
std::string strKey = m_mapReqField["Sec-WebSocket-Key"];
if (strKey.empty())return "";
strKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1 sha;
unsigned int iDigSet[5];
sha.Reset();
sha << strKey.c_str();
sha.Result(iDigSet);
for (int i = 0; i < 5; i++)iDigSet[i] = htonl(iDigSet[i]); //将字节转换成网络字节顺序
strKey = base64_encode(reinterpret_cast<const unsigned char*>(iDigSet), 20);
std::stringstream ssReponse;
ssReponse << "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " << strKey << "\r\nUpgrade: websocket\r\n\r\n";
return ssReponse.str();
}
下面是相关的SHA1类和base64函数源码,网上找的,感谢原作者,在此借用。SHA1类:
/*
* sha1.h
*
* Copyright (C) 1998, 2009
* Paul E. Jones <paulej@packetizer.com>
* All Rights Reserved.
*
*****************************************************************************
* $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $
*****************************************************************************
*
* Description:
* This class implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* Many of the variable names in this class, especially the single
* character names, were used because those were the names used
* in the publication.
*
* Please read the file sha1.cpp for more information.
*
*/
#ifndef _SHA1_H_
#define _SHA1_H_
class SHA1
{
public:
SHA1();
virtual ~SHA1();
/*
* Re-initialize the class
*/
void Reset();
/*
* Returns the message digest
*/
bool Result(unsigned *message_digest_array);
/*
* Provide input to SHA1
*/
void Input( const unsigned char *message_array,
unsigned length);
void Input( const char *message_array,
unsigned length);
void Input(unsigned char message_element);
void Input(char message_element);
SHA1& operator<<(const char *messag