我有一个使用TCP发送/接收消息的简单程序。由于某种原因,它收到第一条消息,但除非我关闭并重新连接客户端,否则服务器不会从该客户端收到第一条消息。我正在提供下面代码的精简版本(所以请原谅缺乏异常处理)。
Private TCPWriter As TcpClient
Private TCPListener As TcpListener
Private Sub Send(sender As Object, e As RoutedEventArgs)
Connect()
WriteTextToServer(MessageBox.Text)
End Sub
Private Sub Connect()
If TCPWriter Is Nothing Then
TCPWriter = New TcpClient
End If
If Not TCPWriter.Connected Then
TCPWriter.Connect(System.Net.IPAddress.Parse(IPBox.Text), PortBox.Text)
TCPWriter.NoDelay = True
End If
End Sub
Private Sub WriteTextToServer(inMsg As String)
Dim data() As Byte = Encoding.ASCII.GetBytes(inMsg + vbCrLf)
Dim ns As NetworkStream = TCPWriter.GetStream()
If ns.CanWrite Then
ns.Write(inMsg, 0, inMsg.Length - 1)
End If
End Sub
Private Sub ReadNewDataFromClient(ByVal inStatus As IAsyncResult)
Dim clientSocket As TcpClient
Dim datalen As Integer
Dim buf() As Byte
Dim message As String
clientSocket = CType(inStatus.AsyncState, TcpListener).EndAcceptTcpClient(inStatus)
clientSocket.ReceiveTimeout = 5000
datalen = clientSocket.Available
If datalen > 0 Then
'get all data at once ...
buf = New Byte(datalen - 1) {}
clientSocket.GetStream().Read(buf, 0, buf.Length)
message = Encoding.ASCII.GetString(buf, 0, buf.Count - 2)
Me.Dispatcher.Invoke(Sub() ReadNewDataFromClients.Add(ReadNewDataFromClients.Count.ToString + ": " + message))
End If
TCPListener.BeginAcceptTcpClient(New AsyncCallback(AddressOf ReadNewDataFromClient), Nothing)
End Sub
如果我将Connect()方法更改为以下代码,它将起作用,但似乎不是正确的方法。
Private Sub Connect()
If TCPWriter IsNot Nothing Then
TCPWriter.close
TCPWriter = nothing
Threading.Thread.Sleep(1000)
End If
TCPWriter = New TcpClient
If Not TCPWriter.Connected Then
TCPWriter.Connect(System.Net.IPAddress.Parse(IPBox.Text), PortBox.Text)
TCPWriter.NoDelay = True
End If
End Sub
我做错了什么?
答案 0 :(得分:0)
您正在使用BeginAcceptTcpClient
接收下一个“消息”,这用于接受新连接(这就是为什么当您断开/重新连接每条消息时它的工作原理)。您应该使用Read
/ BeginRead
来阅读下一条消息。
话虽如此,您还需要了解TCP是一个流协议,因此,它没有“消息”的概念,也许最重要的是,它之间没有可靠的对应关系。发送方对Write
的呼叫次数以及接收方读取所发送数据所需的Read
呼叫次数。
如果要将TCP用于离散消息,则需要发送数据,以便将其拆分回接收端的各个消息中。这通常是通过在消息前面加上固定大小的长度指示符(例如4个字节)来“成帧”消息来完成的。
您还需要确保观察Read
方法的返回值,该方法将指示实际读入缓冲区的字节数。