2024-12月akamai_2.0-sensor-data之cookie反爬分析详细教程(上)

目录

      • 一、网址及目标数据
      • 二、分析接口反爬点(akamai执行过程)
      • 三、逆向分析参数sensor_data
      • 四、扣js算法代码加密详细步骤

在这里插入图片描述

一、网址及目标数据

文章原文

1、网站: https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343

2、目标:爬取这个接口的数据 https://www.dhl.com/utapi?trackingNumber=1232343&language=zh&requesterCountryCode=CN&source=tt ,即如下图片内容的展示

3、直接模拟请求这个接口,响应状态码403,有反爬,目标让这个接口请求状态码响应返回200,且拿到响应文本内容

二、分析接口反爬点(akamai执行过程)

1、utapi的请求方式与请求链接:get请求,参数为固定值,无反爬加密值

2、请求头:无疑似反爬加密值

3、请求头cookie:有个_abck的cookie可能是反爬参数,包括其它ak_bmsc、bm_sz、bm_sv的cookie

4、_abck的cookie:由WDsB接口链接响应头set-cookie返回

5、_abck的cookie:详细看下WDsB接口 , 发现是POST请求,请求参数有个加密值sensor_data

6、ak_bmsc、bm_sz的cookie,由首页请求链接响应返回

这两个cookie直接get请求首页响应返回的, 同时它还返回了个_abck的cookie,但和我们目标utapi接口的cookie并不是同一个

from curl_cffi import requestsurl = "https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343"response = requests.get(url, impersonate="chrome124", timeout=10)
res_cookies = dict(response.cookies)
print(response, res_cookies)

7、bm_sv的cookie,由下面这个json链接返回

我们测试下如下代码,发现这3个ak_bmsc、bm_sz、bm_sv的cookie值我们都拿到了

from curl_cffi import requestsurl = "https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343"
response = requests.get(url, impersonate="chrome124", timeout=10)
res_cookies = dict(response.cookies)
print(response, res_cookies)url = "https://www.dhl.com/global/dhl/news-alerts.gnf.json"
response = requests.get(url, impersonate="chrome124", cookies=res_cookies, timeout=10)
res_cookies = dict(response.cookies)
print(response, res_cookies)

8、虽然前面两个接口请求也拿到了_abck的cookie,但是带入cookie请求utapi接口请求,并不能响应状态码200,且在步骤4和步骤5我们分析得到,是由WDsB的接口返回的_abck的cookie才有用

9、同时我们发现WDsB这个接口请求了2次,第一次是GET请求,第二次是POST请求,

10、且第一次get请求的WDsB接口返回的内容是js内容,

11、第二次Post请求的WDsB接口返回的内容是success:true,这个似乎是验证cookie : _abck是否正确的,所以我们接下来的目标也是拿到WDsB响应是success后的cookie: _abck

12、既然WDsB的第一次内容是js内容,我们需要找下它的js链接是哪里来的

13、我们在首次首页请求的响应文本里面找到了WDsB接口js链接的由来

14、从首页请求取js链接的url,我们多次运行发现js链接url是动态变化的

from curl_cffi import requests
import reurl = "https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343"
response = requests.get(url, impersonate="chrome124", timeout=10)
res_cookies = dict(response.cookies)
print(response, res_cookies)
js_url = 'https://www.dhl.com' + re.search('type="text/javascript"  src="(.*?)">', response.text).group(1)
print(js_url)

15、同时我们在网页上也能发现无法再次找到WDsB的接口链接,而是换成了其它的,也就是说js链接动态变化,同时可以自行观察的js文件内容混淆的也是动态变化的,不变的是算法加密逻辑

16、为了后面方便分析同一份js代码,我们需要保存一份首页的html文件,然后用overrides或者fiddler替换html的方法,保证始终分析的是同一份js代码,方便扣代码算法逻辑 ,这里直接鼠标右击首页接口Override content

