2013-12-11 109 views
3

我正在嘗試創建一個相當簡單的客戶端服務器應用程序,但對於通信我想使用二進制序列化對象。通信本身似乎相當好,但是當我在客戶端關閉流時,服務器並沒有真正注意到它,並一直試圖讀取流。從關閉的NetworkStream中讀取不會導致任何異常

服務器側(類服務器,在單獨的線程中執行):

偵聽連接

listener = new TcpListener(IPAddress.Parse("127.0.0.1"), this.Port); 
listener.Start(); 
while (!interrupted) 
{ 
    Console.WriteLine("Waiting for client"); 
    TcpClient client = listener.AcceptTcpClient(); 
    AddClient(client); 
    Console.WriteLine("Client connected"); 
} 

添加客戶機:

public void AddClient(TcpClient socket) 
    { 
     Client client = new Client(this, socket); 
     this.clients.Add(client); 
     client.Start(); 
    } 

監聽消息(深Client類裏面) :

BinaryFormatter deserializer = new BinaryFormatter(); 
while (!interrupted) 
{ 
    System.Diagnostics.Debug.WriteLine("Waiting for the message..."); 
    AbstractMessage msg = (AbstractMessage)deserializer.Deserialize(stream); 
    System.Diagnostics.Debug.WriteLine("Message arrived: " + msg.GetType()); 
    raiseEvent(msg); 
} 

單元測試:

Server server = new Server(6666); 
server.Start(); 

Thread.Sleep(500); 

TcpClient client = new TcpClient("127.0.0.1", 6666); 
var message = new IntroductionMessage(); 
byte[] arr = message.Serialize(); 

client.GetStream().Write(arr, 0, arr.Length); 

Thread.Sleep(500); 

Assert.AreEqual(1, server.Clients.Count); 

client.GetStream().Close(); 
client.Close(); 

Thread.Sleep(1000); 

Assert.AreEqual(0, server.Clients.Count); 

server.Stop(); 

因此,該消息得到正確讀取,但後來,當我關閉流,deserializer.Deserialize(流)似乎並沒有拋出任何異常......所以它應該只是不以這種方式閱讀,還是應該以不同的方式關閉客戶?

+0

哪裏的服務器從獲取流?服務器對象內的TcpListener實例? – elgonzo

+0

現在,我更新了問題 –

回答

0

假設服務器在用於反序列化的消息流是NetworkStream(這是由TcpClient.GetStream()返回流的類型),你應該做兩件事情:

  1. 爲「連接結束」定義特定消息。當服務器接收並反序列化此消息時,請退出while循環。爲了做到這一點,客戶端顯然需要在關閉它的TcpClient連接之前發送這樣的消息。 (您可以選擇不同的機制以類似的方式工作 - 但爲什麼不使用你已經到位的消息機制......)

  2. 設置一個ReadTimeout的NetworkStream,所以萬一連接丟失,客戶端無法發送「連接結束」消息,服務器將達到超時並意識到客戶端「已死」。

用於監聽客戶端的消息應該類似於此服務器中的代碼:

// 
// Time-out after 1 minute after receiving last message 
// 
stream.ReadTimeOut = 60 * 1000; 

BinaryFormatter deserializer = new BinaryFormatter(); 

try 
{ 
    while (!interrupted) 
    { 
     System.Diagnostics.Debug.WriteLine("Waiting for the message..."); 
     AbstractMessage msg = (AbstractMessage)deserializer.Deserialize(stream); 
     System.Diagnostics.Debug.WriteLine("Message arrived: " + msg.GetType()); 

     // 
     // Exit while-loop when receiving a "Connection ends" message. 
     // Adapt this if condition to whatever is appropriate for 
     // your AbstractMessage type. 
     // 
     if (msg == ConnectionEndsMessage) break; 

     raiseEvent(msg); 
    } 
} 
catch (IOException ex) 
{ 
    ... handle timeout and other IOExceptions here... 
} 
+0

超時幫助:) –

+0

@Kamil:根據消息的原始大小和軟件使用的(網絡)環境,您將不得不選擇一個相當大的超時時間爲蹩腳的酒店互聯網連接,或者甚至是硬皮調制解調器連接,嗯...)。在我的例子中,我選擇了一分鐘的時間。如果您的程序只運行在局域網內(或低延遲的互聯網連接),則可以選擇少於30秒的超時。 – elgonzo

+0

在任何情況下,不要單獨依靠超時並實現專用的「連接終止」消息。如果問題不清楚問題是您的客戶端還是服務器中的問題,或者是否是因特網/網絡連接問題,那麼這將幫助您極大地解決問題。 – elgonzo

相關問題