【求助】【WINSOCK】客户端断开后,服务端如何正确快速地关闭被占用的端口?

goodloop 2006-03-08 10:31:52
小弟昨天开始研究WINSOCK控件的使用,到社区翻了不少相关的帖子,但是这个疑问却还没能够解决

现在程序都是在本地测试,分成服务端和客户端两部分

客户端较为简单:(只使用一个WINSOCK控件)
Me.WinSock.RemoteHost = "localhost"
Me.WinSock.RemotePort = 5000
Me.WinSock.LocalPort = 5001
Me.WinSock.Connect

要求是打开使用5001端口,然后连接本地的5000端口(服务端)

服务端使用了2个WINSOCK控件数组
WinSock() 用于监听端口
WinSockArray() 用于接收数据

基本代码如下:
Dim SocketList As Integer

Private Sub Form_Load()
' 初始化
Me.WinSock(0).LocalPort = 5000
Me.WinSock(0).Listen
SocketList = 1
End Sub

' 收到连接请求
Private Sub WinSock_ConnectionRequest(Index As Integer, ByVal requestID As Long)
' 新建WinSockArray用于接收数据
Load Me.WinSockArray(SocketList)
Me.WinSockArray(SocketList).Accept requestID
SocketList = SocketList + 1

' 重启监听用WinSock
Me.WinSock(Index).Close
Me.WinSock(Index).Listen
DoEvents
End Sub

' 收到数据包
Private Sub WinSockArray_DataArrival(Index As Integer, ByVal bytesTotal As Long)
Dim mydata As String: mydata = ""
Me.WinSockArray(Index).GetData mydata

' 数据处理部分
DoEvents
End Sub

' 收到关闭请求
Private Sub WinSockArray_Close(Index As Integer)
If Me.WinSockArray(Index).State <> sckClosed Then
Me.WinSockArray(Index).Close
End If
End Sub

首先开启服务端,然后开启客户端
这时候可以非常顺利的发送数据(多次也可以)

之后关闭客户端再重新开启
就会发生错误:"10048,地址正在被使用"
在Console下使用netstat命令下查看
也会看到"TCP 127.0.0.1:5001 127.0.0.1:5000 TIME_WAIT"

在社区里找到的帖子上说要在"Sub WinSockArray_Close"中正确的关闭连接
但是还没有找到有明确的代码说明如何才是正确的关闭

请各位对此有研究的前辈看一下小弟的代码
如果能给出解决方法那就更好了
先谢过 ^_^
...全文
730 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
goodloop 2006-03-09
  • 打赏
  • 举报
回复
谢谢,你们这样解释我就明白了

之前过分拘泥于无用的东西
jackcaixia 2006-03-09
  • 打赏
  • 举报
回复
对~~客户端没必要加这句Me.WinSock.LocalPort = 5001
zyg0 2006-03-09
  • 打赏
  • 举报
回复
客户端去掉这个 Me.WinSock.LocalPort = 5001
加上这个有时候出错
如果是udp协议 这个是必需的 tcp不需要

2客户端掉用 close方法 服务器会触发正常的 close()事件
非法客户端关闭 服务器没有响应 需要自己处理
goodloop 2006-03-08
  • 打赏
  • 举报
回复
谢谢提供代码的两位朋友

但似乎我的问题和你们讲的有些不同

就是在客户端,我指定了传输的出口:#5001端口

在你们给出的代码中,对于客户端,并没有指定LocalPort

这样系统会为你的程序随意分配一个没有使用到的端口作连接

就算关闭客户端后再次重开,系统还是会自动为程序分配端口

这样永远不会碰到我所遇到的问题

可以在CMD 里用NETSTAT 看一下,就会明白我说的了

关闭客户端后,刚才被分配的端口没有立刻被释放

而是过一段时候(大约几分钟),由系统自己来释放

在此期间,其他程序无法使用此端口

所以我的问题应该是:如何强制释放客户端曾经使用过的端口(在我的程序中是5001端口)
christion3 2006-03-08
  • 打赏
  • 举报
回复
Dim userState() As Integer '-1 请求退出 0 离线 1 正常在线 2 只能看不能发言 3 正在被踢 4 客户端非正常终止 判断状态


Private Sub sckServer_Close(index As Integer)

If userState(index) = 1 Then userState(index) = -1 '客户端请求退出
Call Stop_sckServer(index, userState(index))

End Sub

Private Sub SendToAll(msg As String)
For i = 0 To MaxChan - 1

If sckServer(i).State = 7 Then

sckServer(i).SendData Trim(msg)