17、由于步骤16我们固定了首页html,所以接下来我们的js请求链接也会同时固定,不再动态变化,从步骤5我们知道是post请求的那个WDsB链接能返回我们需要的_abck的cookie,所以要成功模拟那个接口请求,那么就要分析这个接口需要的请求参数sensor_data

18、通过xhr断点定位sensor_data的生成位置,如图我们打上xhr断点,清缓存后,刷新网页,看到了zfT的参数包含sensor_data

三、逆向分析参数sensor_data

1、定位sensor_data的生成位置可以通过xhr断点拦截js链接 , 如图我们定位到了sensor_data的生成即zfT

2、而zfT是由FIT拼接而成 ,这里的FIT就是sensor_data

3、搜索FIT=发现有12个匹配到,可疑位置打上断点清缓存再次刷新网页

4、第一次断住的位置是,FIT=0,这是初始赋值的地方

5、同时FIT最后生成的位置在这里,也就是从初始到这里的中间流程就是FIT的生成流程

FIT = jr(jr(jr(jr(jr(jr(jr(jr(jr(jr(MxT, wxT), ZfT[AA()[Lc(Dq)].apply(null, [Vt, Br(Uc), x8])]), wxT), O2T[zB]), wxT), O2T[Uc]), wxT), Vg), wxT), FIT);

6、怎么扣FIT生成值,要么倒着扣,要么正着扣,继续调试,会发现第二次断点断在了这里,简单分析了下,M2T是60位数组,然后和其它几个似乎是拼接起来的

四、扣js算法代码加密详细步骤

1、开始扣js代码,我们先逆着扣js代码,如图手动解混淆还原如下,当前我们差FIT的由来

function get_sensor_data(){// var zfT = (EM(typeof BA()[zd(HA)], 'undefined') ? BA()[zd(RO)].apply(null, [tB, lA]) : BA()[zd(mV)](f9T, tg))[S1()[MA(ld)](nh, Q0)](FIT, AA()[Lc(fD)](wp, FA, wB));// (true ? '{"sensor_data":"': BA()[zd(mV)](f9T, tg))['concat'](FIT, '"}');var zfT = '{"sensor_data":"' ['concat'](FIT, '"}');return zfT
}

2、由目录三的步骤5我们知道FIT最后的生成位置,手动解混淆还原如下,其中wxT是分号,现在我们还差O2T、Vg、FIT的由来

//  FIT = jr(jr(jr(jr(jr(jr(jr(jr(jr(jr(MxT, wxT), ZfT[AA()[Lc(Dq)].apply(null, [Vt, Br(Uc), x8])]), wxT), O2T[zB]), wxT), O2T[Uc]), wxT), Vg), wxT), FIT);
// jr(jr(jr(jr(jr(jr(jr(jr(jr(jr(2, wxT), 0), wxT), O2T[zB]), wxT), O2T[Uc]), wxT), Vg), wxT), FIT)
// "2" + ';' + 0 + ';' + O2T[0] + ';' + O2T[1] + ';' + Vg + ';' + FIT;

function get_sensor_data(){FIT = "2" + ';' + 0 + ';' + O2T[0] + ';' + O2T[1] + ';' + Vg + ';' + FIT;var zfT = '{"sensor_data":"' ['concat'](FIT, '"}');return zfT
}
get_sensor_data()

3、全局搜索O2T = , 搜索定位到如下var O2T = fTT || g4() ,其中fTT是undefined ,说明O2T = 是g4()函数,进入到g4()函数

4、发现g4()函数如下,手动解混淆换下如下,其中有些常量我们这里写死了固定值,后续可能还需要多次比对验证常量是固定值;同时这里我们发现它取了document.cookie[‘bm_sz’],所以我们改成传参的形式

function g4(bm_sz) {var gJ = 8888888,qp = 7777777; // 写死固定值var J5 = [gJ, qp];var GQ = bm_szvar Aq = Xm['decodeURIComponent'](GQ)["split"]('~');if (Aq["length"] >= 4) {var kq = Xm["parseInt"](Aq[2], 10);var m0 = Xm["parseInt"](Aq[3], 10);kq = Xm['isNaN'](kq) ? gJ : kq;m0 = Xm["isNaN"](m0) ? qp : m0;J5 = [kq, m0];}var nG;return nG = J5, nG;
}

