管理杂谈OA答疑ERP答疑教程搜索

TCP客户端Socket如何立即释放端口?


在做一个关于web负载均衡的压力测试的时候碰到一个问题。
测试采用的是单机测试,web服务器+3个负载均衡节点服务+客户端 都在同一台4核工作机上。
由于测试逻辑简单,1024个http客户端4s左右会吃光6W个端口,继续访问就会出现错误:由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作。错误的原因是Socket占用的端口没有被释放,需要等待20+s才能进行下一轮测试,很浪费时间。

需要说明的是,释放客户端端口不像服务器端口那样关闭Socket就可以了。客户端端口在Socket关闭之后处于Close_Wait状态,这个状态时间应该是由HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters下的TcpTimedWaitDelay键值决定的,但是这个值最小值是30s。
另外可以使用API函数SetTcpEntry强行关闭连接释放端口,但是某些操作系统由于UAC的原因需要提升为管理员权限,如果通过app.manifest设置管理员身份运行,会弹出一个提升权限的对话框,作为一个性能测试的Demo弹出这样一个框感觉有点吓人。

希望高人指点,如何在不需要用户参与的情况下把端口释放掉。需要具体测试代码的话可以到这里下载https://fastcsharp.codeplex.com/,测试项目为demo.loadBalancingTcpCommandWeb。

发现连接数超某数时,及时清除不用套接字子线程,服务器与客户端均可设超时强制关闭

''及时清除不用的子线程。

Private Sub dogthread(ByVal Tzobj As System.Object, ByVal e As System.EventArgs)

dogrunBZ(Tzobj) = True                                                              ''正在运行时,防止重复运行

Try

mySocket(Tzobj).Shutdown(SocketShutdown.Receive)   ''关闭套接字接收

''mySocket(Tzobj).Shutdown(SocketShutdown.Both)        ''组态王对此条支持不好

Catch

End Try

Try

mySocket(Tzobj).Close()                                                          ''引发子线程立即异常,中断子线程循环

Catch

End Try

Try

myThread(Tzobj).Abort()                                                          ''中止某个线程,销毁这些线程,需要很多时间!

myThread(Tzobj).Join(300)                                                     ''等待300毫秒,超时也终止,防止不出来

Catch

End Try

Try

mySocket(Tzobj) = Nothing                                                     ''防止组态王响应没那么快!!!

Catch

End Try

GC.Collect()

dogrunBZ(Tzobj) = False                                                             ''运行完毕标志 

End Sub


''及时清除不用的子线程。

Private Sub dogthread(ByVal Tzobj As System.Object, ByVal e As System.EventArgs)

dogrunBZ(Tzobj) = True                                 ''正在运行时,防止重复运行

Try

mySocket(Tzobj).Shutdown(SocketShutdown.Receive)   ''关闭套接字接收

''mySocket(Tzobj).Shutdown(SocketShutdown.Both)     ''组态王对此条支持不好

Catch

End Try

Try

mySocket(Tzobj).Close()                            ''引发子线程立即异常,中断子线程循环

Catch

End Try

Try

myThread(Tzobj).Abort()                            ''中止某个线程,销毁这些线程,需要很多时间!

myThread(Tzobj).Join(300)                          ''等待300毫秒,超时也终止,防止不出来

Catch

End Try

Try

mySocket(Tzobj) = Nothing                          ''防止组态王响应没那么快!!!

Catch

End Try

GC.Collect()

dogrunBZ(Tzobj) = False                                ''运行完毕标志 

End Sub


谢谢回复,真不是这么简单的事。你可以试试能不能在30s之内创建>65536个客户端连接。

我对tcp研究的不是那么深.. 请问你这个6W多连接  是长连接没有断开的?

不是吧 client的端口也没这么多..

如果是ope send close dispose的话 感觉还差不多可以..

否则这也不叫长连接了啊.....

难道你的意思 本地client使用了5000端口连接 然后这5000还可以个下一个client用?

不存在啊..那么你2个client 服务器如何找到客户端...

TcpClient c = new TcpClient();

c.Connect(IPAddress.Parse("x"), o);

c.Client.Send(by);

c.Client.Shutdown(SocketShutdown.Both);

c.Client.Close();

c.Close();

​这虽然是tcp_client但是发了就没了.....具体我还真没测试过..不知道我想的是不是跟你想的一样..


