为初级VB爱好者谋福利,关于TCP通讯实战,高手轻喷
vb.net吧
全部回复
仅看楼主
level 7
youki_xwy 楼主
最近为了项目在研究TCP通讯,由于本人经验有限,关于Socket的控制其实还是比较难的,这里发布一个自己编写的服务类,扩展性能还是很好的,需要的就拿走吧
首先是Listenre类,该类只是开启一个线程用于监听,资源释放通过Dispose辅助接口实现
''' <summary>
''' 该类用与Socket服务器基本工作
''' </summary>
''' <remarks></remarks>
Public Class Listener
Implements IDisposable
Private _listener As Socket
Private _thread As Thread
Private _ReceiveConnection As ReceiveConnectionDelegate
Private _ServicePort As Integer
Public Delegate Sub ReceiveConnectionDelegate(ByVal client As Socket)
Public Sub New(ByVal servicePort As Integer, ByVal receiveconnect As ReceiveConnectionDelegate)
_ServicePort = servicePort
_ReceiveConnection = receiveconnect
End Sub
''' <summary>
''' 开始服务监听工作
''' </summary>
''' <remarks></remarks>
Public Sub SpinUP()
_thread = New Thread(AddressOf ThreadEntryPoint)
_thread.IsBackground = True
_thread.Start()
End Sub
Protected Sub ThreadEntryPoint()
Dim ipendpoint As New IPEndPoint(Dns.GetHostEntry(Dns.GetHostName).AddressList(0), _ServicePort)
_listener = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
_listener.Bind(ipendpoint)
_listener.Listen(100)
Dim client As Socket
Do
Try
client = _listener.Accept
Catch ex As Exception
client = Nothing
End Try
If Not client Is Nothing Then
'通过委托把Client对象传递出去()
_ReceiveConnection.Invoke(client)
Else
Exit Do
End If
Loop
End Sub
Private disposedValue As Boolean = False ' 检测冗余的调用
' IDisposable
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: 释放其他状态(托管对象)。
End If
' TODO: 释放您自己的状态(非托管对象)。
' TODO: 将大型字段设置为 null。
If Not _listener Is Nothing Then
_listener.Close()
_listener = Nothing
End If If Not _thread Is Nothing Then
_thread.Join()
_thread = Nothing
End If
End If
Me.disposedValue = True
End Sub
#Region " IDisposable Support "
' Visual Basic 添加此代码是为了正确实现可处置模式。
Public Sub Dispose() Implements IDisposable.Dispose
' 不要更改此代码。请将清理代码放入上面的Dispose(ByVal disposing As Boolean) 中。
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
2013年06月02日 09点06分 1
level 7
youki_xwy 楼主