5、验证下g4()函数传入相同的bm_sz,出参是否相同,出参相同,g4()函数无误

6、现在还差Vg和FIT的由来

function get_sensor_data(bm_sz){var O2T = g4(bm_sz);FIT = "2" + ';' + 0 + ';' + O2T[0] + ';' + O2T[1] + ';' + Vg + ';' + FIT;var zfT = '{"sensor_data":"' ['concat'](FIT, '"}');return zfT
}
var bm_sz = '5FED1A3F67960E11A9FA43C5BE5B3468~YAAQnuBb2taNz2qSAQAATkERlBmfh1YIrwFk3EYaAGiyZsj3sgNaTicXx28L7Ys8mj7OSUlSg+qSvsCJLZszM3bkYX6sSVSoaYDdRACRU8OCIZ2oyKI1GZ30I6XfsfbGhfEwGzBQCozr0pJf9mAGqOcvlNdw2M8forrU4ScI/PWYsMh1UrBxrUpQSHvqEQ+bZTKibFCNCuWX0J2ePdW30WZiDvPgCASbHBypI5Yvkb1Bj82h66RHPStFogRHtNFSI8s5tOtzR/p2OpTSWVwWIACO0kKpXpI+Xz7rK2N4ivZxqC0F49DSIkK7djh3q6p3/rHcuxNVeH9l7ReOiX06fq5kHEfvrQPPPQBKANsxeY+4colUWJgbb8JPjxYeAA6EH4ChjkSl1/EPvOtkhqMxl9vTpDCPaBTKsx//EPkte57rnzqT8Zt7BtLDN7Rk47rou/jSO81mY25eHnA=~4337970~427345'
console.log(get_sensor_data())

7、全局搜索Vg = , 搜索定位到如下,手动解混淆还原后,发现v8() - czT在动态变化,这是因为v8()是当前时间戳,czT也是时间戳,它是一个时间差值; Vg值正常大概是’26,0,0,2,10,0’ , 这里计算的全是函数执行的时间差值,有几个0的我也直接写死了(但是但是如果后面过不掉的话,我们需要和源代码保持一致的逻辑,把这些时间差的逻辑再加回来)

// var Vg = BA()[zd(sr)].apply(null, [C2, gV])[S1()[MA(ld)](nh, MsT)](cR(v8(), czT), S1()[MA(v6)](Zq, Gd))[S1()[MA(ld)].apply(null, [nh, MsT])](J2T, S1()[MA(v6)](Zq, Gd))[S1()[MA(ld)](nh, MsT)](YfT, S1()[MA(v6)](Zq, Gd))[S1()[MA(ld)](nh, MsT)](tNT, S1()[MA(v6)](Zq, Gd))[S1()[MA(ld)](nh, MsT)](kXT, S1()[MA(v6)](Zq, Gd))[S1()[MA(ld)](nh, MsT)](U3);
// var Vg = ""["concat"](cR(v8(), czT), ",")["concat"](J2T, ",")["concat"](YfT, ",")["concat"](tNT, ",")["concat"](kXT, ",")["concat"](U3);
//  var Vg = "" ["concat"](v8() - czT, ",")["concat"](0, ",")["concat"](0, ",")["concat"](tNT, ",")["concat"](kXT, ",")["concat"](0);

J2T差值是0

YfT差值是0

U3差值是0

8、现在还差czT、tNT、kXT、FIT的由来

