多线程, 1服务器, 多客户端
可以有多个客户端连入服务器,服务器对所有客户端群发。
模拟实验 使用场景 :多个客户端申请服务器TCP连接, 服务器把自己的数据,比如压力,温度等发送给所有的客户端(比如工程师站,现场监控屏幕等)
服务器:
FORM代码
Imports System.Text
Public Class Form1
Private message As String
Private WithEvents modbusTcpSrv As ClsSocketTcpSvr
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
modbusTcpSrv = New ClsSocketTcpSvr
Me.Text = modbusTcpSrv.startSvr("5450")
End Sub
Private Sub modbusTcpSrv_来客啦(ip As String) Handles modbusTcpSrv.来客啦
Me.Invoke(显示到控件, ip)
End Sub
Private Sub modbusTcpSrv_客人问(data() As Byte, who As ClsClient) Handles modbusTcpSrv.客人说
who.SendMessage(data) '例子,收到了,直接发回去
message = Encoding.UTF8.GetString(data) ' Byte转为string
message = who.ip + ":" + who.port + " -> " + message
Me.Invoke(显示到控件, message)
End Sub
#Region "显示到控件"
Delegate Sub delegate显示到控件(message As String)
Private 显示到控件 As New delegate显示到控件(AddressOf sub显示到控件)
Private Sub sub显示到控件(message As String)
LabRec.Text = message
End Sub
#End Region
Private Sub Btn群发_Click(sender As Object, e As EventArgs) Handles Btn群发.Click
modbusTcpSrv.BroadcastMessage("r u ok?")
'仅是示范显示连接列表
ListBox1.Items.Clear()
For Each c As ClsClient In modbusTcpSrv.clientList
ListBox1.Items.Add(c.ip + ":" + c.port)
End Sub
Private Sub Form1_Closed(sender As Object, e As EventArgs) Handles Me.Closed
modbusTcpSrv.kill()
End Sub
End Class
ClsSocketTcpSvr类
Imports System.Net.Sockets
Imports System.Net
Imports System.Threading
Imports System.Text
Public Class ClsSocketTcpSvr
Dim sockedWatch As Socket
Public clientList As New List(Of ClsClient)
Dim threadWatch As Thread
Public Event 来客啦(addr As String)
Public Event 客人说(data() As Byte, client As ClsClient)
Public WithEvents client As ClsClient
Public Function startSvr(portNum As String)
Dim ret As String
'定义一个套接字用于监听客户端发来的信息 包含3个参数(IP4寻址协议,流式连接,TCP协议)
sockedWatch = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Dim IPAddress As IPAddress = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList(1) '(0)是外网地址
Dim EndPoint As IPEndPoint = New IPEndPoint(IPAddress, portNum) ''将IP地址和端口号绑定到网络节点endpoint上 'IPAddress.Parse("192.168.31.167")
sockedWatch.Bind(EndPoint) '监听绑定的网络节点
sockedWatch.Listen(20) '将套接字的监听队列长度限制为20
'创建一个监听线程
threadWatch = New Thread(AddressOf WatchConnecting)
threadWatch.IsBackground = True '将窗体线程设置为与后台同步
'启动线程
threadWatch.Start()
ret = "服务启动"
Catch
ret = "服务失败"
End Try
Return ret
End Function
''' <summary>
''' 监听客户端发来的连接请求
''' </summary>
Private Sub WatchConnecting()
While True ' 持续不断监听客户端发来的请求
Dim c As Socket = sockedWatch.Accept()
client = New ClsClient(c)
clientList.Add(client)
AddHandler client.来数据啦, AddressOf Sub来数据啦
RaiseEvent 来客啦(client.ip + ":" + client.port)
End While
End Sub
Private Sub Sub来数据啦(data() As Byte, who As ClsClient)
RaiseEvent 客人说(data, who)
End Sub
Public Sub BroadcastMessage(message As String) '这个函数用来广播接收到的客户端消息
Dim noConnectedList = New List(Of ClsClient) '用来存储已断开服务器连接的客户端
For Each client As ClsClient In clientList '遍历已经存储的客户端
If client.Connected Then ' 判断该客户端的状态
client.SendMessage(message) '发送想广播的内容
Else '把已断开连接的客户端放到数组中
noConnectedList.Add(client)
End If
For Each client As ClsClient In noConnectedList '从存储已连接客户端集合中移除掉已断开连接的客户端
clientList.Remove(client)
client.kill()
End Sub
Public Sub sendToClient(data() As Byte, which As ClsClient)
which.SendMessage(data)
End Sub
Public Sub kill()
On Error Resume Next
For Each client As ClsClient In clientList '遍历已经存储的客户端
client.kill() '停止内部线程
End Sub
End Class
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'每来一个客户端,就new一个新的ClsClient对象
'这个对象开启一个线程,收到消息了,发一个事件,通知上级
'提供发送消息的2个重载
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Class ClsClient
Public Event 来数据啦(data() As Byte, who As ClsClient)
Private clientSocket As Socket
Private t As Thread
Public ip As String = ""
Public port As String = ""
Private data(1024) As Byte
Public Sub New(socket As Socket)
clientSocket = socket
'获取客户端的IP和端口号
ip = CType(socket.RemoteEndPoint, IPEndPoint).Address.ToString
port = CType(socket.RemoteEndPoint, IPEndPoint).Port.ToString
t = New Thread(AddressOf ReceiveMessage) '开启线程执行循环接收消息
t.Start()
End Sub
Private Sub ReceiveMessage() '接收消息
On Error Resume Next
Dim length As Integer ' 初始化消息的长度
While (True) ' 循环接收消息
length = clientSocket.Receive(data) '获取存放消息数据数组的长度
If (length > 0) Then ' 判断是否有数组内是否存放消息数据
Dim dest(length - 1) As Byte
Buffer.BlockCopy(data, 0, dest, 0, length)
RaiseEvent 来数据啦(dest, Me)
length = 0 '
End If
End While
End Sub
Public Sub SendMessage(message As String) '发送消息
Dim data() As Byte = Encoding.UTF8.GetBytes(message)
clientSocket.Send(data)
End Sub
Public Sub SendMessage(data As Byte()) '发送消息
clientSocket.Send(data)
End Sub
Public ReadOnly Property Connected '获取该客户端的状态
Return clientSocket.Connected
End Get
End Property
Public Sub kill()
clientSocket.Close()
clientSocket.Dispose()
t.Abort()
End Sub
End Class
客户端是另一个程序
Imports System.Net
Imports System.Text
Public Class Form1
Dim WithEvents modbusTcpClient As New ClsSocketTcpClient
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim strIP As IPHostEntry = Dns.GetHostEntry(Dns.GetHostName())
Dim localIPStr As String = strIP.AddressList(1).ToString()
TxtLocIP.Text = localIPStr
Txt服务器地址.Text = localIPStr '赋初值,方便本机调试
End Sub
Private Sub Btn发送_Click(sender As Object, e As EventArgs) Handles Btn发送.Click
modbusTcpClient.SendMessage(TextBox1.Text)
End Sub
#Region "服务器来数啦"
Private Sub modbusTcpClient_服务器来数啦(data() As Byte) Handles modbusTcpClient.服务器来数啦
Me.Invoke(服务器来数啦, data)
End Sub
Delegate Sub delegate服务器来数啦(data() As Byte)
Private 服务器来数啦 As New delegate服务器来数啦(AddressOf sub服务器来数啦)
Private Sub sub服务器来数啦(data() As Byte)
Dim Message As String = Encoding.UTF8.GetString(data) 'Encoding.UTF8.GetString(data)
Label1.Text += Message
End Sub
#End Region
#Region "显示连上服务器了么"
Private Sub modbusTcpClient_连上服务器() Handles modbusTcpClient.连上服务器
Lab服务器连接状态.Text = "连上服务器"
End Sub
Private Sub modbusTcpClient_连上服务() Handles modbusTcpClient.连接失败
Lab服务器连接状态.Text = "连接失败"
End Sub
#End Region
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
modbusTcpClient.ConnectToServer(Txt服务器地址.Text, TxtPort.Text)
End Sub
Private Sub Form1_Closed(sender As Object, e As EventArgs) Handles Me.Closed
modbusTcpClient.turnOff()
End Sub
End Class
ClsSocketTcpClient类
Imports System.Net.Sockets
Imports System.Net
Imports System.Threading
Imports System.Text
Public Class ClsSocketTcpClient
Public Event 服务器来数啦(data() As Byte)
Public Event 连上服务器()
Public Event 连接失败()
Private t As Thread
Private WithEvents clientScoket As Socket
Public Sub ConnectToServer(ip As String, port As String)
Dim ret As String
clientScoket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
clientScoket.Connect(New IPEndPoint(IPAddress.Parse(ip), port)) '(New IPEndPoint(IPAddress.Parse("192.168.31.67"), "5450"))
t = New Thread(AddressOf ReceiveMessage) ' 开启线程执行循环接收消息
t.Start()
RaiseEvent 连上服务器()
Catch
RaiseEvent 连接失败()
End Try
End Sub
Sub ReceiveMessage() '接收消息
Dim length As Integer
On Error Resume Next
While (True)
If (clientScoket.Connected = True) Then
Dim data(1024) As Byte
length = clientScoket.Receive(data)
If (length > 0) Then 'Dim Message As String = Encoding.UTF8.GetString(data)
Dim dest(length - 1) As Byte
Buffer.BlockCopy(data, 0, dest, 0, length)
RaiseEvent 服务器来数啦(dest)
End If
End If
End While
End Sub
Public Sub SendMessage(message As String) '发送消息
On Error Resume Next
If clientScoket.Connected Then
Dim data() As Byte = Encoding.UTF8.GetBytes(message)
clientScoket.Send(data)
RaiseEvent 连接失败()
End If
End Sub
'Private sub a Handles clientScoket
Public Sub turnOff()
On Error Resume Next
If clientScoket IsNot Nothing Then
clientScoket.Close()
clientScoket.Dispose()
If t.IsAlive Then
t.Abort()
t.Join()
End If
End If
End Sub
End Class