爬虫_WebSocket
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
返回的状态码是101,这个东西在看直播的时候非常常见,弹幕八成就是这个东西,个人感觉比心跳包好用一些,减轻了比较多的压力。
简单的编写一个websocket服务端,首先先下载一个websocketd
作为服务端:http://websocketd.com其实自己写一个也是可以的,但是可能要对这个协议了解深入,我这里就不写了,有了这个服务端之后,我们需要指定一个开放的端口号:--port=8888 然后放一个自己写的程序上去。
#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
服务端开启之后。回显:
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/
这样子就可以了,我们要在前端进行调用的话呢,是很简单的:
var ws = new WebSocket('ws://127.0.0.1:8888/');
ws.onmessage = function(event) {
console.log(event.data);
};
前段时间有个爬虫需求就是写websocket的,然后不得不在应用程序中调用WebSocket,但是在网上找寻C++关于websocket的代码都是一些别人写好了的库,我不太喜欢自己不能控制的东西,出现了问题也不知道如何解决,所以我就自己写了一个websocket的函数作为调用,方便之后的开发,代码会放在文章底部。
先看一下效果:
和在浏览器控制台的效果是一样的。
首先我们包含写好的:Ws.h
#include "Ws.h"
然后定义三个回调函数。
分别是:
- websocket连接成功的
- websocket断开连接的
- websocket收到消息的
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");
}
连接成功和连接失败的回调函数格式就是没有返回值,没有参数,只是通知。
收到消息的回调函数有两个参数,一个是收到的消息,还有一个是消息的类型。
这个消息的类型是一个枚举:
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:
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函数内部进行添加,使用类似的语句:
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相关的函数:
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环境变量目录下,所以直接就可以用。
WebSocket.zip (2.1 KB)