0%

WebSocket

WebSocket

1
2
3
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

返回的状态码是101,这个东西在看直播的时候非常常见,弹幕八成就是这个东西,个人感觉比心跳包好用一些,减轻了比较多的压力。

ws

简单的编写一个websocket服务端,首先先下载一个websocketd作为服务端:http://websocketd.com其实自己写一个也是可以的,但是可能要对这个协议了解深入,我这里就不写了,有了这个服务端之后,我们需要指定一个开放的端口号:–port=8888 然后放一个自己写的程序上去。

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include "stdafx.h"
#include <Windows.h>
int main() {
int i;
for (i = 1; i <= 10; i++) {
printf("%d\n", i);
Sleep(3000);
}

return 0;
}

执行这个软件
websocketd.exe --port=8888 test.exe
服务端开启之后。回显:

1
2
Sat, 07 Nov 2020 13:51:30 +0800 | INFO   | server     |  | Serving using application   : test.exe
Sat, 07 Nov 2020 13:51:30 +0800 | INFO | server | | Starting WebSocket server : ws://XXXXXXX:8888/

这样子就可以了,我们要在前端进行调用的话呢,是很简单的:

1
2
3
4
5
var ws = new WebSocket('ws://127.0.0.1:8888/');

ws.onmessage = function(event) {
console.log(event.data);
};

js

前段时间有个爬虫需求就是写websocket的,然后不得不在应用程序中调用WebSocket,但是在网上找寻C++关于websocket的代码都是一些别人写好了的库,我不太喜欢自己不能控制的东西,出现了问题也不知道如何解决,所以我就自己写了一个websocket的函数作为调用,方便之后的开发,代码会放在文章底部。

先看一下效果:
res
和在浏览器控制台的效果是一样的。

首先我们包含写好的:Ws.h
#include "Ws.h"
然后定义三个回调函数。

分别是:

  1. websocket连接成功的
  2. websocket断开连接的
  3. websocket收到消息的
1
2
3
4
5
6
7
8
9
10
11
12
void recThread(char* recmessage,WINHTTP_WEB_SOCKET_BUFFER_TYPE rectype)
{
printf_s("type:%d message:%s\n",rectype,recmessage);
}
void acceptThread()
{
printf_s("wait message\n");
}
void destoryThread()
{
printf_s("WebSocket Destory\n");
}

连接成功和连接失败的回调函数格式就是没有返回值,没有参数,只是通知。
收到消息的回调函数有两个参数,一个是收到的消息,还有一个是消息的类型。
这个消息的类型是一个枚举:

1
2
3
4
5
6
7
typedef enum _WINHTTP_WEB_SOCKET_BUFFER_TYPE {
WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE,
WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE,
WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE,
WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE
} WINHTTP_WEB_SOCKET_BUFFER_TYPE;

看看名字就能了解个大半了。

写好回调函数之后就是开启我们的websocket:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
CString url;
url.Format(L"ws://127.0.0.1:8888");
int res = WebSocket(url,recThread,acceptThread,destoryThread);
switch(res)
{
case WEBSOCKET_SUCCESS:
printf("WebSocket success\n");
break;
case WEBSOCKET_URLPARSEERROR:
printf("Error %u in WinHttpCrackUrl.\n", GetLastError());
break;
case WEBSOCKET_CONNECTERROR:
wprintf(L"WinHttpConnect error :%d\n",GetLastError());
break;
case WEBSOCKET_OPENREQUESTERROR:
wprintf(L"WinHttpOpenRequest error :%d\n",GetLastError());
break;
case WEBSOCKET_SENDREQUESTERROR:
wprintf(L"WinHttpSendRequest error :%d\n",GetLastError());
break;
case WEBSOCKET_RECVREQUESTERROR:
wprintf(L"WinHttpReceiveResponse error :%d\n",GetLastError());
break;
case WEBSOCKET_COMPLETEUPGRADEERROR:
wprintf(L"WinHttpWebSocketCompleteUpgrade error :%d\n",GetLastError());
break;
default:
wprintf(L"error\n");
break;
}
system("pause");
return res;
}

参数很简单,就是一个URL地址,然后将我们的三个回调函数传递进去就可以了。
WebSocket函数返回代表了WebSocket的状态,可以通过GetLastError获取错误信息。
这里的话呢还是有一个功能需要注意的,就是我们有的时候是需要给服务端发消息的,这个你们可以在websocket函数内部进行添加,使用类似的语句:

1
tmpMyWinHttpWebSocketSend(hWebSocket,WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE,"wker",strlen("wker));

其实在些这个websocket的时候出现了一个问题,我在文章的顶部写到,是2008年出现的ws,2011年正式的,但是我用的VS是2010的,所以lib库里面根本没有ws的相关函数,并且在winhttp.h中没有定义相关的结构和函数,所以实际上我的VS并不支持我编写websocket的相关功能,所以我不得不自己定义websocket的相关函数,然后从系统的dll文件中拉取ws相关的函数:

1
2
3
4
5
6
7
8
9
MyWinHttpWebSocketCompleteUpgrade tmpWinHttpWebSocketCompleteUpgrade;
MyWinHttpWebSocketReceive tmpMyWinHttpWebSocketReceive;
MyWinHttpWebSocketClose tmpMyWinHttpWebSocketClose;
MyWinHttpWebSocketSend tmpMyWinHttpWebSocketSend;
HMODULE hmodle = LoadLibrary(L"winhttp.dll");
tmpWinHttpWebSocketCompleteUpgrade = (MyWinHttpWebSocketCompleteUpgrade)GetProcAddress(hmodle,"WinHttpWebSocketCompleteUpgrade");
tmpMyWinHttpWebSocketReceive = (MyWinHttpWebSocketReceive)GetProcAddress(hmodle,"WinHttpWebSocketReceive");
tmpMyWinHttpWebSocketClose = (MyWinHttpWebSocketClose)GetProcAddress(hmodle,"WinHttpWebSocketClose");
tmpMyWinHttpWebSocketSend = (MyWinHttpWebSocketSend)GetProcAddress(hmodle,"WinHttpWebSocketSend");

如果在高版本的VS中出现重定义的情况,那么就把我在头文件中的定义删除掉,然后将tmpMy的函数替换成他的函数就可以了。
winhttp.dll在Windows环境变量目录下,所以直接就可以用。