2015-08-28 98 views
1

我正在嘗試編寫一個小型客戶端,它將監聽某個端口上的服務器。當我運行我的代碼時,它似乎掛起,那麼它只會給我第一條消息,之後不再接收?我在這裏破壞了我的大腦。任何幫助讚賞。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 

感謝

大衛

+0

gSocket.Receive應該是一個循環。如果消息爲10k,則一次只能接收1k。簡而言之,一次調用該函數並不能保證收到完整的消息。此外,您的評論是錯誤的vMessageLength不是1025字節,它是1克。 vBuffer數組將會很大。 –

+0

感謝那:-)你的意思是一個while循環?對不起,但不是很有經驗...... – Brownd92

+0

是的,你需要循環,直到你收到所有的信息。這意味着您需要接收固定數量的字節或者擁有包含信息大小的數據包頭。 –

回答

0

如果我是你,我會改寫真相,正義的道貌岸然監護人和堆棧溢出的方式面臨的問題看它和向下投你顯得格格不入;沒有明確的問題。

但是,我認爲你在這種情況下要求指導調試。所以我會試着回答這個問題。

你需要知道這個對話雙方發生了什麼。要做到這一點,您需要使用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. 
+0

感謝您的幫助。非常感激。我只是不太確定爲什麼我無法從服務器獲取完整的消息。 – Brownd92

+0

@ Brownd92'SocketType.Stream' –

+0

不太確定你的意思是馬丁嗎? – Brownd92

相關問題