DoEvents

End If
Next i

End Sub

Private Sub Stop_sckServer(index As Integer, State As Integer) 'State=-1 正常终止 'State>0 非正常终止
Dim S As String

sckServer(index).Close

If userState(index) <> 0 Then
zxrs = zxrs - 1 '在线人数减一

Label1.Caption = " " & zxrs & "人在线"

S = "~" & user(index) & "未知原因被终止!~"
If State = -1 Then S = "~" & user(index) & " 退出聊天室~"
If State = 3 Then S = "~" & user(index) & " 被管理员踢出聊天室~"
If State = 4 Then S = "~" & user(index) & " 非正常退出!~"
Call SendToAll(S)
DoEvents

Call SendToAll("SystemOrder:removefromlist" & user(index))


For i = 0 To List1.ListCount - 1
If List1.List(i) = user(index) Then Exit For
Next
List1.RemoveItem i '从在线名单上删除退出者
Combo1.RemoveItem i + 1
Combo1.ListIndex = 0


user(index) = "" '清除退出者姓名记录
userIP(index) = "" '清除退出者IP记录
userState(index) = 0 '设置用户状态为离线
Call AddToText1(S)

End If
End Sub
Oreo.M 2006-03-08
  • 打赏
  • 举报
回复
mark
凯晰叶子 2006-03-08
  • 打赏
  • 举报
回复
楼上的代码不错啊!收下,谢谢
jackcaixia 2006-03-08
  • 打赏
  • 举报
回复
呵呵~~我刚好也遇到这个问题~~而且已经解决了~~以下是我的完整代码:
服务端:
Private Sub form_Load()
With Me
.SckServer(0).LocalPort = 1001 '本地端口
.SckServer(0).Listen '开始监听
End With
End Sub

Private Sub Form_Unload(Cancel As Integer)
Dim i As Long
For i = 1 To SckServer.Count - 1
If SckServer(i).State = 7 Then
SckServer(i).Close
End If
Next
SckServer(0).Close

End Sub

Private Sub SckServer_Close(Index As Integer)
Me.SckServer(Index).Close
Me.SckServer(Index).Tag = ""
End Sub

Private Sub sckServer_ConnectionRequest(Index As Integer, ByVal requestID As Long)
Dim i As Long, l As Boolean, cou As Long, flag As Long
cou = 0
For i = 1 To Me.SckServer.Count - 1
If Me.SckServer(i).State = 0 And Not l Then
Me.SckServer(i).Accept (requestID)
flag = i
l = True
Else
cou = cou + 1
End If
Next
If Not l Then
i = Me.SckServer.Count
Load Me.SckServer(i)
flag = i
Me.SckServer(i).Accept (requestID)
End If

End Sub
Private Sub sckServer_DataArrival(Index As Integer, ByVal bytesTotal As Long)
Dim strData As String
SckServer(Index).GetData strData
Text1.Text = strData
End If
End Sub

Private Sub SckServer_Error(Index As Integer, ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
Call SckServer_Close(Index)
End Sub
客户端:


Private Sub form_Load()
With Me
.sckClient.RemoteHost = "192.168.0.46" '设置远程IP,本例设为本机。
.sckClient.RemotePort = 1001 '远程端口,就为server中的设置一样.
End With
sckClient.Connect
Text1.Text = "正在测试连接....."
End Sub

Private Sub Form_Unload(Cancel As Integer)
If Me.sckClient.State = 7 Then Me.sckClient.Close
' sckClient.SendData "QUIT"
End Sub

Private Sub sckClient_Close()
sckClient.Close
Text1.Text = "服务器未开启"
End Sub

Private Sub sckClient_Connect()
Text1.Text = "连接成功"
End Sub

Private Sub sckClient_ConnectionRequest(ByVal requestID As Long)
With Me
If .sckClient.State <> sckClosed Then .sckClient.Close
.sckClient.Accept (requestID)
End With
End Sub

Private Sub sckClient_DataArrival(ByVal bytesTotal As Long)
On Error Resume Next
Dim strData As String
sckClient.GetData strData
Text1.Text = strData
End Sub

Private Sub sckClient_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
Call sckClient_Close
End Sub
goodloop 2006-03-08
  • 打赏
  • 举报
回复
先谢谢楼上的朋友,代码我还没仔细看

似乎关键部分还是那句sckServer().Close

另外多加了一个状态判断,是我的程序里没有的

让我先研究一下,之后在揭帖

1,502

社区成员

发帖
与我相关
我的任务
社区描述
VB 网络编程
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