2012-10-04 15 views
1

我正在通過TCP/IP傳遞序列化命令的項目上工作。當我在本地主機上工作時,它有工作,但是當我在不同的服務器上運行偵聽器時,它在試圖在偵聽器端反序列化命令時會失敗。 引發的異常包括:「試圖反序列化一個空流。」和「解析完成前遇到的流結束」。從串行器當不在本地主機上的TcpClient/TcpListener通信問題(多線程)

當我單獨運行一系列命令時,它工作正常,但是當我創建線程並同時運行多個序列時,它會失敗。

監聽器在4個不同的端口上創建偵聽器,客戶端爲每個端口運行1個線程。當其中一個線程到達序列的結尾時,它終止。

我試圖讓我的客戶單身人士,也嘗試互斥。但仍然是同樣的問題。

這裏是我的客戶:

public class TcpIpCommunicator : ICommunicator, IDisposable 
{ 
    private Dictionary<int,TcpClient> clientSockets = new Dictionary<int,TcpClient>(); 
    public IInverterCommand ReadAsyncCommand { set; get; } 
    private static TcpIpCommunicator tcpIpCommunicator; 

    private TcpIpCommunicator() 
    { 
    } 

    public static TcpIpCommunicator GetInstance() 
    { 
     if(tcpIpCommunicator == null) 
      tcpIpCommunicator = new TcpIpCommunicator(); 

     return tcpIpCommunicator; 
    } 

    public void Send(IInverterCommand command, int id) 
    { 
     var serializer = new Serializer(); 
     MemoryStream stream = serializer.SerializeMultipleObjects(command); 
     var _bytes = stream.GetBuffer(); 

     var networkStream = clientSockets[id].GetStream(); 
     networkStream.Write(_bytes, 0, _bytes.Length); 
     networkStream.Flush(); 
    } 

    public IInverterCommand Read(int id) 
    { 
     var memoryStream = new MemoryStream(); 

     byte[] buffer; 
     var networkStream = clientSockets[id].GetStream(); 
     do 
     { 
      buffer = new byte[clientSockets[id].ReceiveBufferSize]; 
      int sizeRead = networkStream.Read(buffer, 0, buffer.Length); 
      memoryStream.Write(buffer, 0, sizeRead); 
     } while (networkStream.DataAvailable); 

     networkStream.Flush(); 

     memoryStream.Position = 0; 
     var serializer = new Serializer(); 
     return serializer.DeSerializeMultipleObject(memoryStream); 

    } 

    public void ReadAsync(object id) 
    { 
     ReadAsyncCommand = Read((int)id); 
    } 

    public void Dispose() 
    { 
     foreach (var tcpClient in clientSockets.Values) 
     { 
      tcpClient.Close(); 
     } 
    } 

    public int Connect(string ip, int port) 
    { 
     var tcpClient = new TcpClient(); 
     tcpClient.ReceiveTimeout = int.MaxValue; 
     tcpClient.SendTimeout = int.MaxValue; 
     tcpClient.Connect(ip, port); 
     int key = findKey(); 
     clientSockets.Add(key, tcpClient); 

     return key; 
    } 

    public void DestroyConnection(int id) 
    { 
     clientSockets[id].Close(); 
     clientSockets.Remove(id); 
    } 

    private int findKey() 
    { 
     int key = 0; 
     while(clientSockets.ContainsKey(key)) 
     { 
      key++; 
     } 

     return key; 
    } 
} 

而且我的服務器端代碼是在這裏:

IInverterCommand command = new SoftwareUpdateInverterCommand(); 
     tcpClient.Send(command, tcpId); 

     var thread = new Thread(tcpClient.ReadAsync); 
     thread.Start(tcpId); 

     if (!thread.Join(timeout)) 
     { 
      thread.Abort(); 
      tcpClient.DestroyConnection(tcpId); 
      return; 
     } 

而且服務器:

public class TCPListener : IDisposable 
{ 
    private readonly TcpListener _serverSocket; 
    private NetworkStream _networkStream; 
    private readonly TcpClient _clientSocket = default(TcpClient); 

    public TCPListener(int port) 
    { 
     _serverSocket = new TcpListener(port); 
     _serverSocket.Server.ReceiveTimeout = int.MaxValue; 
     _serverSocket.Server.SendTimeout = int.MaxValue; 
     _serverSocket.Start(); 
     _clientSocket = _serverSocket.AcceptTcpClient(); 
    } 

    public void Send(IInverterCommand message) 
    { 
     _networkStream = _clientSocket.GetStream(); 
     var serialize = new Serializer(); 
     var stream = serialize.SerializeMultipleObjects(message); 
     var _bytes = stream.GetBuffer(); 

     if (_bytes.Length > _clientSocket.ReceiveBufferSize) 
     { 
      byte[] bytes = new byte[_clientSocket.ReceiveBufferSize]; 
      for (int i = 0; i < _bytes.Length; i += _clientSocket.ReceiveBufferSize) 
      { 
       for (int j = 0; j < _clientSocket.ReceiveBufferSize && i + j != _bytes.Length; ++j) 
       { 
        bytes[j] = _bytes[i + j]; 
       } 

       _networkStream.Write(bytes, 0, _clientSocket.ReceiveBufferSize); 
      } 
     } 
     else 
     { 
      _networkStream.Write(_bytes, 0, _bytes.Length); 
     } 

     Thread.Sleep(50); 

     _networkStream.Flush(); 
    } 

    public IInverterCommand ReadCommand() 
    { 
     _networkStream = _clientSocket.GetStream(); 
     var memoryStream = new MemoryStream(); 

     do 
     { 
      var buffer = new byte[_clientSocket.ReceiveBufferSize]; 
      int sizeRead = _networkStream.Read(buffer, 0, buffer.Length); 
      memoryStream.Write(buffer, 0, sizeRead); 
      Thread.Sleep(50); 
     } while (_networkStream.DataAvailable); 

     _networkStream.Flush(); 
     memoryStream.Position = 0; 
     var serializer = new Serializer(); 
     return serializer.DeSerializeMultipleObject(memoryStream); 
    } 

    public void Dispose() 
    { 
     _clientSocket.Close(); 
     _serverSocket.Stop(); 
    } 
} 

在這裏,在客戶端通常調用代碼側面呼叫代碼:

//Recieving CMD on software update 
      TcpListener = new TCPListener((int)port); 
      var command = TcpListener.ReadCommand(); 

      //Sending OK back to server 
      command.Message = "OK"; 
      TcpListener.Send(command); 
+0

您試圖通過創建單例解決線程問題? –

+0

你可以用不同的線程顯示調用代碼嗎? –

+0

如果您需要查看更多... – user1719689

回答

0

我建議你閱讀線程同步這個主題,以及它的含義和如何使用它。當你在不是線程安全的線程之間共享內容時會發生什麼。

在代碼中根本沒有線程同步,但是您至少從2個不同的線程讀取和寫入字典(不安全),而沒有任何同步。

單靠這個可能會導致很難追蹤的錯誤以及許多您不想處理的不同現象。

真的需要去閱讀如何正確執行線程。

相關問題