【问题标题】:TCP Client not receiving all the messagesTCP 客户端未收到所有消息
【发布时间】:2015-11-23 02:20:38
【问题描述】:

我正在尝试编写一个小型客户端,它将在某个端口上侦听服务器。当我运行我的代码时,它似乎挂起,那么它只会给我第一条消息,之后就不再收到了吗?我在这里破坏了我的大脑。任何帮助表示赞赏。

    Option Strict On

Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Public Class Form1

    'Form Controls.  
    Private lblPort As New Label
    Private txtPort As New TextBox
    Private lblIp As New Label
    Private txtIp As New TextBox
    Private lblSend As New Label
    Private txtSend As New TextBox
    Private WithEvents btnConnectSendReceive As New Button
    Private lblReceived As New Label
    Private lvwReceived As New ListView
    Private txtBoxrecieved As New RichTextBox

    'Global Objects.  
    Private gSocket As Socket = Nothing

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Setup form controls.  
        With Me
            .Text = "SplitPackManagerTest"
            .Controls.Add(lblPort)
            .Controls.Add(txtPort)
            .Controls.Add(lblIp)
            .Controls.Add(txtIp)
            .Controls.Add(lblSend)
            .Controls.Add(txtSend)
            .Controls.Add(btnConnectSendReceive)
            .Controls.Add(lblReceived)
            .Controls.Add(lvwReceived)
            .Controls.Add(txtBoxrecieved)
            .Height = 600
        End With
        With lblPort
            .Text = "Port:"
            .Location = New Point(12, 12)
            .AutoSize = True
        End With
        With txtPort
            .Text = "3001" 'Same port that server is listening on.  
            .Location = New Point(100, 12)
        End With
        With lblIp
            .Text = "IP:"
            .Location = New Point(12, 42)
            .AutoSize = True
        End With
        With txtIp
            .Text = "127.0.0.1" 'Loop-back IP address (localhost).  
            .Location = New Point(100, 42)
        End With
        With lblSend
            .Text = "Send:"
            .Location = New Point(12, 72)
            .AutoSize = True
        End With
        With txtSend
            .Text = Chr(2) & "(login (term-id 2))" & Chr(3)
            .Location = New Point(100, 72)
        End With
        With btnConnectSendReceive
            .Text = "Connect"
            .Location = New Point(12, 122)
            .Width = 260
        End With
        With lblReceived
            .Text = "Received Bytes:"
            .Location = New Point(12, 182)
            .AutoSize = True
        End With
        With lvwReceived
            .Height = 100
            .Dock = DockStyle.Bottom
            .View = View.Details
            .GridLines = True
            .FullRowSelect = True
            .MultiSelect = False
            .Scrollable = True
            .Columns.Add("Dec")
            .Columns.Add("Hex")
            .Columns.Add("Chr")
            For Each vCol As ColumnHeader In .Columns
                vCol.Width = CInt(Math.Floor(.Width / .Columns.Count)) - CInt(Math.Floor(30 / .Columns.Count))
            Next
        End With
        With txtBoxrecieved
            .Height = 200
            .Dock = DockStyle.Bottom
            .ScrollBars = RichTextBoxScrollBars.Both
        End With
    End Sub

    Private Function ConnectSendReceive(ByVal pSendData As Byte(), ByVal pIp As String, ByVal pPort As Integer) As Byte()

        'Try creating IP endpoint.  
        If String.IsNullOrEmpty(pIp) Then Return Nothing
        If Not IPAddress.TryParse(pIp, Nothing) Then Return Nothing
        Dim vIp As IPAddress = IPAddress.Parse(txtIp.Text)
        Dim vEndPoint As New IPEndPoint(vIp, CInt(txtPort.Text))

        'Timeout will be 0.5 seconds.  
        Dim vTimeout As Integer = 500000

        'For our little example, we expect all messages to be 1024 bytes or below (arbitrary amount).  
        Dim vMessageLength As Integer = 1002400000

        'Remember, when dimensioning arrays, the integer specified is the upper bounds, not the length.  
        Dim vServerResponse As Byte() = Nothing

        'Initiate socket.  
        Dim gSocket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

        'Connect.  
        Try
                gSocket.Connect(vEndPoint)

        Catch ex As Exception
            Return Nothing
        End Try
            If Not gSocket.Connected Then Return Nothing

        'Send.  
        'Socket.SendTimeout = vTimeout
        gSocket.Send(pSendData)
        txtBoxrecieved.AppendText("Sending.. " & txtSend.Text)


        'Receive response.  
        'Socket.ReceiveTimeout = vTimeout
        Dim vBuffer(vMessageLength - 1) As Byte
            Dim vNumOfBytesReceived As Integer = 0
            Try
                vNumOfBytesReceived = gSocket.Receive(vBuffer, 0, vMessageLength, SocketFlags.None)
            Catch ex As Exception
                Return Nothing
            End Try

            'Return received bytes.  
            ReDim vServerResponse(vNumOfBytesReceived - 1)
            Array.Copy(vBuffer, vServerResponse, vNumOfBytesReceived)

        'Disconnect (since we're using a "Using" statement, the socket will be disconnected here without us explicitly doing it).  


        Return vServerResponse
    End Function

    Private Sub btnConnectSendReceive_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnectSendReceive.Click

        'Send message and get response from server.  
        Dim vServerResponse As Byte() = ConnectSendReceive(Encoding.ASCII.GetBytes(txtSend.Text), txtIp.Text, CInt(txtPort.Text))

        'Did we receive a response?  
        If vServerResponse Is Nothing Then MessageBox.Show("Server not reachable / Received no response from server.") : Exit Sub

        'Do something with response.  
        For Each vByte As Byte In vServerResponse
            Dim vLvi As New ListViewItem
            With vLvi

                'Decimal column.  
                .Text = vByte.ToString

                'Hexidecimal column.  
                .SubItems.Add(vByte.ToString("X2"))

                'Character column.  
                .SubItems.Add(ChrW(vByte))
            End With
            With lvwReceived
                .Items.Add(vLvi)
                .EnsureVisible(.Items.Count - 1)
            End With

        Next
        txtBoxrecieved.AppendText(UnicodeBytesToString(vServerResponse))
    End Sub
    Private Function UnicodeBytesToString(
    ByVal bytes() As Byte) As String

        Return System.Text.Encoding.ASCII.GetString(bytes)
    End Function
