websocket C/C++服务器应用

1.介绍
websocket介绍不用细说,这里主要给出服务器实现思路,下面直入主题。
websocket服务器跟正常的服务器区别在消息处理上,websocket在我们正常的数据中添加了websocket协议头,所以要拿到正常的数据需要先解析websocket协议,才能拿到实际内容,协议内容如下:

The following is websocket data frame:

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

协议详解请参考

http://www.cnblogs.com/caosiyang/archive/2012/08/14/2637721.html

不熟悉协议的话请尽量先熟悉一下协议。

2.协议解析
握手阶段主要是对客户端请求中Sec-WebSocket-Accept字段值的处理
服务器取该字段的值+258EAFA5-E914-47DA-95CA-C5AB0DC85B11来获取sha1值并base64返回给客户端,对应回应协议中的Sec-WebSocket-Key字段。
注意:sha1后需要对结果做特殊处理,实例代码如下

// create server key
string key =fieldMap["Sec-WebSocket-Key"];
key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1 sha;
unsigned int message_digest[5];
sha.Reset();
sha << key.c_str();
sha.Result(message_digest);
// convert sha1 hash bytes to network byte order because this sha1
//  library works on ints rather than bytes
for (int i = 0; i < 5; i++) message_digest[i] = htonl(message_digest[i]);
key = base64_encode(reinterpret_cast<const unsigned char*>(message_digest),20);

至此握手完毕。(sha1和base64自行去网上查找)

解析数据帧:参考上面的协议详解就可大致能自行解析websocket协议了
贴上代码吧

typedef unsigned char   byte;

int index = 0;
char buf[MAX_LENGTH];ZERO(buf);

byte bEof = ((byte)msg[0] >> 7) > 0; // is eof
byte bmask= ((byte)msg[1]  & 0xF) > 0; // has mask
int  pklen= ((byte)msg[1] & 0x7F);
index+=2;

if (pklen == 126)
{
    pklen = ntohs(*(uint16*)(msg+index));
    index += 2;
}
else
{
    pklen = ntohl(*(ulong*)(msg+index));
    index += 8;
}

// has mask
byte mask[4]; ZERO(mask);
if (bmask)
{
    memcpy(&mask,&msg[index],4);
    index += 4;
}

memcpy(buf, msg+index, pklen);
if (bmask)
{
    for (int i = 0; i < pklen; ++i) 
    {
        buf[i] = (buf[i] ^ mask[i%4]);
    }
}

buf中就是我们需要的实际内容了。

服务器回应数据帧:(同样需要按照websoket协议封装发送的内容)
代码如下:

void WebSocketServer::sendtoclient(int sock, const char* msg, int len)
{
    int sum = 0;
    char outmsg[MAX_LENGTH]; ZERO(outmsg);

    outmsg[sum++] = 130; // FIN+Binary
    if (len < 126)
    {
        outmsg[sum++] = len;
    }
    else if(len > 125 && len <= 65535)
    {
        outmsg[sum++] = 126;
        uint16 tmp = htons(len);
        memcpy(&outmsg[sum], &tmp, 2);
        sum += 2;
    }
    else 
    {
        outmsg[sum++] = 127;
        ulong tmp = htonl(len);
        memcpy(&outmsg[sum], &tmp, 8);
        sum += 8;
    }

    memcpy(&outmsg[sum], msg, len);
    sum += len;

    write(sock, outmsg, sum);
    INFO("send to %d len%d", sock, sum);
}

websocket关闭连接时会先发一个opcode:1000的数据帧,客户端和服务器都需要回应才能正常关闭连接。

3.备注
在开发阶段可以使用Wireshark抓包工具抓包分析传输的数据是否正确。
websocket C/C++服务器应用_第1张图片

你可能感兴趣的:(服务器软件,websocket,服务器,c-c++)