2013-05-11 82 views
2

我有一個套接字連接,並且通過這個套接字發送數據。我連接的服務器會回覆我的數據的每一個正確的發送。我收到了該消息,因此我收到了每封郵件的答案。有時候服務器喜歡保留一段消息幾秒鐘,或者按照不同的順序發送消息。我的解決方案是產生一個線程,讓它繞接收函數旋轉。但是,使用MSDN上的套接字示例,我有適合。他們使用簡單的do/while循環結構。當我這樣做時,我得到了混亂的回覆,和/或不完整的數據。這是爲了做家庭作業,所以我必須手工編寫客戶端,而不是僅僅使用更簡單的解決方案。這個代碼可能有問題嗎?我在它的眼前這麼久,我覺得我失去了一些東西簡單:C#套接字接收線程

private static void ReceiveThread(Socket sock, ReceiverClass rc) 
{ 
    // Create a socket and pass in parameter converted from object socket 
    int receivedBytes = 0; 

    do 
    { 
     // receive data from socket 
     receivedBytes = sock.Receive(rc.buffer); 
     byte[] formattedMsg = new byte[receivedBytes]; 
     Array.Copy(rc.buffer, formattedMsg, receivedBytes); 
     rc.sb.Append("<LF><CR>" + System.Text.Encoding.ASCII.GetString(formattedMsg) + "\r\n"); 
    } 
    while (receivedBytes > 0); 
} 

編輯,並稱滋生接收線程功能。 (這是太長時間,但我打算使它敢時,我得到了愚蠢的事情工作):

public void SendData(Socket sock) 
    { 
     // Set socket timeout 
     sock.ReceiveTimeout = 4000; 

     // Prepare file for IO operations 
     string path = @"c:\Logs\Lab2.Scenario3.WurdingerO.txt"; 
     StreamWriter logWrite = File.AppendText(path); 

     // Get local ip address: 
     IPAddress ip = Dns.GetHostAddresses(Dns.GetHostName()).Where(address => address.AddressFamily == AddressFamily.InterNetwork).First(); 
     string portNum = ((IPEndPoint)sock.LocalEndPoint).Port.ToString(); 

     // response time for scenario 2 and 3 
     int responseTime = 0; 

     // Set up Stopwatch to keep track of time 
     Stopwatch stpWatch = new Stopwatch(); 
     stpWatch.Start(); 

     // setup for logging class 
     ReceiverClass rc = new ReceiverClass(); 

     // setup receiving thread 
     Thread receiveThread = new Thread(delegate() 
      { 
       ReceiveThread(sock, rc); 
      }); 
     receiveThread.Start(); 

     // Counter to call client operations 
     for (int i = 0; i < MESSAGE_COUNT; i++) 
     { 
      string msTime = Convert.ToString(stpWatch.ElapsedMilliseconds); 
      if (msTime.Length > 10) 
      { 
       string newMSTime = ""; 

       for (int t = 9; t >= 0; t++) 
       { 
        newMSTime += msTime[t]; 
       } 
       msTime = newMSTime; 
      } 

      Classes.RequestBuilder reqB = new Classes.RequestBuilder(); 

      byte[] sendMsg; 

      switch (scenarioNo) 
      { 
       case 1: 
        sendMsg = reqB.MessageBuildScenarioOne(sock, msTime, 
         ip.ToString(), portNum, serverPort, serverIP, i); 
        break; 
       case 2: 
        // set up response time delay 
        switch (i) 
        { 
         case 1: 
          responseTime = 1000; 
          break; 
         case 3: 
          responseTime = 3000; 
          break; 
         default: 
          responseTime = 0; 
          break; 
        } 

        sendMsg = reqB.MessageBuildScenarioTwo(sock, msTime, 
         ip.ToString(), portNum, serverPort, serverIP, i, responseTime); 

        break; 
       case 3: 
        // set up response time delay 
        switch (i) 
        { 
         case 1: 
          responseTime = 1000; 
          break; 
         case 3: 
          responseTime = 3000; 
          break; 
         default: 
          responseTime = 0; 
          break; 
        } 

        sendMsg = reqB.MessageBuildScenarioThree(sock, msTime, 
         ip.ToString(), portNum, serverPort, serverIP, i, responseTime); 

        break; 
       default: 
        sendMsg = reqB.MessageBuildScenarioOne(sock, msTime, 
         ip.ToString(), portNum, serverPort, serverIP, i); 

        break; 
      } 

      try 
      { 
       sock.Send(sendMsg); 
      } 
      catch (Exception ex) 
      { 
       System.Windows.Forms.MessageBox.Show(ex.Message); 
      } 
     } 

     // Socket shutdown 
     sock.Shutdown(SocketShutdown.Send); 

     receiveThread.Join(); 

     sock.Shutdown(SocketShutdown.Receive); 

     string date = System.DateTime.Now.ToString("MMddyyyy"); 
     string time = System.DateTime.Now.ToString("HHmmss"); 

     logWrite.Write(rc.sb.ToString()); 
     logWrite.Write(date + "|" + time + "|0|0|"); 

     // Close log file 
     logWrite.Close(); 
     System.Windows.Forms.MessageBox.Show("Finished"); 
    } 