function v8() {if (Xm["Date"]["now"] && typeof Xm["Date"]["now"]() === 'number') {return Xm["Date"]["now"]();} else {return +new(Xm["Date"])();}
}function get_sensor_data(bm_sz){var O2T = g4(bm_sz);var Vg = "" ["concat"](v8() - czT, ",")["concat"](0, ",")["concat"](0, ",")["concat"](tNT, ",")["concat"](kXT, ",")["concat"](0);FIT = "2" + ';' + 0 + ';' + O2T[0] + ';' + O2T[1] + ';' + Vg + ';' + FIT;var zfT = '{"sensor_data":"' ['concat'](FIT, '"}');return zfT
}
var bm_sz = '5FED1A3F67960E11A9FA43C5BE5B3468~YAAQnuBb2taNz2qSAQAATkERlBmfh1YIrwFk3EYaAGiyZsj3sgNaTicXx28L7Ys8mj7OSUlSg+qSvsCJLZszM3bkYX6sSVSoaYDdRACRU8OCIZ2oyKI1GZ30I6XfsfbGhfEwGzBQCozr0pJf9mAGqOcvlNdw2M8forrU4ScI/PWYsMh1UrBxrUpQSHvqEQ+bZTKibFCNCuWX0J2ePdW30WZiDvPgCASbHBypI5Yvkb1Bj82h66RHPStFogRHtNFSI8s5tOtzR/p2OpTSWVwWIACO0kKpXpI+Xz7rK2N4ivZxqC0F49DSIkK7djh3q6p3/rHcuxNVeH9l7ReOiX06fq5kHEfvrQPPPQBKANsxeY+4colUWJgbb8JPjxYeAA6EH4ChjkSl1/EPvOtkhqMxl9vTpDCPaBTKsx//EPkte57rnzqT8Zt7BtLDN7Rk47rou/jSO81mY25eHnA=~4337970~427345'
console.log(get_sensor_data())

9、全局搜索发现tNT、kXT分别是函数FIT = Sw(AZ, [FIT, O2T[Uc]]);执行的时间差值 FIT = Vk(FIT, O2T[kV[rh]]);的时间差值

    // 函数时间差var tNT = v8();FIT = Sw(AZ, [FIT, O2T[1]]);tNT = v8() - tNT;var kXT = v8();FIT = Vk(FIT, O2T[0]);kXT = v8() - kXT;

10、网页上调试执行时间差[tNT,kXT]大致是[1, 4]

11、所以[tNT,kXT]大致是[1, 4]这里的值我又暂时写死了

12、同理,czT也是初始的时间戳,然后执行了一大串逻辑函数后到了Vg计算函数运行时间差,多次调试发现这个v8() - czT时间差值差不多是25~30之间

13、所以这里我们也给了个随机值,这样步骤8的czT、tNT、kXT、FIT的由来,只剩FIT的由来不清楚了

14、我们继续研究,进入Vk函数

15、Vk函数手动解混淆还原如下,右侧截图里面有成var zl = (qS >> 8) & 65535;

16、其中 Vk里面还有些变量Kq 、IW需要补充上

var Kq = new (Xm[S1()[MA(sv)].apply(null, [ht, JG])])(lM);
var Kq = new (Xm['Array'])(127)
var IW = "";

17、传入相同的参数,检验Vk函数输出结果一致,Vk函数无误 ,可以发现Vk函数是将明文加密的一个流程

18、继续进入到Sw函数里面

19、手动解混淆还原如下,传入相同的参数,验证结果输出一致,Sw52无误

20、继续往上研究,手动解混淆如下

FIT = jr(jr(jr(jr(jr(LzT, lIT), HPT), B2T(E4(FIT), Qt)), HPT), FIT);
FIT = LzT + lIT + HPT + (E4(FIT)^24) + HPT + FIT;FIT = jr(jr(jr(jr(nmT, HPT), nmT), HPT), FIT);
FIT = "2" + HPT + "2" + HPT + FIT;

21、现在还差LzT 、lIT 、E4、 HPT 、FIT的由来

22、LzT搜索定位如下,这里的扣代码算法逻辑和前面步骤一样,手动解混淆还原验证,如下2张图

23、lIT还原如下,其中这里写死了个值,后面如果换版本需要留意是否一样, 留意变化

24、E4函数如下

25、HPT先定义了个字符串,然后HPT = zg(M2T, 2, false);

