这个问题有点令人困惑,我用谷歌和 Stack Overflow 一遍又一遍地寻找答案。我在 VB.NET 中编写了一个 Windows 服务,它使用 TcpListener 类来侦听连接应用程序。这是一个相当直接的服务,它只允许应用程序的多个实例将 UDP 数据报发送到我们在端口 7000 上侦听的仪器。该服务的原因是因为它必须在端口 7001 上侦听来自仪器的 UDP 数据报,并转发它们到使用 TCP 连接的客户端应用程序。
无论如何,关于我的问题。当客户端应用程序是使用 TcpClient 连接到服务的 VB.NET 应用程序时,一切运行正常。当客户端应用程序关闭其 TCP 连接时,服务中的 TcpClient 无法再接收数据(这会导致异常,进而提示服务关闭并删除有问题的 TcpClient)。到目前为止,一切都很好。
当客户端是使用 Winsock 控件连接到服务的 VB6 应用程序时,一切都很好,直到 Winsock 控件关闭连接。此时,TcpClient.Connected 属性仍为 True,尝试读取流不会导致异常。本质上,服务仍然认为连接是打开的。更糟糕的是,网络流中 BeginReceive 函数的回调函数不断被本应关闭的 TcpClient 调用。这会导致 Process Explorer 显示服务上升到使用 100% 的处理器(或内核)。经过进一步调查,Process Explorer 将连接状态报告为 CLOSE_WAIT。
也许 TcpClient 类中还有另一个属性或方法,我可以检查连接是关闭还是关闭。不过,到目前为止我还没有找到它。这是代码的副本,供大家检查。也许你可以看到我错过了什么。感谢您抽时间阅读。
' Receives the TCP packet from the clients, processes them and forwards them on as requested.
Private Sub ReceiveTcpPacketCallback(ByVal AsyncResult As System.IAsyncResult)
' Retrieve the asynchronous state for the callback.
Dim AsyncState As TcpAsyncState = CType(AsyncResult.AsyncState, TcpAsyncState)
' Get the TCP client associated with this state.
Dim TcpClient As System.Net.Sockets.TcpClient = AsyncState.TcpClient
Try
If TcpClient.Connected Then
Dim CommandData As String
' Get the network stream associated with the TCP client that sent the packet.
Dim NetworkStream As System.Net.Sockets.NetworkStream = TcpClient.GetStream
' Sync lock the receive buffer to ensure that no other threads are touching it.
SyncLock m_pReceiveBuffer
' End the read to retrieve the data into the locked buffer.
Dim DataLength As Integer = NetworkStream.EndRead(AsyncResult)
' Convert the bytes retrieved into a string using ASCII encoding.
CommandData = System.Text.Encoding.ASCII.GetString(m_pReceiveBuffer, 0, DataLength)
End SyncLock
' Process the command data that was sent from the TCP client and process the command if complete.
If AsyncState.ProcessCommandData(CommandData) Then ProcessCommandInformation(AsyncState)
' Continue listening for more commands from the client.
NetworkStream.BeginRead(m_pReceiveBuffer, 0, m_pReceiveBuffer.Length, AddressOf ReceiveTcpPacketCallback, AsyncState)
Else
' Attempt to close the TCP client and remove it from the list.
TcpClient.Close()
If m_pTcpClients.Contains(TcpClient) Then m_pTcpClients.Remove(TcpClient)
End If
Catch ex As Exception
Dim Message As New System.Text.StringBuilder
' If an exception occurs, assemble the message to write to the event log.
Message.Append(Date.Now.ToString).Append(","c).Append("ReceiveTcpPacketCallback Exception,")
Message.Append("Error Receiving Command From TCP Client,")
Message.Append(ex.Source).Append(","c).Append(ex.Message)
' Write the message to the event log.
Me.EventLog.WriteEntry(Message.ToString)
' Attempt to close the TCP client and remove it from the list.
TcpClient.Close()
If m_pTcpClients.Contains(TcpClient) Then m_pTcpClients.Remove(TcpClient)
End Try
End Sub