編輯: 我把睡眠計時器在發送操作後,並且固定我跑的問題成。 謝謝!

+0

構建它們時,套接字類型或協議是什麼? – Ribose 2013-05-11 22:21:05

+0

你的代碼似乎沒問題,但不知道代碼的其他部分,這很難回答。 – I4V 2013-05-11 22:42:30

+0

「我在發送操作之後放置了一個睡眠定時器,並解決了問題」可能不是固定的。睡眠掩蓋了這個問題,所以在99%的情況下它不會發生。它仍然會以1%的速度失敗,因爲它看起來像是比賽條件。 – usr 2013-05-12 10:30:16

回答

0

我想我有類似的問題。

我正在使用Socket類發送JSON字符串,並且突然JSON.NET會拋出異常。我檢查了字符串,並且看到正在處理2個JSON(根)對象。然後我拋出一個最小的例子,發現套接字將存儲*任何未讀數據(未讀=否.Receive已被調用,而.Receive將清除存儲)。

*隱藏:我不確定這是否是正確的術語。這對我有意義。

這是我的測試。如果你想運行它,創建一個新的測試類並安裝XUnit。

/// <summary> 
/// Testing Net Sockets.. 
/// </summary> 
/// <author>Jeff Hansen</author> 
public class SocketTests 
{ 
    /// <summary> 
    /// The local endpoint for the server. There's no place like 127.0.0.1 
    /// </summary> 
    private IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1337); 

    /// <summary> 
    /// Tests that a call to .Receive will return all unread data. 
    /// </summary> 
    /// <author>Jeff Hansen</author> 
    [Fact] 
    public void ReceiveWillReadAllUnreadData() 
    { 
     // We expect to receive double data. 
     const string expected = "HELLOHELLO"; 
     const string dataToSend = "HELLO"; 

     // Start the server in a background task. 
     // We do this because the receive call is blocking. 
     var bgTask = Task.Factory.StartNew(() => 
     { 
      var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      try 
      { 
       // Bind to the endpoint and start listening. 
       server.Bind(endpoint); 
       server.Listen(10); // 10 is max allowed connections in queue (I think) 

       // Client connected, receive data 
       var connectingClient = server.Accept(); 

       // So low-level we even get to allocate room for the data buffer manually. 
       var buf = new byte[1024]; 

       // Hangs until data flows. 
       var sz = connectingClient.Receive(buf); // This is the size of the data we just received. 
       var data = Encoding.ASCII.GetString(buf, 0, sz); // We use the size to only grab what we need from the buffer. 
       Console.WriteLine("Data received: {0}", data); 

       // This is going to pass because we sent the data twice, 
       // and the call to Receive would not be able to complete in time 
       // for it to clear before more data becomes available. 
       Assert.Equal(expected, data); 

       /* 
       * BAM! Theory proven. We seriously had issues 
       * because we didn't understand how it worked. This is why you usually end 
       * your transmission with a newline. 
       */ 

       Console.WriteLine("Server closing"); 
       server.Close(); 
      } 
      catch (Exception e) 
      { 
       // Make sure we close the server. 
       server.Close(); 
       throw; 
      } 
     }); 

     // Create a client socket and connect it to the server. 
     // The server thread should have started it up by now. 
     var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     client.Connect(endpoint); 

     // Get the bytes of our string 
     var buffer = Encoding.ASCII.GetBytes(dataToSend); 

     // Send it out twice. This happens faster than the server will process it, 
     // so the data is stacking up. You would THINK that .Receive would 
     // simply return the first data sent to it, and the next time .Receive is called 
     // return the next. But that's not how it works, apparently. 
     client.Send(buffer); 
     client.Send(buffer); 

     // Wait for the server to finish whatever it's doing. 
     try 
     { 
      // We give it 3000ms to complete. 
      bgTask.Wait(); 
     } 
     catch (AggregateException ag) 
     { 
      // Throw any esceptions that were thrown in the background thread. 
      ag.Handle(ex => { throw ex; }); 
     } 

     // Close the client socket. 
     client.Close(); 
    } 
} 

我們通過用換行符分隔數據解決了這個問題。有趣的事實:我在搞TcpClient,發現如果我沒有給流寫一個換行符,它不會發送任何東西 - 它只會掛在那裏。雖然可能是本地化的問題。任何人都在意詳細說明這一點?