什么是socket?socket翻译为套接字,是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。
socket抽象层在网络中的位置图解:
因此,只要知道如何使用相应的接口,就能够实现两个进程间的通信。
socket通信流程:
用代码方式实现
服务端:
'获得服务器ip地址和端口
Dim serverIpAdress As IPAddress = IPAddress.Parse(ipAddressText.Text)
Dim serverPort As Integer = Integer.Parse(portText.Text)
'将IP地址和端口号绑定到网络节点point上
Dim serverPoint As IPEndPoint = New IPEndPoint(serverIpAdress, serverPort)
'创建服务器socket
serverSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
'绑定节点
serverSocket.Bind(serverPoint)
'设置监听队列最大个数
serverSocket.Listen(10)
'创建监听线程
listener = New Thread(AddressOf Listen)
'将窗体线程设置为与后台同步,随着主线程结束而结束
listener.IsBackground = True
'启动线程
listener.Start()
Listen方法
'监听线程
Public Sub Listen()
'连接socket
Dim mySocket As Socket = Nothing
'不断监听接收请求连接
While True
Try
mySocket = serverSocket.Accept()
Catch ex As Exception
'提示套接字监听异常
BeginInvoke(New EventHandler(AddressOf AddInfo), ex.Message)
End Try
'可在此处做相关逻辑处理
'创建通信线程
Dim conThread As Thread = New Thread(AddressOf recv)
conThread.IsBackground = True
conThread.Start(mySocket)
End While
End Sub
recv方法
'通信线程
Public Sub recv(ByVal mySocket As Socket)
Dim conSocket As Socket = mySocket
'不断对话
While True
Try
Dim bytes() As Byte = New Byte(1024) {} '字符缓冲区
Dim data As String = String.Empty '字符串数据
'传来的字符大小
Dim bytesRec As Integer = conSocket.Receive(bytes)
'转化成人能看懂的字符
data = Encoding.UTF8.GetString(bytes, 0, bytesRec)
'再对接收到的信息进行相关逻辑处理……
End Try
End While
End Sub
客户端:
'定义服务端IP地址和端口
Dim ipAddresss As String = ipAddressText.Text
Dim port As String = portText.Text
Dim remoteEP As New IPEndPoint(Net.IPAddress.Parse(ipAddresss), Integer.Parse(port))
cliSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
'连接服务端
cliSocket.Connect(remoteEP)
'开启接收线程
myThread = New Thread(AddressOf ReciveMsg)
myThread.Start()
ReciveMsg方法
Sub ReciveMsg()
Dim bytes() As Byte = New Byte(1024) {}
Dim data As String = String.Empty
Dim tokens() As String
'不断循环接收服务器发来的信息
While True
Dim bytesRec As Integer = cliSocket.Receive(bytes)
data = Encoding.UTF8.GetString(bytes, 0, bytesRec)
'对接收到的信息进行处理……
End While
End Sub
编写了这些方法,客户端就能够与服务端通信,但是要做到多个客户端进行聊天,于是要增加一层服务端转发信息的逻辑。
假设三个客户端A,B,C, 一个服务端Sever。
A向服务端发送信息,Server收到后就需要向除去A的客户端转发信息,这样BC才能看到A发送的消息。
要进行转发,于是服务端就需要存储连接的客户端socket。
我是定义一个字典存储客户端socket
'创建集合存储连接的客户端信息
Dim connectionGroup As Dictionary(Of String, Socket) = New Dictionary(Of String, Socket)
在Listen方法里面监听到客户端的请求就将对应的socket放入字典
'向集合中加入该客户端
connectionGroup.Add(clientPoint, mySocket)
在recv方法里面进行转发,就是遍历该字典send就行了
For Each oneSocket As Socket In connectionGroup.Values
If oneSocket.RemoteEndPoint.ToString <> conSocket.RemoteEndPoint.ToString Then
oneSocket.Send(Encoding.UTF8.GetBytes("【客户端:" + conSocket.RemoteEndPoint.ToString + " 时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff") + "】: " + data))
End If
Next
如此一来,客户端A发送信息给server,其他连接的客户端就能够看到A发来的消息,简易的聊天室就基本实现了。
不过我们需要考虑到这样的情况,假如服务端挂了,客户端能否及时知道服务端已挂,为了保证通信连接正常,于是一般的做法是引入心跳检测机制。
心跳检测机制
简单来说就是客户端定时发送一句话给服务端:你挂没?
服务端收到客户端发来的这特定的问话就回个:我没挂!
于是双方就知道互相都没挂了。
服务器自动断开掉线客户端机制
在心跳检测机制上服务端会定时收到客户端的问话,那么假如客户端长时间没有发来问话,有可能客户端因为某些异常断开连接了,但是服务端没有收到断开连接的反馈,于是服务端这边就需要用一种机制定时检测客户端是否挂没挂。
我的做法是再引入一个字典,存储建立连接的客户端的时间,定义超时时间,如果客户端超过了这个超时时间还没发来信息,就将其释放掉
'创建集合存储连接的客户端通信时间
Dim conTimeGroup As Dictionary(Of String, DateTime) = New Dictionary(Of String, DateTime)
'定义超时时间
Dim lateTime As Integer = 40
'定义检查超时时间
Dim checkTime As Integer = 60000
'定时检查心跳包是否逾期
Private Sub heartIsLate(sender As Object, e As ElapsedEventArgs)
Dim saveTime As List(Of String) = New List(Of String)
Try
If conTimeGroup.Count > 0 Then
'遍历时间集合找到超时的socket,并且将之释放删除
For Each conTime As KeyValuePair(Of String, DateTime) In conTimeGroup
If DateDiff("s", conTime.Value, DateTime.Now) > lateTime Then
BeginInvoke(New EventHandler(AddressOf AddInfo), "客户端:" + conTime.Key + "已离线!")
Dim soc As Socket = connectionGroup.Item(conTime.Key)
soc.Close()
saveTime.Add(conTime.Key)
End If
Next
If saveTime.Count > 0 Then
For Each time As String In saveTime
conTimeGroup.Remove(time)
Next
End If
saveTime.Clear()
End If
Catch ex As Exception
BeginInvoke(New EventHandler(AddressOf AddInfo), ex.Message)
End Try
End Sub
另外为了区分客户端发来的是心跳包还是正常的聊天内容,我用分隔符的方式来进行定义
发送的内容如Chat|info
Dim bytes() As Byte = New Byte(1024) {} '字符缓冲区
Dim data As String = String.Empty '字符串数据
Dim tokens() As String '分割后的信息
'传来的字符大小
Dim bytesRec As Integer = conSocket.Receive(bytes)
'转化成人能看懂的字符
data = Encoding.UTF8.GetString(bytes, 0, bytesRec)
'进行分割
tokens = data.Trim.Split("|")
Select Case tokens(0) '分析接收到的数据
Case "Chat" '表示对话
Case "Exit" '表示客户端主动断线
Case "Heart" '表示心跳包
End Select
成品展示
这只是简单的通过socket实现下多个客户端的通信,讲述下相关实现逻辑,仅供各位参考!
源码下载地址