长连接就不是30s的问题了,短连接,用完立即Shutdown+Close,Dispose也是没用的。 我这里可以用到6W+端口,这个数量与范围也可以修改注册表。
测试中同时存在的client只有1024,也就是1024并发。
端口在Socket关闭之后是可以给其它Socket使用的,不过现在的问题就是要等待30s。
引用 5 楼 diaodiaop 的回复:
C# code
?
1
2
3
4
5
6
            TcpClient c = new TcpClient();
            c.Connect(IPAddress.Parse("x"), o);
            c.Client.Send(by);
            c.Client.Shutdown(SocketShutdown.Both);
            c.Client.Close();
            c.Close();
这虽然是tcp_client但是发了就没了.....具体我还真没测试过..不知道我想的是不是跟你想的一样..
你在Close之后的30s内应该都能用netstat看到这个连接处于Close_Wait状态,并没有释放端口。

假设本地0-65536都可用
也就是 
C# code
?
1
2
3
4
5
6
7
for 30000
           TcpClient c = new TcpClient();
            c.Connect(IPAddress.Parse("x"), o);
            c.Client.Send(by);
            c.Client.Shutdown(SocketShutdown.Both);
            c.Client.Close();
            c.Close();
这个情况下..使用了0-30000这个3W个port..
30s之内 这些port是不能重复使用的..
假设在30s之内已经到了65535 就死掉了..但是理想中希望的是  前面的端口已经释放了 还可以用 是吧..
这个还真没研究过..一般都是玩server.client哪有这么多啊..
如果你只是要测试server的压力 我都用别人的软件测试并发. 如果自己写好像就是什么线程池吧....
就是先建立连接不发..然后..一起发送...

是这样的,压力测试工具都有这个问题。要么达不到这个吞吐量,要么需要管理员身份运行。 如果仅仅是自己用没有什么问题,但是作为一个Demo要以管理员身份运行,有点过分,不合适。

服务器端可以限制连接总数,超过总数的,拒绝连接即可,保持连接总数不超过mou值
Visual Basic code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
          If i < 100 Then ''否则,线程已满,忙。。 不接收新线程了
                    mySocket(i) = socktep
                    socktep = Nothing
                    Try
                        myThread(i) = New Thread(AddressOf WaitData)  ''建立新的线程--为每个客户端创建一个线程
                        myThread(i).IsBackground = True
                        Threadnumber = i    '' 开启的当前线程号数
                        myThread(i).Start()
                    Catch
                        IsRun(i) = False   ''改标志,以便侦听时,消除此出错线程
                    End Try
                Else
                    ''人数已满,稍后连接!--关闭第100个套接字
                    If mySocket(100) Is Nothing Then
                        mySocket(100) = socktep
                        socktep = Nothing
                        Try
                            Dim msg1 As String "服务器忙,稍后连接!"
                            Call sendmsg(msg1, 100)   ''把msg1内的消息内容 发送出去
                            msg1 = "Exit"
                            Call sendmsg(msg1, 100)
                        Catch
                        End Try
                        Try
                            System.Threading.Thread.Sleep(1)
                            mySocket(100).Shutdown(SocketShutdown.Both)
                        Catch
                        End Try
                        Try
                            System.Threading.Thread.Sleep(2)
                            mySocket(100).Close()
                            mySocket(100) = Nothing
                        Catch
                        End Try
                    Else
                        Try
                            Dim msg As Byte() = Encoding.UTF8.GetBytes("Exit")
                            Dim bytesSent As Integer = socktep.Send(msg)
                        Catch
                        End Try
                        Try
                            System.Threading.Thread.Sleep(1)
                            socktep.Shutdown(SocketShutdown.Both)
                        Catch
                        End Try
                        Try
                            System.Threading.Thread.Sleep(2)
                            socktep.Close()
                            socktep = Nothing
                        Catch
                        End Try
                        If dogrunBZ(100) = True Then
                            dogrunBZ(100) = False    ''关闭线程的相应异步调用,正在运行时=true  ''防止重复运行
                        End If
                        Try
                            mySocket(100).Shutdown(SocketShutdown.Both)
                        Catch
                        End Try
                        Try
                            System.Threading.Thread.Sleep(2)
                            mySocket(100).Close()
                            mySocket(100) = Nothing
                        Catch
                        End Try
                    End If
                End If

没什么好办法。除非你自己使用 TcpClient 封装一个“http客户端”类(不用.net类库中现成的),重复使用本地端口、而不关闭。

更多精彩文章浏览...
点击右上角图标分享到朋友圈
官方网站:https://www.clicksun.cn
咨询热线:400-186-1886
服务邮箱:service@clicksun.cn