TCP客户端未收到所有消息

时间:2015-08-28 12:09:58

标签: vb.net sockets tcp

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

    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

感谢

大卫

1 个答案:

答案 0 :(得分:0)

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

但是,我认为你在这种情况下要求有关调试的指导。所以我会尝试回答这个问题。

您需要知道此对话双方发生了什么。要做到这一点,你需要使用像Wireshark这样的工具。下载Wireshark并学习使用它。

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

因此,如果数据被分成几个数据包,则需要继续读取,直到您拥有完整的有效负载。这可能需要在流中查找边界标记,如果有可能在同一连接上接收更多有效负载,则需要以允许累积其余有效内容的方式保留下一个有效负载的开始。 / p>

如果你这样做,你可能会看到一个包含部分有效负载的完整数据包,正如我在写作时发布的评论中的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.