http://coffecoco.bokee.com/806222.html
制作一个网络通讯类(一)- -
简介
TcpListener类提供一些简单方法,用于在同步阻塞模式下侦听和接受传入连接请求。
TcpClient 类提供了一些简单的方法,用于在同步阻塞模式下通过网络来连接、发送和接收流数据。
为了使用方便,我利用.Net提供的这两个类作了一个网络通讯用的类CTcpTalk。
工作原理和使用方法
* 每个CTcpTalk对象中包含一个用于监听的TcpListener部件,一个用于传输数据的TcpClient部件,和一个用于接收连接请求的TcpClient部件。
* 在创建一个CTcpTalk时需要指定要使用的端口号。然后使用CTcpTalk.Open开启对网络的监听。
* 接收数据:当监听到有数据传送到本机时,使用接收连接请求的TcpClient部件接收对方的连接请求以及发送来的数据。接收完毕后关闭TcpClient部件,并触发DataArrival事件,可以使用GetData()函数获取收到的数据。
* 发送数据:设置接收方的名称和端口,使用传输数据的TcpClient部件请求连接,连接成功后发送数据。数据发送完毕之后关闭TcpClient部件,并触发SendComplete事件。
设计中的问题
* 由于TcpListener和TcpClient都是工作在同步阻塞模式下,因此数据传输和监听都使用了单独的线程。
* 对于TcpListener的监听线程,因为是阻塞的模式,所以在关闭监听时,需要先由本机向本机自己发一个连接请求,以解除监听线程的阻塞,然后通过相应量的设置,退出监听循环,关闭监听。在监听阻塞状态下直接关闭监听会导致错误,通过错误陷阱隐藏后,似乎也不会影响后面的使用。
* 使用流模式读取和发送数据,为了方便而采用了流的同步读写。
* 设计为发送方申请建立连接、发送接收完毕后立刻断开连接的模式。类似于点对点的模型,没有服务器客户端之分。参加通讯的机器只需要维持一个监听线程就可以了。而不必保留已连接列表并随时检查列表中各个项的连接状态。这也是因为采用了同步读写模式,如果阻塞流的读线程反而会大大降低性能。
* 对于传输数据量的大小,有8K字节的限制。由于使用了Unicode编码解码,所以实际的传输量测试为每次4K以下。可以通过外部编程对大数据量进行分页传输,但是在内部仍然是每次传输前建立连接、传输完毕后断开连接的方式。因此对于过大的数据需要消耗额外的资源用于频繁建立和断开连接。
* 因为可能要用于.Net Framework精简版,所以方法、事件和属性都考虑使用受精简版支持的版本。
测试程序界面(单机测试)
本界面为单机测试结果。此程序也可用于多机。
按钮加入网络
启动本机的网络监听。此按钮在已经启动监听后不可用
Name = BJoinNet
按钮退出网络
关闭本机的网络监听。关闭之后将无法再接收连接请求。此按钮在监听关闭时不可用
Name = BExitNet
按钮关闭程序
关闭程序
Name = BClose
按钮发送
发送文本框中的内容。在未加入网络时此按钮不可用。
Name = BSend
文本框发送的内容
Name = TBSend
MultiLine = True
ScrollBars = Vertical
文本框接收的内容
Name = TBRecv
MultiLine = True
ScrollBars = Vertical
ReadOnly = True
文本框状态监视
Name = TBState
MultiLine = True
ScrollBars = Vertical
ReadOnly = True
测试程序代码
组件声明
Private WithEvents sck1 As CTcpTalk
界面加载
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'获取本机名称和IP
Try
LLocalName.Text = Dns.GetHostName
LLocalIP.Text = Dns.Resolve(LLocalName.Text).AddressList(0).ToString
Catch ex As Exception
LLocalName.Text = "无法获得主机名"
LLocalIP.Text = "无法获得主机IP"
End Try
sck1 = New CTcpTalk
'重绘界面
SetUIDisconnect()
End Sub
界面关闭
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
If sck1 Is Nothing Then
Else
If sck1.State <> CTcpTalk.StateConstants.sckClosed Then
sck1.Close()
End If
End If
Application.Exit()
End Sub
按钮加入网络
Private Sub BJoinNet_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BJoinNet.Click
'检查端口号
If TBPort.Text = "" Then
MsgBox("请输入端口号")
Exit Sub
End If
Dim port As Long
Try
port = CLng(TBPort.Text)
Catch ex As Exception
MsgBox("端口号格式错误, 请重新设置")
Exit Sub
End Try
'开启监听
sck1 = New CTcpTalk(port)
sck1.Open()
'设置界面
SetUIListen()
End Sub
按钮退出网络
Private Sub BExitNet_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BExitNet.Click
'设置界面
SetUIWait()
AppendTxt(TBState, "正在退出网络...")
'关闭监听
sck1.Close()
'设置界面
SetUIDisconnect()
AppendTxt(TBState, "已经退出网络")
End Sub
按钮关闭程序
Private Sub BClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BClose.Click
If sck1.State <> CTcpTalk.StateConstants.sckClosed Then
'关闭监听
sck1.Close()
SetUIDisconnect()
End If
'退出程序
Application.Exit()
End Sub
按钮发送
Private Sub BSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BSend.Click
'检查参数
If TBRemote.Text = "" Then
MsgBox("请输入对方计算机名称或IP")
TBRemote.Focus()
Exit Sub
End If
If TBPort.Text = "" Then
MsgBox("请输入端口号")
Exit Sub
End If
Dim port As Long
Try
port = CLng(TBPort.Text)
Catch ex As Exception
MsgBox("端口号格式错误")
Exit Sub
End Try
'设置远程主机名称和端口
sck1.RemotePort = port
sck1.RemoteHost = TBRemote.Text
'发送数据
sck1.Send(TBSend.Text)
End Sub
sck1的DataArrival事件
Private Sub sck1_DataArrival(ByVal bytesTotal As Long) Handles sck1.DataArrival
AppendTxt(TBRecv, sck1.GetData)
End Sub
sck1的ErrorEvt事件
Private Sub sck1_ErrorEvt(ByVal ex As CTcpTalkException) Handles sck1.ErrorEvt
AppendTxt(TBState, ex.Message)
End Sub
sck1的Connect事件
Private Sub sck1_Connect() Handles sck1.Connect
AppendTxt(TBState, "Connected")
End Sub
sck1的SendComplete事件
Private Sub sck1_SendComplete() Handles sck1.SendComplete
AppendTxt(TBState, "Send Complete")
End Sub
设置界面(无监听状态)
Private Sub SetUIDisconnect()
BJoinNet.Enabled = True
BExitNet.Enabled = False
BSend.Enabled = False
End Sub
设置界面(监听状态)
Private Sub SetUIListen()
BJoinNet.Enabled = False
BExitNet.Enabled = True
BSend.Enabled = True
End Sub
设置界面(等待状态)
Private Sub SetUIWait()
BJoinNet.Enabled = False
BExitNet.Enabled = False
BSend.Enabled = False
End Sub
向指定文本框添加文本
Private Sub AppendTxt(ByVal tb As TextBox, ByVal txt As String)
tb.Text = tb.Text + txt + vbCrLf
End Sub