知识星球:知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具知识星球是创作者连接铁杆粉丝,实现知识变现的工具。任何从事创作或艺术的人,例如艺术家、工匠、教师、学术研究、科普等,只要能获得一千位铁杆粉丝,就足够生计无忧,自由创作。社群管理、内容沉淀、链接粉丝等就在知识星球。https://articles.zsxq.com/id_5x1m9wdv3e20.html
目录
[知识星球:知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具知识星球是创作者连接铁杆粉丝,实现知识变现的工具。任何从事创作或艺术的人,例如艺术家、工匠、教师、学术研究、科普等,只要能获得一千位铁杆粉丝,就足够生计无忧,自由创作。社群管理、内容沉淀、链接粉丝等就在知识星球。https://articles.zsxq.com/id_5x1m9wdv3e20.html](#知识星球:知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具知识星球是创作者连接铁杆粉丝,实现知识变现的工具。任何从事创作或艺术的人,例如艺术家、工匠、教师、学术研究、科普等,只要能获得一千位铁杆粉丝,就足够生计无忧,自由创作。社群管理、内容沉淀、链接粉丝等就在知识星球。https://articles.zsxq.com/id_5x1m9wdv3e20.html)
[2.long poll(长轮询)](#2.long poll(长轮询))
[五 websocket实现方式](#五 websocket实现方式)
[1. 客户端](#1. 客户端)
[1. RPC 简介](#1. RPC 简介)
[1. 使用方法](#1. 使用方法)
[2. 测试使用](#2. 测试使用)
一,什么是websocket
WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
Websocket是一个持久化的协议
二,websocket的原理
websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
在websocket出现之前,web交互一般是基于http协议的短连接或者长连接
websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"
三,websocket与http的关系
相同点:
都是基于tcp的,都是可靠性传输协议
都是应用层协议
不同点:
WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息
HTTP是单向的
WebSocket是需要浏览器和服务器握手进行建立连接的
而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接
联系:
WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的
总结(总体过程):
首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。
四,websocket解决的问题
1.http存在的问题
http是一种无状态协议,每当一次会话完成后,服务端都不知道下一次的客户端是谁,需要每次知道对方是谁,才进行相应的响应,因此本身对于实时通讯就是一种极大的障碍
http协议采用一次请求,一次响应,每次请求和响应就携带有大量的header头,对于实时通讯来说,解析请求头也是需要一定的时间,因此,效率也更低下
最重要的是,需要客户端主动发,服务端被动发,也就是一次请求,一次响应,不能实现主动发送
2.long poll(长轮询)
对于以上情况就出现了http解决的第一个方法------长轮询
基于http的特性,简单点说,就是客户端发起长轮询,如果服务端的数据没有发生变更,会 hold 住请求,直到服务端的数据发生变化,或者等待一定时间超时才会返回。返回后,客户端又会立即再次发起下一次长轮询
优点是解决了http不能实时更新的弊端,因为这个时间很短,发起请求即处理请求返回响应,实现了"伪·长连接"
张三取快递的例子,张三今天一定要取到快递,他就一直站在快递点,等待快递一到,立马取走
从例子上来看有个问题:
假如有好多人一起在快递站等快递,那么这个地方是否足够大,(抽象解释:需要有很高的并发,同时有很多请求等待在这里)
总的来看:
推送延迟。服务端数据发生变更后,长轮询结束,立刻返回响应给客户端。
服务端压力。长轮询的间隔期一般很长,例如 30s、60s,并且服务端 hold 住连接不会消耗太多服务端资源。
3.Ajax轮询
基于http的特性,简单点说,就是规定每隔一段时间就由客户端发起一次请求,查询有没有新消息,如果有,就返回,如果没有等待相同的时间间隔再次询问
优点是解决了http不能实时更新的弊端,因为这个时间很短,发起请求即处理请求返回响应,把这个过程放大n倍,本质上还是request = response
举个形象的例子(假设张三今天有个快递快到了,但是张三忍耐不住,就每隔十分钟给快递员或者快递站打电话,询问快递到了没,每次快递员就说还没到,等到下午张三的快递到了,but,快递员不知道哪个电话是张三的,(可不是只有张三打电话,还有李四,王五),所以只能等张三打电话,才能通知他,你的快递到了)
从例子上来看有两个问题:
假如说,张三打电话的时间间隔为10分钟,当他收到快递前最后一次打电话,快递员说没到,他刚挂掉电话,快递入库了(就是到了),那么等下一次时间到了,张三打电话知道快递到了,那么这样的通讯算不算实时通讯?很显然,不算,中间有十分钟的时间差,还不算给快递员打电话的等待时间(抽象的解释:每次request的请求时间间隔等同于十分钟,请求解析相当于等待)
假如说张三所在的小区每天要收很多快递,每个人都采取主动给快递员打电话的方式,那么快递员需要以多快的速度接到,其他人打电话占线也是问题(抽象解释:请求过多,服务端响应也会变慢)
总的来看,Ajax轮询存在的问题:
推送延迟。
服务端压力。配置一般不会发生变化,频繁的轮询会给服务端造成很大的压力。
推送延迟和服务端压力无法中和。降低轮询的间隔,延迟降低,压力增加;增加轮询的间隔,压力降低,延迟增高
4.websocket的改进
一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实现了"真·长链接",实时性优势明显。
五 websocket实现方式
1. 客户端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="box" type="text">
<button onclick="ps()">发送</button>
<script>
// 与服务器约定的连接 以及端口 本机的 hosts文件 localhost www.ps.com
const websocket = new WebSocket('ws://127.0.0.1:8080/')
//连接发生错误的回调方法
websocket.onerror = () => {
console.log("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("WebSocket连接成功");
}
//接收到消息的回调方法 接收服务器的数据
websocket.onmessage = function (event) {
console.log(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭");
}
function ps() {
// jquery -> val JS -> value
var text = document.getElementById('box').value
// 客户端发信息发服务器
websocket.send(text)
}
</script>
</body>
</html>
2.服务端
# encoding: utf-8
import asyncio
import websockets
async def echo(websocket):
# 使用WebSocket在客户端和服务器之间建立全双工双向连接后,就可以在连接打开时调用send()方法。
message = 'hello world'
# 发送数据
await websocket.send(message)
return True
async def recv_msg(websocket):
while 1:
# 接收数据
recv_text = await websocket.recv()
print(recv_text)
async def main_logic(websocket, path):
await echo(websocket)
await recv_msg(websocket)
start_server = websockets.serve(main_logic, '127.0.0.1', 8080)
loop = asyncio.get_event_loop()
loop.run_until_complete(start_server)
# 创建了一个连接对象之后,需要不断监听返回的数据,则调用 run_forever 方法,要保持长连接即可
loop.run_forever()
六。案例
基于此我们可以本地替换实时获取数据
案例网站:https://www.xiaohongshu.com
我们利用爬虫工具库-spidertools.cn快速构建请求
import requests
import json
headers = {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
"cache-control": "no-cache",
"content-type": "application/json;charset=UTF-8",
"origin": "https://www.xiaohongshu.com",
"pragma": "no-cache",
"priority": "u=1, i",
"referer": "https://www.xiaohongshu.com/",
"sec-ch-ua": "\"Google Chrome\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
"x-b3-traceid": "4a3e0e2edcd69a5a",
"x-s": "XYW_eyJzaWduU3ZuIjoiNTUiLCJzaWduVHlwZSI6IngyIiwiYXBwSWQiOiJ4aHMtcGMtd2ViIiwic2lnblZlcnNpb24iOiIxIiwicGF5bG9hZCI6ImU5ZTczMzdlOTZiNGRmMTVkYTJjNDAyNWM3ZjIzNzRmZGQxMjc1ZmE5YWEyNWVjYzJiMmVjN2MxYjBmYmRlYjFkMzc1YmJjM2Y2MzZmZGQ5MTAxYmI5NDc0NDE0OTQzOTI5YmEwYTQwOGUxYmVmZTQ4NTA2ZGNkZGY0OWFhMzE4ZTY5Mzc3NjU0ODdhOWVlMTFhYTgwNWM0NDg5NDNkNjcwMmI2OTk4NmE2OTk1Nzc1ZTM4ZTkzMTdjN2EyNWMxMjNiYzM0Y2Y4OTVkMTA0NmE3YTAyZGQzZGIwMWUwZmI0ODdmZTU4NjZhNmQ2ZDBlMjk5YTUwOWZlNWE4ZDMyZjU3ZTM1ZmFmYTY0YjY1MDViYzBmYTBmYmU5YjlkZjVlM2ExNmQ0NmM0NTkwMDY3ZDI3YjNlZGZlZDI5MDg0NGE4OWUxYTIxODc1ZjRlN2I1OTc5MTRhYmE1MjVlNGVmMThjYzBiODkyZjA3ZmEwNTg2NjIxOGYyZmQzZThhOWY2OTcyYWQ3NjcyMmQwMjM1ZTQ1MjE2MWFlZDBkZWUxODQ1In0=",
"x-s-common": "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",
"x-t": "1727942080882",
"x-xray-traceid": "c928afdcb6b114122d4b8c7d18662d5e"
}
cookies = {
}
url = "https://edith.xiaohongshu.com/api/sns/web/v1/homefeed"
data = {
"cursor_score": "",
"num": 47,
"refresh_type": 1,
"note_index": 34,
"unread_begin_note_id": "",
"unread_end_note_id": "",
"unread_note_count": 0,
"category": "homefeed.food_v3",
"search_key": "",
"need_num": 22,
"image_formats": [
"jpg",
"webp",
"avif"
],
"need_filter_image": False
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, cookies=cookies, data=data)
print(response.text)
print(response)
、
数
据包是homefeed
其中有X-s和X-s-common进行了加密
我们查找X-s赋值位置即可,全局搜索
其中x-s和x-t是由window._webmsxyw(c, i)生成的,其中c为'/api/sns/web/v1/homefeed',t为查询参数
比如
data = { "cursor_score": "", "num": 39, "refresh_type": 1, "note_index": 34, "unread_begin_note_id": "", "unread_end_note_id": "", "unread_note_count": 0, "category": "binggaokao_feed_recommend", "search_key": "", "need_num": 14, "image_formats": [ "jpg", "webp", "avif" ], "need_filter_image": false }
那么我们直接利用websocket借用浏览器的环境处理,本地替换
效果
具体代码:
本地替换
!(function () {
if (window.flag) {
} else {
const websocket = new WebSocket('ws://127.0.0.1:8080')
// 创建一个标记用来判断是否创建套接字
window.flag = true;
//连接发生错误的回调方法
websocket.onerror = () => {
console.log("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("WebSocket连接成功");
}
//接收到消息的回调方法 接收服务器的数据
websocket.onmessage = function (event) {
console.log(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭");
}
// 接收服务端发送的信息
websocket.onmessage = function (event) {
。。。。。。。。。。。。。。。。。具体见知识星球
}
}
}())
服务端
。。。。。。。。。。。。。。。。。具体见知识星球
利用rpc获取数据
RPC
1. RPC 简介
为什么要使用RPC技术呢?我们在使用websocket时候可以发现,python在操作的时候,需要创建连接,还需要不断去接受传递数据,非常的麻烦, 那这个时候rpc技术可以帮助到我们,简单来说就是网页直接和rpc服务器进行交互,我们python可以直接调用,rpc暴露的接口,不需要关心,创建连接这一块的问题.
RPC 技术是非常复杂的,对于我们搞爬虫、逆向的来说,不需要完全了解,只需要知道这项技术如何在逆向中应用就行了。
RPC 在逆向中,简单来说就是将本地和浏览器,看做是服务端和客户端,二者之间通过 WebSocket 协议进行 RPC 通信,在浏览器中将加密函数暴露出来,在本地直接调用浏览器中对应的加密函数,从而得到加密结果,不必去在意函数具体的执行逻辑,也省去了扣代码、补环境等操作,可以省去大量的逆向调试时间。
2.Sekiro-RPC
- 官网地址:私有API导出框架 | sekiro
1. 使用方法
1. 执行方式
- 在本地开启服务端
- 需要有 Java 环境,配置参考:百度安全验证
- Linux & Mac:bin/sekiro.sh 双击打开服务端
- Windows:bin/sekiro.bat 双击打开服务端
2.客户端环境
- 地址:file.virjar.com/sekiro_web_client.js?_=123 这个地址是在前端创建客户端的时候需要用到的代码,Sekiro-RPC 把他封装在一个地址里面了
3.使用参数说明
-
使用原理:客户端注入到浏览器环境,然后通过SekiroClient和 Sekiro服务器通信,即可直接 RPC 调用浏览器内部方法,官方提供的SekiroClient 代码样例如下:
// 生成唯一标记uuid编号
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=ws-group&clientId="+guid());
// 业务接口
client.registerAction("登陆",function(request, resolve, reject){
resolve(""+new Date());
})
- group:业务类型(接口组),每个业务一个 group,group 下面可以注册多个终端(SekiroClient),同时group 可以挂载多个 Action;
- clientId:指代设备,多个设备使用多个机器提供 API 服务,提供群控能力和负载均衡能力;
- SekiroClient:服务提供者客户端,主要场景为手机/浏览器等。最终的 Sekiro 调用会转发到 SekiroClient。每个client 需要有一个惟一的 clientId;
- registerAction:接口,同一个 group 下面可以有多个接口,分别做不同的功能;
- resolve:将内容传回给服务端的方法;
- request:服务端传过来的请求,如果请求里有多个参数,可以以键值对的方式从里面提取参数然后再做处理。
2. 测试使用
1. 前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="http://file.virjar.com/sekiro_web_client.js?_=123"></script>
<script>
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=rpc-test&clientId=" + guid());
client.registerAction("clientTime", function (request, resolve, reject) {
resolve("" + new Date());
})
</script>
</body>
</html>
2. SK API
Sekiro 为我们提供了一些 API
- 查看分组列表:http://127.0.0.1:5620/business-demo/groupList
- 查看队列状态:http://127.0.0.1:5620/business-demo/clientQueue?group=rpc-test
- 调用转发:http://127.0.0.1:5620/business-demo/invoke?group=rpc-test&action=clientTime
3.python调用代码
import requests
params = {
"group": "rpc-test",
"action": "clientTime",
}
res = requests.get("http://127.0.0.1:5620/business-demo/invoke", params=params)
print(res.text)
3.案例
案例还是上一个
首先打开服务端
-
确定好位置之后我们就可以替换当前文件,把我们需要的rpc的代码给注入到网页中
-
JavaScript代码
(function () {
function SekiroClient(wsURL) { this.wsURL = wsURL; this.handlers = {}; this.socket = {}; this.base64 = false; // 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鐜鐨剋ebsocket 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; // 涓峜heck 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 (typeof response == 'string') { 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); if (responseText.length < 1024 * 6) { this.socket.send(responseText); return; } if (this.base64) { responseText = this.base64Encode(responseText) } //澶ф姤鏂囪鍒嗘浼犺緭 var segmentSize = 1024 * 5; var i = 0, totalFrameIndex = Math.floor(responseText.length / segmentSize) + 1; for (; i < totalFrameIndex; i++) { var frameData = JSON.stringify({ __sekiro_frame_total: totalFrameIndex, __sekiro_index: i, __sekiro_seq__: seq, __sekiro_base64: this.base64, __sekiro_is_frame: true, __sekiro_content: responseText.substring(i * segmentSize, (i + 1) * segmentSize) } ); console.log("frame: " + frameData); this.socket.send(frameData); } }; 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; }; SekiroClient.prototype.encodeWithBase64 = function () { this.base64 = arguments && arguments.length > 0 && arguments[0]; }; SekiroClient.prototype.base64Encode = function (s) { if (arguments.length !== 1) { throw "SyntaxError: exactly one argument required"; } s = String(s); if (s.length === 0) { return s; } function _get_chars(ch, y) { if (ch < 0x80) y.push(ch); else if (ch < 0x800) { y.push(0xc0 + ((ch >> 6) & 0x1f)); y.push(0x80 + (ch & 0x3f)); } else { y.push(0xe0 + ((ch >> 12) & 0xf)); y.push(0x80 + ((ch >> 6) & 0x3f)); y.push(0x80 + (ch & 0x3f)); } } var _PADCHAR = "=", _ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", _VERSION = "1.1";//Mr. Ruan fix to 1.1 to support asian char(utf8) //s = _encode_utf8(s); var i, b10, y = [], x = [], len = s.length; i = 0; while (i < len) { _get_chars(s.charCodeAt(i), y); while (y.length >= 3) { var ch1 = y.shift(); var ch2 = y.shift(); var ch3 = y.shift(); b10 = (ch1 << 16) | (ch2 << 8) | ch3; x.push(_ALPHA.charAt(b10 >> 18)); x.push(_ALPHA.charAt((b10 >> 12) & 0x3F)); x.push(_ALPHA.charAt((b10 >> 6) & 0x3f)); x.push(_ALPHA.charAt(b10 & 0x3f)); } i++; } switch (y.length) { case 1: var ch = y.shift(); b10 = ch << 16; x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _PADCHAR + _PADCHAR); break; case 2: var ch1 = y.shift(); var ch2 = y.shift(); b10 = (ch1 << 16) | (ch2 << 8); x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _ALPHA.charAt((b10 >> 6) & 0x3f) + _PADCHAR); break; } return x.join(""); }; function startRpc() { if (window.flag) { } else { function guid() { function S4() { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); } return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); } // 创建一个标记用来判断是否创建套接字 window.flag = true; var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc-test&clientId=" + guid()); client.registerAction("ths", function (request, resolve, reject) { resolve(rt.update()); }) } } setTimeout(startRpc, 1000)
})()
别看这么长,实际要改的代码只有
var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc-xhs&clientId=" + guid());
client.registerAction("xhs", function (request, resolve, reject) {
var data=request['data'];
// console.log(data)
data=JSON.parse(data)
console.log(data)
var res=window._webmsxyw('/api/sns/web/v1/homefeed',data)
res=JSON.stringify(res)
resolve(res);
这样既开启成功了
接下来调用即可
代码
具体看知识星球
效果