接下来这个类是重中之重,该类把一个连接好的Socket对象抽象,进行数据收发管理,实现的功能很多,可以自动收,自动发,范型模式,提供更广的兼容性质,该类收发信息主要通过将信息封状在一个可以序列化的类中进行,序列化的好处不用我说了吧
''' <summary>
''' 表示一个远程对象,用于管理连接好的Socket对象
''' </summary>
''' <remarks></remarks>
Public Class Clienter(Of T As {TransmitMessage, New})
Private _client As Socket
Private _clientType As ClientType
Private _clientStream As NetworkStream
Private _ReceiveThread As Thread
Private _TransmitThread As Thread
Private _threadState As Boolean
Private _thransmitStyle As TransmitStyle
Private _autoTransmitTimeControl As Integer
Private _transmitQueue As Queue(Of T)
Private _RWLock As ReaderWriterLock
Private _allReceivedMessage As List(Of T)
Private _needRecord As Boolean
Private _messageTempleList As List(Of T)
Private _recevedMessageShold As Integer
Private Const mEndMessage As String = "TheConnectWillBeClose"
Private Delegate Sub FinalizeDelegate(ByVal value As Boolean)
Private _FinalizeValue As Boolean = False
''' <summary>
''' 当模式中带Receive字样时候才会触发此事件,收到若干封装的序列化信息时候触发,若不及时处理信息将会在事件触发后丢失
''' </summary>
''' <param name="mList"></param>
''' <remarks></remarks>
Public Event ReceivedAnyMessage(ByVal mList As List(Of T))
Public Enum ClientType
ForServer = 0
ForClient = 1
End Enum
Public Enum TransmitStyle
ReceiveAndTransmitNormal = 1
ReceiveAndAutoTransmit = 2
TransmintNormalOnly = 4
TransmitAutoOnly = 8
End Enum
''' <summary>
''' 构造函数
''' </summary>
''' <param name="client">连接好的Socket对象</param>
''' <param name="type">用于服务器端或者用于客户端</param>
''' <param name="transmitstyle">传输模式</param>
''' <remarks></remarks>
Public Sub New(ByVal client As Socket, ByVal type As ClientType, ByVal transmitstyle As TransmitStyle)
_client = client
_clientType = type
_clientStream = New NetworkStream(_client)
_thransmitStyle = transmitstyle
If _thransmitStyle = Clienter(Of T).TransmitStyle.ReceiveAndAutoTransmit OrElse _thransmitStyle = Clienter(Of T).TransmitStyle.TransmitAutoOnly Then
_transmitQueue = New Queue(Of T)
_RWLock = New ReaderWriterLock
_autoTransmitTimeControl = 2000
End If
If _thransmitStyle = Clienter(Of T).TransmitStyle.ReceiveAndTransmitNormal OrElse _thransmitStyle = Clienter(Of T).TransmitStyle.ReceiveAndAutoTransmit Then
_recevedMessageShold = 1
_messageTempleList = New List(Of T)
_allReceivedMessage = New List(Of T)
End If
End Sub
''' <summary>
''' 当传送模式中带Receive的时候,事件ReceivedAnyMessage的触发频率控制,默认是1
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property RecevedMessageShold() As Integer
Set(ByVal value As Integer)
If _thransmitStyle = TransmitStyle.ReceiveAndTransmitNormal OrElse _thransmitStyle = TransmitStyle.ReceiveAndAutoTransmit Then
_recevedMessageShold = value
End If
End Set
End Property
''' <summary>
''' 当需要记录所有收到的信息时候,请先设置该属性
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property NeedRecord() As Boolean
Set(ByVal value As Boolean)
_needRecord = value
End Set
End Property
''' <summary>
''' 当选择自动收模式时候,设置完NeedRecord属性后才能返回所有收到信息的记录
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property AllMessage() As List(Of T)
Get
If _needRecord Then
Return _allReceivedMessage
Else
Return Nothing
End If
End Get
End Property
''' <summary>
''' 确定当前连接状态
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property LinkState() As Boolean
Get
If _thransmitStyle = TransmitStyle.TransmintNormalOnly Then
Return Connected()
Else
Return Not _threadState
End If
End Get
End Property
''' <summary>
''' 自动发送信息的时间间隔,毫秒为单位
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public WriteOnly Property TransmitTimeControl() As Integer
Set(ByVal value As Integer)
If Not _TransmitThread Is Nothing Then
_autoTransmitTimeControl = value
End If
End Set
End Property
''' <summary>
''' 如果资源已经释放,则返回True,该属性用于检测资源是否已经自动释放(目前没实现,可以通过Receive线程实现)
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property FinalizeState() As Boolean
Get
Return _FinalizeValue
End Get
End Property
''' <summary>
''' 开始工作,需要自动收或者自动发的时候请调用,否则调用无效
''' </summary>
''' <remarks></remarks>
Public Sub StartAutoWork()
If _thransmitStyle = TransmitStyle.ReceiveAndTransmitNormal OrElse _thransmitStyle = TransmitStyle.ReceiveAndAutoTransmit Then
_ReceiveThread = New Thread(AddressOf ReceiveEntyPoint)
_ReceiveThread.Start()
End If
If _thransmitStyle = TransmitStyle.ReceiveAndAutoTransmit OrElse _thransmitStyle = TransmitStyle.TransmitAutoOnly Then
_TransmitThread = New Thread(AddressOf TransmitEntryPoint)
_TransmitThread.Start()
End If
End Sub
''' <summary>
''' 发送EndMessage通知远程对象,即将关闭连接前请先调用
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function SendCloseMessage() As Boolean
Dim obj As New T
obj.Message = mEndMessage
If Serialize(_clientStream, obj) Then
_client.Shutdown(SocketShutdown.Send)
Return True
Else
Return False
End If
End Function
''' <summary>
''' 发送信息,返回True表示成功
''' </summary>
''' <param name="ms">信息</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function SendMessage(ByVal ms As T) As Boolean
If _thransmitStyle = TransmitStyle.ReceiveAndTransmitNormal OrElse _thransmitStyle = TransmitStyle.TransmintNormalOnly Then
Return Serialize(_clientStream, ms)
Else
SendMessageToTheQueue(ms)
Return True
End If
End Function
2013年06月02日 09点06分 2
level 7
youki_xwy 楼主
接上面的,代码太长了
''' <summary>
''' 通过线程池异步关闭并释放对象
''' </summary>
''' <param name="callBack">对象释放完成后回调的方法</param>
''' <param name="objState">传递到回调方法的参数</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function BeginClose(ByVal callBack As AsyncCallback, ByVal objState As Object) As IAsyncResult
_threadState = True
Dim finalizeD As New FinalizeDelegate(AddressOf MyFinalize)
Return finalizeD.BeginInvoke(_FinalizeValue, callBack, objState)
End Function
''' <summary>
''' 关闭并释放当前的对象
''' </summary>
''' <remarks></remarks>
Public Sub Close()
_threadState = True
MyFinalize(_FinalizeValue)
End Sub
''' <summary>
''' 自动发模式下通过集合来暂存封装后的信息对象
''' </summary>
''' <param name="ms"></param>
''' <remarks></remarks>
Protected Overridable Sub SendMessageToTheQueue(ByVal ms As T)
_RWLock.AcquireWriterLock(-1)
_transmitQueue.Enqueue(ms)
_RWLock.ReleaseWriterLock()
End Sub
''' <summary>
''' 自动发模式工作入口点
''' </summary>
''' <remarks></remarks>
Protected Overridable Sub TransmitEntryPoint()
Dim workQueue As New Queue(Of T)
While Not _threadState
Thread.Sleep(_autoTransmitTimeControl)
workQueue.Clear()
_RWLock.AcquireWriterLock(-1)
If _transmitQueue.Count > 0 Then
For Each ms As T In _transmitQueue
workQueue.Enqueue(ms)
Next
_transmitQueue.Clear()
End If
_RWLock.ReleaseWriterLock()
If workQueue.Count > 0 Then
For Each ms As T In workQueue
If Not Serialize(_clientStream, ms) Then
_threadState = True
Exit For
End If
Next
End If
End While
End Sub
''' <summary>
''' 自动收模式工作入口点
''' </summary>
''' <remarks></remarks>
Protected Overridable Sub ReceiveEntyPoint()
Dim ms As T
While Not _threadState
ms = Nothing
ms = DeSerialize(_clientStream) '这里会同步阻塞
If Not ms Is Nothing Then
If ms.Message = mEndMessage Then
_threadState = True
Else
_messageTempleList.Add(ms)
End If
If _needRecord Then
_allReceivedMessage.Add(ms)
End If
If _messageTempleList.Count >= _recevedMessageShold Then
RaiseEvent ReceivedAnyMessage(_messageTempleList)
_messageTempleList.Clear()
End If
Else
_threadState = True
End If
End While
End Sub
''' <summary>
''' 确定当前的连接状态,使用非阻止模式发送零字节的数据测试
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Protected Overridable Function Connected() As Boolean
Dim blockingState As Boolean = _client.Blocking
Dim result As Boolean
Try
Dim tmp(0) As Byte
_client.Blocking = False
_client.Send(tmp, 0, 0)
result = True
Catch e As SocketException
' 10035 == WSAEWOULDBLOCK
If e.NativeErrorCode.Equals(10035) Then
result = True
Else
result = False
End If
Finally
_client.Blocking = blockingState
End Try
Return result
End Function
''' <summary>
''' 该方法用与释放资源
''' </summary>
''' <remarks></remarks>
Protected Overridable Sub MyFinalize(ByVal finalizevalue As Boolean) If Not _FinalizeValue Then
If Not _client Is Nothing Then
_client.Close()
_clientStream.Close()
_client = Nothing
_clientStream = Nothing
End If
If Not _ReceiveThread Is Nothing Then
_ReceiveThread.Join()
_ReceiveThread = Nothing
_messageTempleList.Clear()
_messageTempleList = Nothing
End If
If Not _TransmitThread Is Nothing Then
_TransmitThread.Join()
_TransmitThread = Nothing
_RWLock = Nothing
_transmitQueue.Clear()
_transmitQueue = Nothing
End If
If Not _allReceivedMessage Is Nothing Then
_allReceivedMessage.Clear()
_allReceivedMessage = Nothing
End If
End If
_FinalizeValue = True
End Sub
''' <summary>
''' 将一个特定的对象序列化到特定的流中,成功时返回True
''' </summary>
''' <param name="stream">目标流</param>
''' <param name="obj">需要序列化的对象</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function Serialize(ByVal stream As Stream, ByVal obj As T) As Boolean
Dim formatter As New BinaryFormatter
If stream.CanWrite Then
Try
formatter.Serialize(stream, obj)
Return True
Catch ex As Exception
Return False
End Try
Else
Return False
End If
End Function
''' <summary>
''' 通过一个特定的流,尝试反序列化,返回Nothing表示失败
''' </summary>
''' <param name="stream">目标流</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function DeSerialize(ByVal stream As Stream) As T
Dim formatter As New BinaryFormatter
Dim obj As T
If stream.CanRead Then
Try
obj = CType(formatter.Deserialize(stream), T)
Return obj
Catch ex As Exception
Return Nothing
End Try
Else
Return Nothing
End If
End Function
2013年06月02日 09点06分 3
level 7
youki_xwy 楼主
继续接上面的
''' <summary>
''' 通过一个内存流来序列化一个对象为一个Byte数组
''' </summary>
''' <param name="obj">需要序列化的对象</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function Serialize(ByVal obj As T) As Byte()
Dim formatter As New BinaryFormatter()
Dim stream As New MemoryStream()
Dim messagebuff As Byte()
formatter.Serialize(stream, obj)
messagebuff = stream.ToArray
stream.Close()
Return messagebuff
End Function
''' <summary>
''' 通过一个序列化后的Byte数组建立内存流,将其反序列化
''' </summary>
''' <param name="data">通过序列化生成的Byte数组</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function DeSerialize(ByVal data As Byte()) As T
Dim formatter As New BinaryFormatter
Dim stream As New MemoryStream(data)
Dim obj As T
obj = CType(formatter.Deserialize(stream), T)
stream.Close()
Return obj
End Function
''' <summary>
''' 尝试获得一个远程主机连接好的Socket对象,返回Nothing表示失败
''' </summary>
''' <param name="ServicePort">服务器的端口号</param>
''' <param name="ServiceIP">服务器IP地址</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetConnectedSocket(ByVal ServicePort As Integer, ByVal ServiceIP As IPAddress) As Socket
Dim client As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Dim ipEndPoint As New IPEndPoint(ServiceIP, ServicePort)
Try
client.Connect(ipEndPoint)
Return client
Catch ex As Exception
Return Nothing
End Try
End Function
End Class
2013年06月02日 09点06分 4
level 7
youki_xwy 楼主
最后说明下序列化的相关知识,我们在网上传递信息,大部分人都觉得是字符或者字节数组,其实对于计算机任何对象都是一组二进制数据,所以虽然TCP的Socket对象没有实现收发对象的能力,我们可以用XML或者二进制的序列化的方法,把某个对象序列化成一个特定的元素到一个基础流中,再从该流中反序列化成特定的对象,这样的好处很多
首先,你不需要再为收法数据的分割线范畴了,不用担心很多数据过来不知道如何划分界限
其次,序列化和反序列化都可以在通信的双方任何一方关闭连接或者ShutDown后准确发生异常,但是如果你用通常的Read或者Receive方法,如果远程机器先用shutdown,便立刻返回0,不会出错,用Socket.Conneted属性去查看连接,那是非常坑爹的,这个根本不可靠
最后要注意,序列化的类的程序集和命令空间服务器端和客户端必须一致
上面网盘中有个十分简单的例子,但是能说明问题,使用Clienter类使代码工作量非常少,资源释放也不需要担心
2013年06月02日 09点06分 6
1