mysql+redis
# TCP 协议
需要建立三次握手,断开需要建立四次握手。
连接过程:Client (客户端) 发送连接请求 SYN 报文,Server 段接受连接后回复 ACK 报文,然后分配资源,客户端接收 ACK 报文后,也向服务端发送 ACK 报文,分配资源,然后就建立了 TCP 连接
断开连接:客户端发送 FIN 报文,假如数据还没传完,服务端继续发送 ACK 报文,之后在发送 FIN 报文给客户端,然后客户端再发 ACK 报文回来 (告诉服务器可以关闭了),然后就关闭了 TCP 连接。
# WebSocket 协议
实现了浏览器与服务器全双工通信 (full-duplex)。只需要建立一次连接,就可以一直保持连接状态,并且前后端可以互相通信,不再是单向的了。
简单来讲,就是可以直接搭建起双向连接机制,
WebSocket 的原理,及如何测试 websocket 是否连接成功 - Egrep - 博客园 (cnblogs.com)
# 目标网站
https://www.douyin.com/user/MS4wLjABAAAAeHJ41sMLJQv06KFI-PyqxocoipBl5AKhEV8EROp7HZU
RPC 注入:
function SekiroClient(wsURL) { | |
this.wsURL = wsURL; | |
this.handlers = {}; | |
this.socket = {}; | |
// check | |
if (!wsURL) { | |
throw new Error('wsURL can not be empty!!') | |
} | |
this.webSocketFactory = this.resolveWebSocketFactory(); | |
this.connect() | |
} | |
SekiroClient.prototype.resolveWebSocketFactory = function () { | |
if (typeof window === 'object') { | |
var theWebSocket = window.WebSocket ? window.WebSocket : window.MozWebSocket; | |
return function (wsURL) { | |
function WindowWebSocketWrapper(wsURL) { | |
this.mSocket = new theWebSocket(wsURL); | |
} | |
WindowWebSocketWrapper.prototype.close = function () { | |
this.mSocket.close(); | |
}; | |
WindowWebSocketWrapper.prototype.onmessage = function (onMessageFunction) { | |
this.mSocket.onmessage = onMessageFunction; | |
}; | |
WindowWebSocketWrapper.prototype.onopen = function (onOpenFunction) { | |
this.mSocket.onopen = onOpenFunction; | |
}; | |
WindowWebSocketWrapper.prototype.onclose = function (onCloseFunction) { | |
this.mSocket.onclose = onCloseFunction; | |
}; | |
WindowWebSocketWrapper.prototype.send = function (message) { | |
this.mSocket.send(message); | |
}; | |
return new WindowWebSocketWrapper(wsURL); | |
} | |
} | |
if (typeof weex === 'object') { | |
// this is weex env : https://weex.apache.org/zh/docs/modules/websockets.html | |
try { | |
console.log("test webSocket for weex"); | |
var ws = weex.requireModule('webSocket'); | |
console.log("find webSocket for weex:" + ws); | |
return function (wsURL) { | |
try { | |
ws.close(); | |
} catch (e) { | |
} | |
ws.WebSocket(wsURL, ''); | |
return ws; | |
} | |
} catch (e) { | |
console.log(e); | |
//ignore | |
} | |
} | |
//TODO support ReactNative | |
if (typeof WebSocket === 'object') { | |
return function (wsURL) { | |
return new theWebSocket(wsURL); | |
} | |
} | |
// weex å’Œ PC环境的websocket APIä¸å®Œå…¨ä¸€è‡´ï¼Œæ‰€ä»¥åšäº†æŠ½è±¡å…¼å®¹ | |
throw new Error("the js environment do not support websocket"); | |
}; | |
SekiroClient.prototype.connect = function () { | |
console.log('sekiro: begin of connect to wsURL: ' + this.wsURL); | |
var _this = this; | |
// ä¸check close,让 | |
// if (this.socket && this.socket.readyState === 1) { | |
// this.socket.close(); | |
// } | |
try { | |
this.socket = this.webSocketFactory(this.wsURL); | |
} catch (e) { | |
console.log("sekiro: create connection failed,reconnect after 2s"); | |
setTimeout(function () { | |
_this.connect() | |
}, 2000) | |
} | |
this.socket.onmessage(function (event) { | |
_this.handleSekiroRequest(event.data) | |
}); | |
this.socket.onopen(function (event) { | |
console.log('sekiro: open a sekiro client connection') | |
}); | |
this.socket.onclose(function (event) { | |
console.log('sekiro: disconnected ,reconnection after 2s'); | |
setTimeout(function () { | |
_this.connect() | |
}, 2000) | |
}); | |
}; | |
SekiroClient.prototype.handleSekiroRequest = function (requestJson) { | |
console.log("receive sekiro request: " + requestJson); | |
var request = JSON.parse(requestJson); | |
var seq = request['__sekiro_seq__']; | |
if (!request['action']) { | |
this.sendFailed(seq, 'need request param {action}'); | |
return | |
} | |
var action = request['action']; | |
if (!this.handlers[action]) { | |
this.sendFailed(seq, 'no action handler: ' + action + ' defined'); | |
return | |
} | |
var theHandler = this.handlers[action]; | |
var _this = this; | |
try { | |
theHandler(request, function (response) { | |
try { | |
_this.sendSuccess(seq, response) | |
} catch (e) { | |
_this.sendFailed(seq, "e:" + e); | |
} | |
}, function (errorMessage) { | |
_this.sendFailed(seq, errorMessage) | |
}) | |
} catch (e) { | |
console.log("error: " + e); | |
_this.sendFailed(seq, ":" + e); | |
} | |
}; | |
SekiroClient.prototype.sendSuccess = function (seq, response) { | |
var responseJson; | |
if (typeof response == 'string') { | |
try { | |
responseJson = JSON.parse(response); | |
} catch (e) { | |
responseJson = {}; | |
responseJson['data'] = response; | |
} | |
} else if (typeof response == 'object') { | |
responseJson = response; | |
} else { | |
responseJson = {}; | |
responseJson['data'] = response; | |
} | |
if (Array.isArray(responseJson)) { | |
responseJson = { | |
data: responseJson, | |
code: 0 | |
} | |
} | |
if (responseJson['code']) { | |
responseJson['code'] = 0; | |
} else if (responseJson['status']) { | |
responseJson['status'] = 0; | |
} else { | |
responseJson['status'] = 0; | |
} | |
responseJson['__sekiro_seq__'] = seq; | |
var responseText = JSON.stringify(responseJson); | |
console.log("response :" + responseText); | |
this.socket.send(responseText); | |
}; | |
SekiroClient.prototype.sendFailed = function (seq, errorMessage) { | |
if (typeof errorMessage != 'string') { | |
errorMessage = JSON.stringify(errorMessage); | |
} | |
var responseJson = {}; | |
responseJson['message'] = errorMessage; | |
responseJson['status'] = -1; | |
responseJson['__sekiro_seq__'] = seq; | |
var responseText = JSON.stringify(responseJson); | |
console.log("sekiro: response :" + responseText); | |
this.socket.send(responseText) | |
}; | |
SekiroClient.prototype.registerAction = function (action, handler) { | |
if (typeof action !== 'string') { | |
throw new Error("an action must be string"); | |
} | |
if (typeof handler !== 'function') { | |
throw new Error("a handler must be function"); | |
} | |
console.log("sekiro: register action: " + action); | |
this.handlers[action] = handler; | |
return this; | |
}; | |
function guid() { | |
function S4() { | |
return (((1+Math.random())*0x10000)|0).toString(16).substring(1); | |
} | |
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); | |
} | |
var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=dy&clientId="+guid()); | |
client.registerAction("getSignature",function(request, resolve,reject ){ | |
let pageurl = '/aweme/v1/web/aweme/post/?device_platform=webapp&aid=6383&channel=channel_pc_web&sec_user_id=MS4wLjABAAAAGyky76MpsaNiuI6tNX0dogABIPR8P_Od0YV7cGD_9P0&max_cursor=1645674431000&locate_query=false&count=10&publish_video_strategy_type=2&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=en-US&browser_platform=Win32&browser_name=Chrome&browser_version=99.0.4844.74&browser_online=true&engine_name=Blink&engine_version=99.0.4844.74&os_name=Windows&os_version=10&cpu_core_num=8&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50&webid=7077786468781164044'; | |
function sendAjax(url){ | |
let xhr = new XMLHttpRequest; | |
xhr.responseType = "json"; | |
xhr.open('get', url, true); | |
xhr.onload = function(e){ | |
resolve(xhr['response']['aweme_list']); | |
if( this.status == 200 || this.status == 304){ | |
console.log(xhr.responseURL); | |
console.log(xhr.responseType); | |
return xhr['response']; | |
} | |
}; | |
xhr.send(null); | |
} | |
sendAjax(pageurl); | |
}); |