HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\tcpip\Parameters\TcpTimedWaitDelay to 30
and HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\tcpip\Parameters\MaxUserPort to 65534
and rebooting the machine
See the readmedoc file for more information
通过百度搜索介绍最多的还是让修改TimedWaitDelay 和MaxUserPort这2个值,其中是将TimedWaitDelay修改的相对小点,可以根据实际情况来定,
同时将MaxUserPort这个值修改大些,但是修改完并重启机器后,该问题仍然存在,通过多方查资料,然后对一些注册表进行修改:
[HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parameters]
TcpNumConnections = 0x00fffffe (Default = 16,777,214)
以上注册表信息配置单机的最大允许的TCP连接数,默认为 16M。这个数值看似很大,这个并不是限制最大连接数的唯一条件,还有其他条件会限制到TCP 连接的最大连接数。
最大动态端口数
TCP客户端和服务器连接时,客户端必须分配一个动态端口,默认情况下这个动态端口的分配范围为 1024-5000 ,也就是说默认情况下,客户端最多可以同时发起3977 个Socket 连接。我们可以修改如下注册表来调整这个动态端口的范围
[HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parameters]
MaxUserPort = 5000 (Default = 5000, Max = 65534)
最大TCB 数量
系统为每个TCP 连接分配一个TCP 控制块(TCP control block or TCB),这个控制块用于缓存TCP连接的一些参数,每个TCB需要分配 05 KB的pagepool 和 05KB 的Non-pagepool,也就说,每个TCP连接会占用 1KB 的系统内存。
系统的最大TCB数量由如下注册表设置决定
[HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parameters]
MaxFreeTcbs = 2000 (Default = RAM dependent, but usual Pro = 1000, Srv=2000)
非Server版本,MaxFreeTcbs 的默认值为1000 (64M 以上物理内存)
Server 版本,这个的默认值为 2000。
也就是说,默认情况下,Server 版本最多同时可以建立并保持2000个TCP 连接。
最大TCB Hash table 数量
TCB 是通过Hash table 来管理的,下面注册表设置决定了这个Hash table 的大小
HKEY_LOCAL_MACHINE \System \CurrentControlSet \services \Tcpip \Parameters]
MaxHashTableSize = 512 (Default = 512, Range = 64-65536)
这个值指明分配 pagepool 内存的数量,也就是说,如果MaxFreeTcbs = 1000 , 则 pagepool 的内存数量为 500KB
那么 MaxHashTableSize 应大于 500 才行。这个数量越大,则Hash table 的冗余度就越高,每次分配和查找 TCP 连接用时就越少。这个值必须是2的幂,且最大为65536
MaxUserPort = 65534 (Decimal)
MaxHashTableSize = 65536 (Decimal)
MaxFreeTcbs = 16000 (Decimal)
这里我们可以看到 MaxHashTableSize 被配置为比MaxFreeTcbs 大4倍,这样可以大大增加TCP建立的速度。窗口大小 应该是一次最大传送多少数据包的
比如我有 1000个数据包要发给你
窗口大小是10
我先 1-10 共10个数据包发送给你了。
然后停下来,等着你告诉我 “成功的收到”
当我收到了你发送来的 “1-5”已经收到了。
这个时候, 我就把 11-15 个数据包 发出去。
然后继续等响应。
但是确保 在“发送过程中,等待响应” 的数据包的数量,是10个。
也就是数量不超过窗口大小。
窗口大小,好像是这个样子的。
即使端口处于2MSL状态,使用该选项,仍然能够在该端口建立连接。
服务器常会设置该选项,以防服务器重启。
如果在TIME_WAIT时间内,收到了对端发送来的数据报(不是重置报文段都行),那么该状态将被破坏,称为 时间等待错误 。原因是,当收到报文段以后,通常Seq是旧的,所以本端就会发送ACK,对端已经关闭或者是别的连接,就会发送RST,导致TIME_WAIT状态被破坏。
但是许多系统规定,TIME_WAIT状态是不对重置报文段做出反应。
两端同时发送FIN,两端又同时ACK。又同时进入TIME_WAIT
当处于TIME_WAIT的主机崩溃以后,重启,然后需要等待相当与一个MSL的时间才能建立新的连接。
这段时间成为静默时间。
当一段发现到达的报文段对相关连接(也就是进程,套接字对)而言不正确的时候,TCP就会发送一个重置报文段,从而导致对端的连接快速拆卸(也就是结束吧!)。
重置报文段的ACK位必须有,而且ACK的值必须在正确的窗口范围内,这样可以防止被攻击。
FIN正常关闭一条连接成为 有序释放 ,通常不会出现丢失数据的情况。
重置报文段终止一条连接成为 终止释放 。重置报文段在任何时候都可以发送,代替FIN来终止连接,且不学校对端ACK
终止报文段特性:
当该数值设置为0,那么也意味着,不会再连接终止之前为了确保本端缓存中的数据都发送出去而等待。
TCP在发送数据时会设置计时器,如果计时器超时认为受到数据确认信息,就会引发相应的超时,或给予计时器的重传 *** 作,计时器超时时成为重传超时( RTO )。
TCP累计确认无法返回新的ACK,或者当ACK包含选择确认信息(SACK)时,表明出现书序数据报,空洞。就会引起 快速重传 。
若RTO短与RTT,那么没分都会重传,反之,整个网络利用率就会随之下降。
RTT样本 :TCP在收到数据后会返回确认信息ACK,该信息中携带一个字节的数据,测量传输该确认需要的时间,该测量结果成为RTT样本。
每个连接的RTT军独立计算。
如何根据RTT来设置RTO,有如下的方法
公式: SRTT=aSRTT+(1-a)RTT ,a取 08-09 。
当TCP运行在RTT变化较大的网络中,无法取得期望的结果。
以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补
因为丢失ACK,或者实际RTT显著增长,可能出现伪超时的现象。
以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补
每个包可以选择各自的传送路径。某些高级路由器的采用多个并行数据链路,不同的处理演示也会导致包的离开顺序和到达顺序不匹配
包的失序会造成重传,很近单嘛,前面一个小号的Seq没到达,后面的先到达,那么ACK就会 重复
当TCP超时重发是,循序执行重新租宝,发送送一个更大的报文段提高性能,不超过MSS和MTU。
出现在每次传送的包较小,又丢包的情况
每个交互键通常会生成一个单独的数据报,也就是每个按键是独立传输的。
ssh调用一个shell,对客户端输入的字符做出辉县,因此,每个字符生成4个tcp数据段,客户端的交互按键输入,服务器对按键的ACK,服务器生成的辉县,客户端对回显的ACK。通常第二段和第三段合并,成为 捎带延时确认 。
PSH位设置,意味着发送端的缓存为空,也就是没什么可以发送了。
许多情况下,TCP并不是对每个到来的包都单独的ACK,利用累计ACK可以确认之前的ACK。累计确认可以允许TCP延时一段时间发送ACK,以便将ACK和相同方向上需要传输的数据结合发。这种捎带传输的方法常用于批量数据传输。
不能任意的延迟ACK,会造成重传。同时当失序发生时,必须立刻传送ACK。
系统可以设置,一般延时为200-600毫秒。
该算法要求,当TCP连接中有在传数据(那些已发送,但是未确认的数据)时,小的报文段就不能被发送,知道所有的数据都受到ACK。并且受到ACK后,TCP收集这些小的数据,整合到一个报文段中发送。
这种方法破事TCP遵循等停规程,只有收到所有传送数据的ACK后才能继续发送新数据。
该算法的不同之处在于他实现了自时钟控制,ACK返回越快,传输也越快。在相对高延迟的广域网中,更需要减小小报文的数目,该算法使得单位时间内发送的报文数目更少,RTT控制发包速率。
该算法减少小包数目的同时,也增大了传输时延,也就是总的发送时间。
窗口大小表明本端可用缓存大小,对端传送的数据不应该超过改大小。
也表明对端发送的数据的最大大小为TCP头部ACK号和窗口大小字段之和。
也就是Seq = ACK+MSS
TCP活动的两端都维护一个发送窗口结构和接受窗口结构。
TCP以 字节 为单位维护窗口结构。
每个TCP报文段都包含ACK号和窗口通告信息,TCP发送端可以据此调节窗口结构。
窗口左边界不能左移。
窗口的动作分为,关闭(收到ACK,左边右移),打开(MSS扩大,右边右移),收缩(MSS减小,右边左移)
当收到ACK号增大,而MSS不变时窗口向前 滑动
当当左边界与右边界相等时,成为 零窗口 ,此时发送端不能在发送新的数据,这种情况下,TCP开始探测对端窗口,伺机增大窗口。
当接受窗口值变为0是,可以邮箱的组织发送端继续发送,知道窗口大小回复为非0值。当接收端窗口得到可用空间是,就会给发送端传输一个窗口更新,告知器可以继续发送数据,这样的这样的窗口更新通常不包含数据,成为纯ACK,因此不能保证传输的可靠性。
如果一端的窗口更新ACK丢失,通信双方就会处于等待状态。为避免这种情况发生。发送端会采用一个持续计时器间歇性的查询接收端,看其窗口是否已经增长。
持续计时器会触发窗口探测的传输,强制要求对端返回ACK。 窗口探测包 包含一个字节数据,采用TCP传输,因此可以避免窗口更新丢失导致的死锁。因为包含一个字节数据Seq改变,接受端必须处理,如果接受就会ACK。窗口大小还是0,那么就会丢弃该报,没有响应。这时候发送端会持续的发送窗口探测包。
当接收端通告窗口较小,或者发送端发送的数据较小。这样数据报的有效携带率小,耗费网络资源多。
避免方法
以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补
这个窗口是一个浮动窗口,每次接收的量(字节数)在窗口里定义,最大的窗口大小最大为65535字节,
现实中你可以这样想,你在眼和头都不动的时候看外面,能看到的外面景物大小如果理解为窗口大小,那眼动一下看到的新的景物就是新窗口了,那么现在眼和头都不动, 一个窗口每移一次就是新的一个IP数据报 ,窗口就是数据报大小
解答:要传送的512B的数据必须划分为6个报文段传送,前5个报文段各100B,最后一个报文段传送12B图T-5-41是双方交互的示意图。
下面进行简单的解释。
报文段#1:A发起主动打开,发送SYN报文段,处于SYN-SENT状态,并选择初始序号seq=100。
B处于LISTEN状态。报文段#2:B确认A的SYN报文段,因此ack=101(是A的初始序号加1)。
B选择初始序号seq=200。B进入到SYN-RCVD状态。报文段#3:A发送ACK报文段来确认报文段#2,ack=201(是B的初始序号加1)。A没有在这个报文段中放入数据。
因为SYN报文段#1消耗了一个序号,因此报文段#了的序号是seq=101这样,A和B都进入了ESTABLISHED状态。报文段#4:A发送100字节的数据。
报文段#3是确认报文段,没有数据发送,报文段#3并不消耗序号,因此报文段#4的序号仍然是seq=101A在发送数据的同时,还确认B的报文段#2,因此ack=201。报文段#S:B确认A的报文段#4。
由于收到了从序号101到200共100字节的数据,因此在报文段#5中,ack=201(所期望收到的下一个数据字节的序号)。
B发送的SYN报文段#2消耗了一个序号,因此报文段#5的序号是seq=201,比报文段#2的序号多了一个序号。
在这个报文段中,B给出了接收窗口rwnd=100从报文段彬6到报文段#13都不需要更多的解释。到此为止,A已经传送了500字节的数据。
值得注意的是,B发送的所有确认报文段都不消耗序号,其序号都是seq=201报文段#14:A发送最后12字节的数据,报文段#14的序号是seq=601。
扩展资料:
TCP建立连接
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接,TCP使用的流量控制协议是可变大小的滑动窗口协议。
TCP三次握手的过程如下:
客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
参考资料来源:百度百科-TCP
整理自 CSDN 公众号
1 客户端
TCP 三次握手的开始是客户端发起 SYN,如果服务端没有及时回复,那么会重传,重传的间隔和次数是可控的,默认是五次,第一次间隔 1 秒,第二次 2 秒,第三次 4 秒,第四次 8 秒,第五次16 秒,最终超时时间是 63 秒,因此在优化时可以修改重传次数和间隔,以尽快把错误暴露给应用程序。
2 服务端的半连接队列优化
服务端在第一次返回 SYN + ACK 时,就会把这次请求维护进一个半连接队列,这个队列用来维护尚未完成的握手信息(相对于全连接),如果这个队列溢出了,服务端就无法继续接受新的请求了,这也是 SYN Flood 攻击的点。
通过一个命令 netstat -s 可以得到累计的、由于半连接队列已满引发的失败次数,隔几秒执行一次就可以知道这个次数是否有上升的趋势以及分析是否正常。
这种 SYN Flood 攻击之所以成立,是因为维护这个半连接队列一定要分配一定的内存资源,那么应对的方式之一 syncookies 就是如何不分配资源的前提下,可以确认是一次有效的连接并 establish。
syncookies 的工作原理是,服务器使用一种算法,计算出一个哈希值,它包含了客户端发来请求的部分信息,再将这个哈希值和 SYN+ACK 一起返回给客户端,客户端也经过一些运算,再返回给服务端,那么服务端根据这个返回值和之前的计算值比较,如果合法,就可以建立有效连接,从而不会占据半连接队列的内存。应对 SYN 攻击时,只需将 syncookies 的参数值调为 1(半连接队列溢出时启用),即可。
相当的,可以增大半连接队列,但是要和 accept 的队列同时增大才有效,(否则会导致 accept 队列溢出同样丢失 TCP 连接)
此时,对于客户端来说已经是 established 状态,但是还要再返回给服务端一个 ACK,服务端收到后,服务端才是 established 状态并开始传数据,如果网络不稳定,同样的,服务端会重发 SYN+ACK,当网络不稳定时,应该增加服务端重发 SYN+ACK 的次数。
3 服务端的 accept 队列优化
当连接已经建立、应用程序尚未调用时,TCP 连接会被保存在一个 accept 队列中,如果进程未能及时调用,就会导致 accept 队列溢出,溢出部分连接将被默认丢弃。对此可以做的是,选择向客户端发送 RST 报文,告知关闭这个连接,丢弃握手过程。打开这一功能需要将 tcp_abort_on_overflow 参数设置为 1。如果想让客户端了解是由于 accept 队列溢出造成连接失败可以这样做。当 tcp_abort_on_overflow 参数设置为 0 时,则如果 accept 队列溢出,就会丢弃客户端传来的 ACK(用于最后一次握手)。
应对高并发流量时,更好的选择是 tcp_abort_on_overflow 参数设置为 0,这样对于客户端它的状态仍然是 established,客户端会定时发送带有 ack 报文的发送数据请求,一旦服务端的 accept 队列有空位,那么连接仍有可能建立成功。所以只有很确定在一段时间内 accept 都是将溢出的状态,才推荐 tcp_abort_on_overflow 参数设置为 1。
同样的,可以调整 accept 队列长度,也可以查看累计的由于溢出导致丢失的连接总数,来判断趋势。
在 Linux 37 内核版本之后,提供了 TCP Fast Open 功能,这个功能如此生效:
初次建立 TCP 连接时,客户端在第一个 SYN 包中传入一个请求 cookie,表明打开 fast open 功能,服务端对应生成一个 cookie 给客户端,除此之外,三次握手没有不同,但是,在 cookie 没有过期之前,下一次再连接的时候,客户端发送带有 cookie 的 SYN 包,服务端校验了 cookie 有效以后,就可以开始传输数据了,从而节约了一个往返的时间消耗。
TCP Fast Open 功能需要服务端和客户端同时打开才能生效。
(备注一个之前看到差点忘了的知识点。
当主动方收到被动方的 FIN 报文后,内核会回复 ACK 报文给被动方,同时主动方的连接状态由 FIN_WAIT2 变为 TIME_WAIT,在 Linux 系统下大约等待 1 分钟后,TIME_WAIT 状态的连接才会彻底关闭。
1 主动方的优化
关闭的方式有两种 RST 和 FIN,RST 是暴力关闭连接的方式,安全关闭连接则必须四次挥手。
FIN 报文关闭则可以使用 close 和 shutdown 两种函数来实现。close 相对来说是“不优雅”的,调用 close 的一方的连接叫做「孤儿连接」,会同时关闭读和写,而 shutdown 可以控制是读还是写。
关闭读的时候,会丢弃接收缓冲区里的所有数据,如果后续再接受到数据,也会悄悄丢弃,并发送 ACK,对方不会知道被丢弃了。
关闭写的时候,会把发送缓冲区的数据全部发送并发送 FIN。
(1)FIN_WAIT1 的优化
主动方发送 FIN 以后,进入 FIN_WAIT1 状态,如果迟迟没收到 ACK,会定时重发 FIN,重发次数由 tcp_orphan_retries 参数控制,默认为 8 次,如果处于 FIN_WAIT1 状态的连接过多,应该考虑降低次数,重发次数超过参数时,连接会被直接关闭。
如果遇到恶意攻击,可能无法发送出 FIN,因为 TCP 按顺序发送所有包, FIN 也不能绕过,另外如果对方的接收窗口已经满了,发送方也无法再发送数据。
此时应该做的是调整 tcp_max_orphans 参数,它定义了「孤儿连接」的最大数量,当系统中的孤儿连接超过参数值,新增的孤儿连接不会再处于 FIN_WAIT1 状态,而是会被 RST 报文直接关闭。(只会影响 CLOSE 函数关闭的连接,不会影响 shutdown 关闭的,不会影响还有读或写的可能)
(2)FIN_WAIT2 的优化
主动方收到 ACK 后,会处于 FIN_WAIT2,因为被动方还可能有数据发送,如果是 shutdown 关闭,那它也可能还会发送数据,但是对于 close 关闭的连接,无法再发送和接收数据,保持在 FIN_WAIT2 的状态已经没有太大意义,tcp_fin_timeout 控制了这个状态下连接的持续时长,默认值是 60 秒。这个时间和 TIME_WAIT 状态时长是一致的。
(3)TIME_WAIT 的优化
TIME_WAIT 和 FIN_WAIT2 的时间是一致的,都是 2MSL,1MSL 表示一个报文在网络中存活的最长时间(报文每经过一次路由器的转发,IP 头部的 TTL 字段就会减 1,减到 0 时报文就被丢弃,这就限制了报文的最长存活时间),那么为什么是等待 2MSL 呢,其实就是允许报文至少丢失一次、再发送一次,这样第一个丢失了,等待的时间里第二个 ACK 还会到达,为什么不是 4MSL 以上呢,这是一个概率的问题,如果一个网络丢包率达到 1%,那么连续两次丢包的概率是万分之一,不必为了这种概率增加等待的时长。
TIME_WAIT 有存在的意义,但是太多保持在这种状态的连接会占用双方资源,占据客户端的端口资源和服务端的系统资源。
Linux 提供了 tcp_max_tw_buckets 参数,当 TIME_WAIT 的连接数量超过该参数时,新关闭的连接就不再经历 TIME_WAIT 而直接关闭。这个参数的设定应该取一个平衡点,即既不会太少导致高并发时产生连接间数据错乱的问题,也不会太多而导致耗尽端口和线程资源。
对于用户端来讲,还可以启用 tcp_tw_reuse 参数来复用处于 TIME_WAIT 状态的连接(来节约接口资源。)这个参数有几个前提,一个是只有客户端可以打开,一个是 TIME_WAIT 状态也要保持 1 秒,另一个是要同步打开时间戳功能,报文带上时间戳就可以避免没有了 2MSL 时长以后的混乱情况,时间戳过期的报文就会被丢掉。
另外对于 TIME_WAIT,还可以调整 socket 选项,来达到调用 close 关闭连接时跳过四次挥手直接关闭的效果,但不推荐。
2 被动方的优化
首先,被动方收到 FIN 时,会自动回复 ACK,接着等待应用程序调用 close/shutdown 来结束连接,再发送 FIN。如果系统中同时查看到多个连接处于 CLOSE_WAIT 状态,则需要排查是否是应用程序出了故障。
然后,当被动方也发送了 FIN 以后,还需要等待主动方回复一个 ACK,如果迟迟没收到,也会重发 FIN,重发次数也是 tcp_orphan_retries 参数控制,这点和主动方的优化一致,可以调整次数。(需确认被动方是否有 tcp_max_orphans 参数)
3 如果双方同时关闭?
1 ACK 延迟
目前在 TCP 中每传输一个报文都要求接收方进行确认,大量短而频繁的确认报文给网络带来了很多开销。因此采取了延迟 ACK 策略来减少 ACK 的数量,就是接收方收到一个报文以后,不会立即发送 ACK,而是等待 1~200ms,这期间若有回送数据报文就捎带确认,但收到两个连续数据报文或者等待超时则发送一个独立确认。有效减少了 ACK 的数量,改善了 TCP 的整体性能。
2 滑动窗口
接收方的接收缓冲区不是不变的,接收到新的会变小,应用程序取出后又会变大,因此接收方会把自己当前的接收窗口大小放在 TCP 头告知发送方,如果不考虑拥塞控制,发送方的窗口大小「约等于」接收方的窗口大小。
对于这一点,可以把 tcp_window_scaling 配置设为 1(默认打开)来扩大 TCP 通告窗口至 1G 大小。要使用这一选项,需要主动方在 SYN 中先告知,被动方在 SYN 中再反馈。
但是缓冲区并非越大越好,还要考虑网络吞吐的能力。如果缓冲区与网络传输能力匹配,那么缓冲区的利用率就达到了最大化。
3 调整缓冲区大小
这里需要说一个概念,就是带宽时延积,它决定网络中飞行报文的大小,它的计算方式:
(1)发送缓冲区的调整
发送缓冲区是自行调节的,当发送方发送的数据被确认后,并且没有新的数据要发送,就会把发送缓冲区的内存释放掉。
接收缓冲区要复杂一些:
上面三个数字单位都是字节,它们分别表示:
(2)接收缓冲区的调整
接收缓冲区可以根据系统空闲内存的大小来调节接收窗口:
(3)内存的判断
那么如何判断内存紧张或充分呢?
上面三个数字单位不是字节,而是「页面大小」,1 页表示 4KB,它们分别表示:
在实际的场景中,TCP 缓冲区最小值保持默认 4K 即可,来提高并发处理能力;最大值则尽可能靠近带宽时延积,来最大化网络效率。
总结以上:为了提高并发能力、提高网络效率,我们要充分利用网络能力和自己的内存。网络这方面就是将缓冲区大小的极值尽可能靠近带宽时延积,而同时对缓冲区的自动调节需要结合内存来判断,这个 TCP 内存的判断是通过系统内存计算出来的几个值来划分的,在不同区间会对分配给缓冲区的内存大小进行调整。
以上就是 TCP 在不同阶段的优化策略和思路,有关拥塞控制和流量控制之后再补一篇笔记。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)