End Class

谢谢

大卫

【问题讨论】:

  • gSocket.Receive 应该在一个循环中。如果消息是 10k,您一次只能收到 1k。简而言之,调用一次函数并不能保证收到完整的消息。另外,您的评论是错误的 vMessageLength 不是 1025 字节,而是 1g。 vBuffer 数组会很大。
  • 谢谢你:-) 你的意思是一个while循环吗?对不起,但不是很有经验...
  • 是的,您需要循环直到收到所有信息。这意味着您需要接收固定数量的字节或具有包含信息大小的数据包头。

标签: vb.net sockets tcp


【解决方案1】:

如果我是你,我会在真理、正义和堆栈溢出之道的圣洁守护者看到它之前重写这个问题,并否决你不合适的投票;没有明确的问题。

但是,我认为您是在寻求有关在这种情况下进行调试的指导。所以我会试着回答这个问题。

您需要了解对话双方的情况。为此,您需要使用 Wireshark 之类的工具进行监听。下载 Wireshark 并学习使用它。

我倾向于同意 the_lotus 的原因是您分配的非常大的缓冲区(忽略可能的大小错误)意味着您期望一个相当大的响应。数据包有效负载最高可达 64KB,但这并不常见,即使发生这种情况,协议也允许在途中进行分段。

因此,如果数据被拆分为多个数据包,则您需要继续阅读,直到获得完整的有效负载。这可能需要在流中查找边界标记,如果有可能通过同一连接接收更多有效负载,您需要以允许您累积剩余部分的方式保留下一个有效负载的开始。

如果你这样做,你可能会看到一个带有部分有效载荷的完整数据包,正如我还在写作时发表的评论中 the_lotus 所描述的那样。

我认为他可能是对的,但他也可能是错的,像 Wireshark 这样的工具可以让你找出答案,而不是盲目地猜测和破解你的代码来验证你的假设。

您使用的 I/O 样式称为轮询。有时有必要这样做,例如嵌入式系统通常不支持更复杂的东西,没有空间。但是在具有完整 .NET 框架的系统上,您确实应该使用 IOCP。对于单用户应用来说,这并不是绝对必要的,但如果你养成了正确的习惯,就不会产生可扩展性问题。

好的,至于为什么响应被截断,在检查了你的代码(VB 让我的眼睛受伤)之后,我几乎可以肯定 the_lotus 是正确的。您只从套接字读取一次,然后关闭它,而没有任何尝试检查您是否有完整的响应或任何尝试读取更多数据。

这看起来像是大学一年级的作业,所以大概你有某种规范。要确定您是否有完整的响应,您需要了解以下任一情况

  • 预期的有效载荷长度
  • 有效载荷中数据的位置和结构告诉您剩余的长度(或者有时是总数据包的大小)
  • 有效载荷结束标记,这将是一个特定的字节序列,保证不会以其他方式出现在有效载荷中。

一些伪代码

open the connection
while not CheckForCompletePayload(buffer)
  read some data and append it to your buffer 
close the connection.

【讨论】:

  • 感谢您的帮助。非常感激。我只是不太清楚为什么我无法从服务器获得完整的消息。
  • @Brownd92 'SocketType.Stream'
  • 不太清楚你的意思马丁?
猜你喜欢
  • 1970-01-01
  • 2013-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 2017-01-11
  • 2018-06-05
相关资源
最近更新 更多