26、我们还发现,zg这个函数传入的M2T参数,它是一个60位的数组,包含了一些环境信息,这里我们先写死M2T,后面逐个研究由来

27、手动解混淆zg函数还原如下,传入相同的参数,验证结果输出不一致,因为函数本身里面有很多随机数,所以这个问题不大,后面如果过不了风控,我们再回来对比研究

28、补完上面的内容后,这个时候我们已经能生成sensor_data了,如下

29、其中在扣js代码中,我们写死了一些值,后面如果过不了接口,还得回来反复核对,现在我们还剩M2T这个60位数组的研究, 到目前为止,整个加密算法200多行,重点是60位数组的生成逻辑

30、M2T在这里开始生成

31、我们把上面的代码整理下,得到下面的内容,然后逐步分析M2T的各个数组的值的来源

32、从60位数组里面再次还原下,发现近一半的是固定变量可以直接写死,剩下的需要研究的变量的由来有29个,我们需要一一研究由来,大多数是环境检测ua、屏幕尺寸、页面的input信息、请求页面地址/canvas/wbgl/字体指纹/陀螺仪/事件/鼠标轨迹/函数运行时间差

33、把要研究的变量单独拧出来,发现其它值似乎一直是固定的,有5个特别长的在动态变化,我们先研究这5个看看能不能出结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/34662.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【Docker】如何在Docker中配置防火墙规则?

Docker本身并不直接管理防火墙规则;它依赖于主机系统的防火墙设置。不过,Docker在启动容器时会自动配置一些iptables规则来管理容器网络流量。如果你需要更细粒度地控制进出容器的流量,你需要在主机系统上配置防火墙规则。以下是如何在Linux主…

煤矿 35kV 变电站 3 套巡检机器人 “上岗”,力破供电瓶颈

近日,杭州旗晟智能科技与甘肃某变电站配电室的三套智能巡检机器人线下测试顺利完成,并成功交付使用,这为电力运维工作注入了全新的活力与强大的技术支撑。 一、项目背景 甘肃某变电站总建筑面积1098平方米的变电站集变电、配电、监控等多功能…

[创业之路-170]:《BLM战略规划》- 领导力 - 战略制定 - 洞察力 (战略能力中最最核心的能力) - 市场洞察 -2- 看客户-B2B客户分析

目录 一、看客户概述 一、看客户的核心意义 二、看客户的具体内容 三、看客户的实践方法 四、看客户的重要性 二、2B客户的研究方法:研究客户的决策流程 2.1 客户的战略:财务、市场、运营组织 1、研究客户的决策流程 2、研究客户的战略 3、研究…

langgraph 多智能体 Multi-agent supervisor

