Socket服务端关闭问题
源代码(VB.NET 2005)
Imports System.Net
Imports System.Net.Sockets
Imports System.Threading
Imports System.Text
Public Class frmMain
Private objSocketServer As SocketServer
Public Sub New()
objSocketServer = New SocketServer()
End Sub
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
objSocketServer.BindIP = txtInternetIP.Text.Trim()
objSocketServer.BindPort = Integer.Parse(txtDataListenPort.Text)
objSocketServer.Run()
End Sub
Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
objSocketServer.StopRun()
End Sub
End Class
Public Class SocketServer
Private m_BindIP As String
Private m_BindPort As Integer
Private RunState As Boolean
Private aryClientList As ArrayList
Private sckServer As Socket
'Private thdAccept As Thread
Private Done As ManualResetEvent
Public Sub New()
m_BindIP = " "
m_BindPort = 0
RunState = False
aryClientList = New ArrayList()
Done = New ManualResetEvent(False)
End Sub
Public Property BindIP() As String
Get
Return m_BindIP
End Get
Set(ByVal value As String)
m_BindIP = value
End Set
End Property
Public Property BindPort() As Integer
Get
Return m_BindPort
End Get
Set(ByVal value As Integer)
m_BindPort = value
End Set
End Property
Public Sub StopRun()
RunState = False
sckServer.Close()
'thdAccept.Abort()
End Sub
Public Sub Run()
Dim ipLocal As IPAddress
Dim intListenPort As Integer
Dim ipepServer As IPEndPoint
ipLocal = IPAddress.Parse(m_BindIP.Trim())
intListenPort = m_BindPort
ipepServer = New IPEndPoint(ipLocal, intListenPort)
sckServer = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Try
sckServer.Bind(ipepServer)
sckServer.Listen(100)
Dim thdAccept As New Thread(AddressOf ThreadBeginAccept)
thdAccept.Start()
Catch ex As Exception
MessageBox.Show( "错误描述: " & ex.Message & vbCrLf & "错误源: " & ex.Source, "错误 ", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End Try
End Sub
Private Sub ThreadBeginAccept()
RunState = True
Do
Done.Reset()
sckServer.BeginAccept(New AsyncCallback(AddressOf OnConnectRequest), sckServer)
Done.WaitOne()
Loop While RunState = True
End Sub
Private Sub OnConnectRequest(ByVal ar As IAsyncResult)
Done.Set()
Dim Listener As Socket = CType(ar.AsyncState, Socket)
NewConnection(Listener.EndAccept(ar))
Listener.BeginAccept(New AsyncCallback(AddressOf OnConnectRequest), Listener)
End Sub
Private Sub NewConnection(ByVal sckClient As Socket)
Dim objSocketClient As New SocketClient(sckClient)
Debug.Print(objSocketClient.Sock.RemoteEndPoint.ToString() & " 已连接 " & vbCrLf)
aryClientList.Add(objSocketClient)
objSocketClient.RecieveCallback(Me)
End Sub
Public Sub OnRecieveData(ByVal ar As IAsyncResult)
Dim objSocketClient As SocketClient
objSocketClient = CType(ar.AsyncState, SocketClient)
Dim bytRecieveData() As Byte = objSocketClient.GetRecieveData(ar)
'判断远程客户端是否断开连接
If bytRecieveData.Length < 1 Then
Debug.Print(objSocketClient.Sock.RemoteEndPoint.ToString() & " 已断开 " & vbCrLf)
objSocketClient.Sock.Close()
aryClientList.Remove(objSocketClient)
Exit Sub
End If
Dim x As String = Encoding.ASCII.GetString(bytRecieveData).Trim()
Debug.Print(vbCrLf & "接收数据: " & x & vbCrLf)
If Left(x, x.Length) = "123456 " Then
Dim SendBuff() As Byte = Encoding.ASCII.GetBytes( "567890 ".ToCharArray())
objSocketClient.Sock.Send(SendBuff, SendBuff.Length, SocketFlags.None)
End If
objSocketClient.RecieveCallback(Me)
End Sub
End Class
Public Class SocketClient
Private m_sock As Socket
Private RecvBuff(256) As Byte
Public Sub New(ByVal sckSock As Socket)
m_sock = sckSock
End Sub
Public ReadOnly Property Sock() As Socket
Get
Return m_sock
End Get
End Property
Public Sub RecieveCallback(ByVal objSocketServer As SocketServer)
Dim RecieveData As AsyncCallback
Try
RecieveData = New AsyncCallback(AddressOf objSocketServer.OnRecieveData)
m_sock.BeginReceive(RecvBuff, 0, RecvBuff.Length, SocketFlags.None, RecieveData, Me)
Catch ex As Exception
End Try
End Sub
Public Function GetRecieveData(ByVal ar As IAsyncResult) As Byte()
Dim intByteLen As Integer
Dim bytReturn As Byte()
intByteLen = 0
Try
intByteLen = m_sock.EndReceive(ar)
Catch ex As Exception
End Try
ReDim bytReturn(intByteLen - 1)
Array.Copy(RecvBuff, bytReturn, intByteLen)
Return bytReturn
End Function
End Class
问题:
点击btnStart运行之后,客户端服务端连接及发送接收数据一切正常
但在点击btnStop按钮之后,跟踪发现
Public Sub StopRun()
RunState = False
sckServer.Close()
End Sub
也成功执行完毕,原以为应该服务端就停了,可异步方式的托管代码,却还是执行了
Private Sub OnConnectRequest(ByVal ar As IAsyncResult)
Done.Set()
Dim Listener As Socket = CType(ar.AsyncState, Socket)
NewConnection(Listener.EndAccept(ar))
Listener.BeginAccept(New AsyncCallback(AddressOf OnConnectRequest), Listener)
End Sub
并在
NewConnection(Listener.EndAccept(ar))
报错
具体错误是指因sckServer.Close()而导致传进来的ar值因对象已释放而无法访问
请高手帮忙解决,谢谢!
[解决办法]
mark..学习!!
[解决办法]
这是正常的,你要在这里作异常处理。当Socket被关闭后,BeginAccept、BeginReceive、BeginSend、Accept、Receive、Send都会立即返回。并引发异常!所以你要在这些地方作异常处理!
[解决办法]
我的给你参考一下,删除了很多内部的东西,可能运行不了,只能看看,呵呵
New了以后直接BeginReceive就可以了。
关闭时只要Close就行了。
代码如下:
Imports System.IO
Imports System.Text
Imports System.Net
Imports System.Net.Sockets
Public Class MSComm
Implements IDisposable
#Region " 变量定义 "
'-------------------------------
Private _LocalIp As String '侦听本机IP
Private _LocalPort As Integer '侦听本机端口
Private _bKeepWorking As Boolean = False '是否继续侦听和异步接收数据的标志
'***********************************
Private sckListener As TcpListener '连接侦听器
Private sckServer As Socket '服务器连接(被连接方)
'-------------------------------
Private _ReceiveString As String = String.Empty
Private _ReceiveTimeout As Integer = 10
Private _ErrMessage As String = String.Empty
#End Region
#Region " Dispose "
Private Sub Dispose() Implements IDisposable.Dispose
_bKeepWorking = Nothing
_LocalIp = Nothing
_LocalPort = Nothing
_ReceiveString = Nothing
_ReceiveTimeout = Nothing
_ErrMessage = Nothing
MyBase.Finalize()
End Sub
#End Region
#Region " 属性定义 "
Public ReadOnly Property RemoteIP() As String
Get
Try
Return CType(sckServer.RemoteEndPoint, IPEndPoint).Address.ToString
Catch
Return " "
End Try
End Get
End Property
Public ReadOnly Property ErrMessage() As String
Get
Return _ErrMessage
End Get
End Property
Public Property ReceiveTimeout() As Integer
Get
Return _ReceiveTimeout
End Get
Set(ByVal Value As Integer)
_ReceiveTimeout = Value
End Set
End Property
#End Region
#Region " New "
Public Sub New(ByVal ListenIP As String, ByVal ListenPort As Integer)
_LocalIp = ListenIP
_LocalPort = ListenPort
'开始侦听连接
StartListen()
End Sub
#End Region
#Region " 事件:ConnectionEvent "
Delegate Sub NotificationCallback(ByVal notify As sockNotificationEnum, ByVal sckClient As MSComm, ByVal SourceData As Object, ByVal sockHead As MSDefine.SockHead, ByVal sockBody As Object) ', ByVal sockPrefix As MSDefine.SockPrefix)
Public Event ConnectionEvent As NotificationCallback
#End Region
Private Sub StartListen()
ShutDownListener()
Try
sckListener = New TcpListener(IPAddress.Parse(_LocalIp), _LocalPort)
Dim thdLsn As New Threading.Thread(AddressOf StartListenBase)
thdLsn.Name = "侦听线程 "
thdLsn.Start()
Catch ex As Exception
ShutDownListener()
End Try
End Sub
Private Sub StartListenBase()
sckListener.Start()
Try
Do
Dim sckTmp As Socket = sckListener.AcceptSocket
ShutDownServer()
sckServer = sckTmp
sckTmp = Nothing
BeginReceive()
Loop Until _bKeepWorking = False
Catch e As Exception
If _bKeepWorking Then
ShutDownListener()
StartListen()
Else
ShutDownListener()
End If
End Try
End Sub
#Region " Close "
Public Overloads Sub Close()
EndReceive()
ShutDownListener()
ShutDownServer()
Dispose()
End Sub
Private Sub ShutDownListener()
If Not IsNothing(sckListener) Then
sckListener.Stop()
sckListener = Nothing
End If
End Sub
Private Sub ShutDownServer()
If Not (sckServer Is Nothing) Then
Try
sckServer.Shutdown(SocketShutdown.Both)
Catch
Finally
sckServer.Close()
sckServer = Nothing
End Try
_ServerState = ConnectionStateEnum.Closed
End If
End Sub
#End Region
#Region " EstablishSocket "
Private Sub EstablishSocket(ByVal state As Object)
Try
If Not IsNothing(sckServer) Then
ReceiveAsync()
If _bKeepWorking Then
_ErrMessage = String.Empty
RaiseEvents(sockNotificationEnum.RemoteClose, Nothing, Nothing, Nothing)
End If
Else
ShutDownServer()
_ErrMessage = String.Empty
RaiseEvents(sockNotificationEnum.ConnectionError, Nothing, Nothing, Nothing)
End If
Catch e As IOException
Dim sockExp As SocketException = CType(e.InnerException, SocketException)
If Not (sockExp Is Nothing) And sockExp.ErrorCode = 10054 Then
_ErrMessage = String.Empty
RaiseEvents(sockNotificationEnum.RemoteClose, Nothing, Nothing, Nothing)
Else
_ErrMessage = sockExp.Message
RaiseEvents(sockNotificationEnum.ConnectionError, Nothing, Nothing, Nothing)
ShutDownServer()
End If
sockExp = Nothing
Catch e As Exception
_ErrMessage = e.Message
RaiseEvents(sockNotificationEnum.ConnectionError, Nothing, Nothing, Nothing)
ShutDownServer()
End Try
End Sub
#End Region
#Region " BeginReceive,EndReceive "
Private Sub BeginReceive()
_bKeepWorking = True
System.Threading.ThreadPool.QueueUserWorkItem(New System.Threading.WaitCallback(AddressOf EstablishSocket))
End Sub
Private Sub EndReceive()
_bKeepWorking = False
End Sub
Private Sub ReceiveAsync()
Dim _bufferRead(CCommunicationBufferSize - 1) As Byte
Dim _RcvBytes As Integer = 0
Dim readStream As New NetworkStream(sckServer)
Dim strSocket As String = " "
Dim intEnd As Integer = 0
sckServer.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 0)
Do While _bKeepWorking
_bufferRead.Clear(_bufferRead, 0, CCommunicationBufferSize - 1)
_RcvBytes = readStream.Read(_bufferRead, 0, CCommunicationBufferSize)
If _RcvBytes > 0 Then
End If
Loop
intEnd = Nothing
_bufferRead = Nothing
_RcvBytes = Nothing
strSocket = Nothing
readStream = Nothing
End Sub
Private Overloads Sub RaiseEvents(ByVal EventType As sockNotificationEnum, ByVal tHead As MSDefine.SockHead, ByVal tBody As Object, ByVal tPrefix As MSDefine.SockPrefix)
RaiseEvent ConnectionEvent(EventType, Me, " ", tHead, tBody)
End Sub
Private Overloads Sub RaiseEvents(ByVal EventType As sockNotificationEnum, ByVal SourceData As String)
RaiseEvent ConnectionEvent(EventType, Me, SourceData, Nothing, Nothing)
End Sub
Private Sub ThrowMsg(ByVal strMsg As String)
RaiseEvent ConnectionEvent(sockNotificationEnum.ShowMsg, Me, "[MidSocket] " & strMsg, Nothing, Nothing)
End Sub
#End Region
End Class
[解决办法]
你给你的socket设置一个状态标记
你在调用委托之前判断一下socket的状态,就可以避免