Procházet zdrojové kódy

[FIX BUG] 在并发请求的情况下,ResultSet处理WebSocket会存在数据丢失、响应延迟

lonelyPoker před 3 roky
rodič
revize
ce9b89b23f
4 změnil soubory, kde provedl 100 přidání a 40 odebrání
  1. 24 18
      README.md
  2. 51 22
      main.go
  3. 17 0
      test/muilte_request.py
  4. 8 0
      test/register_function.js

+ 24 - 18
README.md

@@ -1,14 +1,13 @@
 # JsRPC
+
 ##### 黑脸怪-hliang
 
 -- js逆向之远程调用(rpc)免去抠代码补环境
 
 > tip:懒得自己编译的 ,[releases](https://github.com/jxhczhl/JsRpc/releases)中有已经编译好的包 (win和Linux的都有~)
 
-
-
 - [JsRPC](#jsrpc)
-        - [黑脸怪-hliang](#黑脸怪-hliang)
+  - [黑脸怪-hliang](#黑脸怪-hliang)
   - [目录结构](#目录结构)
   - [基本介绍](#基本介绍)
   - [实现](#实现)
@@ -26,9 +25,8 @@
   - [其他案例](#其他案例)
   - [TODO](#todo)
 
-
-
 ## 目录结构
+
 ```dart
 -- main.go (服务器的主代码)
 -- resouces/JsEnv.js (客户端注入js环境)
@@ -47,7 +45,6 @@
 在https的网站想要新建WebSocket连接如果是连接到普通的ws可能会报安全错误,好像连接本地(127.0.0.1)不会报错~ 可以用本地和wss 你自己看着玩
 
 1. 无https证书者。直接编译main.go 我试了一下,发现使用本地ip(127.0.0.1)可以在https的网站直接连接ws使用 默认端口12080
-
 2. 有https证书者。修改main.go文件 把r.Run()注释掉,把r.RunTls注释取消掉 并且参数设置证书的路径 直接输入名字就是当前路径 默认端口:12443
 
 > 另外的题外话,有域名没证书不会搞的 或者有域名有公网(非固定IP的)都可以搞成的,自己研究研究
@@ -59,7 +56,6 @@
 如下图所示
 ![image](https://user-images.githubusercontent.com/41224971/161306799-57f009dc-5448-402f-ab4d-ee5c6c969c91.png)
 
-
 **api 简介**
 
 - `/list` :查看当前连接的ws服务  (get)
@@ -67,10 +63,9 @@
 - `/go` :获取数据的接口  (get | post)
 - `/execjs` :传递jscode给浏览器执行 (get | post)
 
-
-说明:接口用?group和name来区分任务 如 ws://127.0.0.1:12080/ws?group={}&name={}"  
+说明:接口用?group和name来区分任务 如 ws://127.0.0.1:12080/ws?group={}&name={}"
   //注入ws的例子 group和name都可以随便起名(必填)
-http://127.0.0.1:12080/go?group={}&name={}&action={}&param={} //这是调用的接口 
+http://127.0.0.1:12080/go?group={}&name={}&action={}&param={} //这是调用的接口
 group和name填写上面注入时候的,action是注册的方法名,param是可选的参数 接口参数暂定为这几个,但是param还可以传stringify过的json(字符串) 下面会介绍
 
 ### 注入JS,构建通信环境
@@ -80,14 +75,17 @@ group和name填写上面注入时候的,action是注册的方法名,param是
 ![image](https://user-images.githubusercontent.com/41224971/161307187-1265ec7c-fe64-45d7-b255-5472e0f25802.png)
 
 ### 连接通信
+
 ```js
 // 注入环境后连接通信
 var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&name=hlg");
 ```
+
 #### I 远程调用0:
+
 ##### 接口传js代码让浏览器执行
-浏览器已经连接上通信后 调用execjs接口就行
 
+浏览器已经连接上通信后 调用execjs接口就行
 
 ```python
 import requests
@@ -108,10 +106,11 @@ data = {
 res = requests.post(url, data=data)
 print(res.text)
 ```
+
 ![image](https://user-images.githubusercontent.com/41224971/165704850-0a22dd7e-68ea-44fe-bda9-608c10795558.png)
 
+#### Ⅱ 远程调用1: 浏览器预先注册js方法 传递函数名调用
 
-#### Ⅱ 远程调用1: 浏览器预先注册js方法 传递函数名调用 
 ##### 远程调用1:无参获取值
 
 ```js
@@ -124,12 +123,14 @@ demo.regAction("hello", function (resolve) {
     resolve(Js_sjz);
 })
 ```
+
     访问接口,获得js端的返回值
     http://localhost:12080/go?group=zzz&name=hlg&action=hello
 
 ![image](https://user-images.githubusercontent.com/41224971/161309382-81a9a9cc-65f7-4531-a1e6-a892dfe1facd.png)
 
 ##### 远程调用2:带参获取值
+
 ```js
 //写一个传入字符串,返回base64值的接口(调用内置函数btoa)
 demo.regAction("hello2", function (resolve,param) {
@@ -138,11 +139,11 @@ demo.regAction("hello2", function (resolve,param) {
     resolve(base666);
 })
 ```
-    访问接口,获得js端的返回值
-![image](https://user-images.githubusercontent.com/41224971/161311297-6731c089-3de2-44ed-80b9-21a03746a52c.png)
 
+    访问接口,获得js端的返回值![image](https://user-images.githubusercontent.com/41224971/161311297-6731c089-3de2-44ed-80b9-21a03746a52c.png)
 
 ##### 远程调用3:带多个参获 并且使用post方式 取值
+
 ```js
 //假设有一个函数 需要传递两个参数
 function hlg(User,Status){
@@ -155,7 +156,9 @@ demo.regAction("hello3", function (resolve,param) {
     resolve(res);
 })
 ```
+
    访问接口,获得js端的返回值
+
 ```python
 url = "http://localhost:12080/go"
 data = {
@@ -168,14 +171,13 @@ print(data["param"]) #dumps后就是长这样的字符串{"user": "\u9ed1\u8138\
 res=requests.post(url, data=data) #这里换get也是可以的
 print(res.text)
 ```
-![image](https://user-images.githubusercontent.com/41224971/161313397-166cbda0-fe8b-4063-b815-376902d82f74.png)
 
+![image](https://user-images.githubusercontent.com/41224971/161313397-166cbda0-fe8b-4063-b815-376902d82f74.png)
 
 ## 食用案例-爬虫练手-xx网第15题
 
     本题解是把它ajax获取数据那一个函数都复制下来,然后控制台调用这样子~
 
-
     1.f12查看请求,跟进去 找到ajax那块,可以看到call函数就是主要的ajax发包 输入页数就可以,那我们复制这个函数里面的代码备用
 
 ![image](https://user-images.githubusercontent.com/41224971/134793093-bac742e9-2f66-4fe4-b98b-7769d7379350.png)
@@ -185,13 +187,18 @@ print(res.text)
 
 ![image](https://user-images.githubusercontent.com/41224971/134795740-c62fce0d-7271-4b34-a9e5-07515b99ab81.png)
 
-    3.调用接口就完事了,param就是传参页数 
+    3.调用接口就完事了,param就是传参页数
 
 ![image](https://user-images.githubusercontent.com/41224971/134799668-3dd385e7-f44c-4fb3-85ff-00d78c674865.png)
 
     控制台可以关,但是注入的网页不要关哦
 
+## BUG修复
+
+1.修复ResultSet函数,在并发处理环境下存在数据丢失,响应延迟等问题。
+
 ## 其他案例
+
     1. JsRpc实战-猿人学-反混淆刷题平台第20题(wasm)
         https://mp.weixin.qq.com/s/DemSz2NRkYt9YL5fSUiMDQ
     2. 网洛者-反反爬练习平台第七题(JSVMPZL - 初体验)
@@ -199,7 +206,6 @@ print(res.text)
 
 ![image](https://user-images.githubusercontent.com/41224971/165717648-8763d592-b380-4a5c-9a97-accee032e493.png)
 
-
 ## TODO
 
 - [ ] ssl Docker Deploy

+ 51 - 22
main.go

@@ -24,7 +24,8 @@ var (
 		CheckOrigin: func(r *http.Request) bool { return true },
 	}
 	hlSyncMap sync.Map
-
+	gm = &sync.Mutex{}
+	gchan = make(chan string)
 	// OverTime 设置接口没得到结果时的超时时间
 	OverTime = time.Second * 30
 )
@@ -77,6 +78,7 @@ func ws(c *gin.Context) {
 			action := msg[:strIndex]
 			client.Data[action] = msg[strIndex+5:]
 			fmt.Println("get_message:", client.Data[action])
+			gchan <- msg[strIndex+5:]
 			hlSyncMap.Store(group+"->"+name, client)
 		} else {
 			fmt.Println(msg, "message error")
@@ -115,6 +117,29 @@ func QueryFunc(client *Clients, funcName string, param string) {
 
 }
 
+func GQueryFunc(client *Clients, funcName string, param string, resChan chan <- string) {
+	WriteDate := Message{}
+	WriteDate.Action = funcName
+	if param == "" {
+		//WriteDate = "{\"action\":\"" + funcName + "\"}"
+		WriteDate.Param = ""
+	} else {
+		//WriteDate = "{\"action\":\"" + funcName + "\",\"param\":\"" + param + "\"}"
+		WriteDate.Param = param
+	}
+	data, _ := json.Marshal(WriteDate)
+	ws := client.clientWs
+	gm.Lock()
+	err := ws.WriteMessage(2, data)
+	gm.Unlock()
+	if err != nil {
+		fmt.Println(err, "写入数据失败")
+	}
+	res := <- gchan
+	fmt.Printf("res: %v\n", res)
+	resChan <- res
+}
+
 func ResultSet(c *gin.Context) {
 	var getGroup, getName, Action, Param string
 
@@ -147,28 +172,32 @@ func ResultSet(c *gin.Context) {
 		return
 	}
 	//发送数据到web里得到结果
-	QueryFunc(client, Action, Param)
+	// QueryFunc(client, Action, Param)
 
-	ctx, cancel := context.WithTimeout(context.Background(), OverTime)
-	for {
-		select {
-		case <-ctx.Done():
-			// 获取数据超时了
-			cancel()
-			return
-		default:
-			data := client.Data[Action]
-			//fmt.Println("正常中")
-			if data != "" {
-				cancel()
-				//这里设置为空是为了清除上次的结果并且赋值判断
-				client.Data[Action] = ""
-				c.JSON(200, gin.H{"status": "200", "group": client.clientGroup, "name": client.clientName, "data": data})
-			} else {
-				time.Sleep(time.Millisecond * 500)
-			}
-		}
-	}
+	// ctx, cancel := context.WithTimeout(context.Background(), OverTime)
+	// for {
+	// 	select {
+	// 	case <-ctx.Done():
+	// 		// 获取数据超时了
+	// 		cancel()
+	// 		return
+	// 	default:
+	// 		data := client.Data[Action]
+	// 		//fmt.Println("正常中")
+	// 		if data != "" {
+	// 			cancel()
+	// 			//这里设置为空是为了清除上次的结果并且赋值判断
+	// 			client.Data[Action] = ""
+	// 			c.JSON(200, gin.H{"status": "200", "group": client.clientGroup, "name": client.clientName, "data": data})
+	// 		} else {
+	// 			time.Sleep(time.Millisecond * 500)
+	// 		}
+	// 	}
+	// }
+
+	c2 := make(chan string)
+	go GQueryFunc(client, Action, Param,c2)
+	c.JSON(200, gin.H{"status": "200", "group": client.clientGroup, "name": client.clientName, "data": <-c2})
 
 }
 

+ 17 - 0
test/muilte_request.py

@@ -0,0 +1,17 @@
+import requests,time
+from concurrent.futures.thread import ThreadPoolExecutor
+tp = ThreadPoolExecutor(max_workers=50)
+def fetch_response(url):
+    response = requests.get(url)
+    return url,response.text
+
+def callback_successed(f):
+    print(f.result())
+
+start_timestamp = time.time()
+for i in range(100):
+    tp.submit(fetch_response,"http://localhost:12080/go?group=zzz&name=hlg&action=hello&param={}".format(i)).add_done_callback(callback_successed)
+tp.shutdown()
+end_timestamp = time.time()
+
+print("每个请求时间开销:{}ms".format(round(end_timestamp-start_timestamp,3) *1000 / 100))

+ 8 - 0
test/register_function.js

@@ -0,0 +1,8 @@
+hlc = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&name=hlg");
+
+
+hlc.regAction("hello", function (resolve,param) {
+    var base666 = btoa(param)
+    resolve(base666 + "**" + atob(base666));
+
+})