1. 工具定义 1.1网络搜索工具 from typing import Annotated import os from langchain_community.tools.tavily_search import TavilySearchResults from langchain_core.tools import tool from langchain_experimental.utilities import PythonREPLos.environ["TAVIL…

前缀和(七) 连续数组中最长的01个数相等序列

525. 连续数组 给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。 示例 1: 输入: nums [0,1] 输出: 2 说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。 示例 2: 输入: nums [0,1,0] 输出: 2 说明: [0, 1] (或…

【硬件测试】基于FPGA的64QAM基带通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 64QAM调制解调系统的设计 2.2 信号解调 3.Verilog核心程序 4.开发板使用说明和如何移植不同的开发板 5.完整算法代码文件获得 1.算法仿真效果 本文是之前写的文章: 《基于FPGA的64QAM基带通信系统,包含testbench,高斯…

BUUCTF:misc刷题记录2(会持续更新的)

乌镇峰会种图 打开之后什么也没有发现 用010找到flag flag{97314e7864a8f62627b26f3f998c37f1} wireshark 解压后是个压缩包,用wirshark打开。 根据题目信息,我们可以在wirshark中去寻找密码 在这里进行过滤http.request.methodPOST 在这里的ffb7567a1…

elasticSearch(一):elasticSearch介绍

一、搜索引擎 搜索引擎的核心目的是帮助用户以最小的成本才海量数据中找到最想要的结果。糟糕的搜索引擎往往会所问非所答,用户查了半天也得不到自己想要的,好的搜索引擎往往第一页就是用户最想要的结果。而目前判断搜索引擎好坏一般是从召回率、精确率…

python解析各城市历史天气数据

1 背景介绍 python解析各城市历史天气数据 2 基本思路 获取原始数据,解析,然后保存到excel表格里面。 以浙江省杭州市西湖区2016年9月到2017年4月的 历史天气数据为例,最终成果如下: 3 核心代码 对于数据比较少时&#xff…

【并集查询】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…

组件中的生命周期

文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了Flutter中如何使用三方包相关的内容,本章回中将介绍Widget的生命周期.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 本章回中介绍的生命周期是指Widget从创建到结果的整个过程,这里提到的Widget是一个泛…

DAY35|动态规划Part03|LeetCode:01背包问题 二维、01背包问题 一维、416. 分割等和子集

目录 01背包理论基础(一) 基本思路 C代码 01背包理论基础(二) 基本思路 C代码 LeetCode:416. 分割等和子集 基本思路 C代码 01背包理论基础(一) 题目链接:卡码网46. 携带研究材料 文字…

Shopee大卖选品策略揭秘:印尼市场选品与成本分析案例

东南亚电商市场愈发成熟,越来越多做跨境的卖家转向本土。但对于“本土化选品运营”这个问题还相当疑惑!看别人本土店销量飞起,自己却还在就纠结做啥能挣钱? 别急,今天EasyBoss根据之前合作的Shopee大卖分享的经验&…

鸿蒙特色实战2

服务卡片开发 创建服务卡片 创建一个新的工程后,可以通过如下方法进行创建服务卡片: 创建服务卡片包括如下两种方式: 选择模块(如entry模块)下的任意文件,单击菜单栏File > New > Service Widget创…

php基础:文件处理2

1.文件属性 当我们在程序中操作文件时,可能会使用到文件的一些常见属性,比如文件的大小、类型、修改时间、访问时间以及权限等等。PHP 中提供了非常全面的用来获取这些属性的内置函数,如下表所示。 2.目录操作 新建目录:mkdir(路…

激活函数在神经网络中的应用与选择

目录 ​编辑 Sigmoid函数 代码示例与分析 Tanh函数 代码示例与分析 ReLU函数 代码示例与分析 Leaky ReLU函数 代码示例与分析 PReLU函数 代码示例与分析 ELU函数 代码示例与分析 SELU函数 代码示例与分析 Softmax函数 代码示例与分析 结论 在深度学习领域&am…

如何在.NET 8.0 上安装 FastReport 并创建简单报告(下)

FastReport 是一款灵活而强大的报告工具。它允许用户以各种格式访问数据源并以可视化方式呈现它们。使用 FastReport 创建的报告可以在用户界面中使用拖放逻辑轻松设计,并转换为不同的格式(PDF、Excel、Word 等)。>> 如何在.NET 8.0 上…

SpringBoot期末知识点大全

一、学什么 IoC AOP:面向切面编程。 事物处理 整合MyBatis Spring框架思想! 二、核心概念 问题:类之间互相调用/实现,导致代码耦合度高。 解决:使用对象时,程序中不主动new对象,转换为由外部提…

vscode CMakeLists中对opencv eigen的引用方法

CMakeLists.txt 项目模式(只有一个main函数入口) cmake_minimum_required(VERSION 3.5)project(vsin01 VERSION 0.1 LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)set(OpenCV_DIR G:/MinGW_Opencv/opencv4.10/opencv…

11.15【JAVA】【网络编程】【DEBUG】

代码以开源至cqujk/CquJavaEE 的myExp-socketCode分支,欢迎拷打 参考REPO Java 11: Standardized HTTP Client API 没反应 这是因为这应当是两个线程,当server创建好套接字后,进入accept时,就不会继续向下运行,客户端自然也就无法发送请求 首先要保证server进入accept(这个…