2010-08-17 2 views
2

클라이언트 응용 프로그램이 닫힐 때까지 클라이언트에서 서버로의 연결이 열려있는 클라이언트/서버 응용 프로그램에서 작업하고 있습니다.다른 쪽 끝이 닫힐 때 소켓이 등록되지 않음

클라이언트가 데이터를 읽는 동안 서버 응용 프로그램이 예기치 않게 다운 된 경우 클라이언트에서 예외로 처리하고 예외를 catch 한 다음 해당 예외를 인수로 발생 시키길 원합니다.

나는이 시스템이 작동하는지 테스트해야한다고 생각하는 테스트를 작성했지만, 테스트중인 객체는 중단 점을 넣은 다음 계속 진행하지 않으면 소켓이 닫혔다는 것을 등록하지 않는 것처럼 보입니다.

시험의 중요한 부분은 다음과 같다 다음 TcpListener 객체 listener가 루프백 주소를 듣고

StreamingMonitor sm = new StreamingMonitor(); 
bool errored = false; 
string msg = ""; 
sm.ErrorOccurred += (s, a) => 
{ 
    errored = true; 
    msg = a.Exception.Message; 
}; 
sm.Enabled = true; 
client = listener.AcceptTcpClient(); 
client.GetStream().Write(BitConverter.GetBytes(10000), 0, 4); 
client.Close(); 
while(!errored) 
{} 
Assert.AreEqual("A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call", msg); 

.

StreamingMonitor은 활성화 될 때 검색 할 데이터의 길이를 수신하기 시작합니다. 데이터의 길이는 항상 부호가있는 32 비트 정수에 맞다고 가정됩니다.

메시지 길이가 수신되면이 메서드가 호출됩니다.

private void GotMessageLength(IAsyncResult asyncResult) 
    { 
     try 
     { 
      client.Client.EndReceive(asyncResult); 
      if(firstMessage) 
      { 
       firstMessage = false; 
       if (Connected != null) 
       { 
        Connected(this, new EventArgs()); 
       } 
      } 
      int msgLen = BitConverter.ToInt32(messageLength, 0); 
      byte[] message = new byte[msgLen]; 
      List<byte> lbMessage = new List<byte>(); 
      int bytesReturned = client.Client.Receive(message); 
      int remaining = (msgLen < bytesReturned) ? bytesReturned - msgLen : msgLen - bytesReturned; 
      if(remaining > 0) 
      { 
       if (bytesReturned > 0) 
       { 
        for (int i = 0; i < bytesReturned; i++) 
        { 
         lbMessage.Add(message[i]); 
        } 
       } 
       while(remaining > 0) 
       { 
        if(!client.Connected) 
        { 
         throw new SocketException((int)SocketError.Shutdown); 
        } 
        bytesReturned = client.Client.Receive(message); 
        remaining = (remaining < bytesReturned) ? bytesReturned - remaining : remaining - bytesReturned; 
        if (bytesReturned > 0) 
        { 
         for (int i = 0; i < bytesReturned; i++) 
         { 
          lbMessage.Add(message[i]); 
         } 
        } 
       } 
       message = lbMessage.ToArray(); 
      } 
      MessageReceived(this, new MessageReceivedEventArgs(message)); 
      if (Enabled) 
      { 
       client.Client.BeginReceive(messageLength, 0, 4, SocketFlags.None, GotMessageLength, null); 
      } 
     } 
     catch (SocketException ex) 
     { 
      if(ErrorOccurred != null) 
      { 
       ErrorOccurred(this, new ErrorEventArgs(ex)); 
      } 
     } 
     catch (ObjectDisposedException) 
     { 

     } 
    } 

이 메서드는 지정된 바이트 수를 읽을 때까지 네트워크 스트림에서 데이터를 읽습니다. 원격 연결이 닫히면 소켓 예외가 발생합니다.

그러나 소켓이 StreamingMonitor에 없으므로 다른 쪽 끝이 닫혀 있지 않으므로 단위 테스트가 무한 루프에서 발생하여 오류가 발생하기를 기다립니다.

SteamingMonitor은 서버가 종료되었음을 알 수 있습니까?

루프백 주소에서 가능합니까?

모든 코드를 죄송합니다. 방법을 줄이는 방법을 생각할 수 없었습니다.

답변

3

도움이 될만한 부분에 대한 일반적인 지침을 드릴 수 있습니다.

일반적으로 루프백 (또는 로컬 호스트를 사용하는 것)은 실제 네트워크와 동일한 방식으로 작동하지 않습니다. 소켓 api에 대한 각각의 호출에서 얼마나 많은 데이터가 보내고 받는지와 같은 시나리오. 따라서 항상 실제 네트워크 연결을 테스트하십시오.

소켓 API는 상대방이 소켓에 연결하려고 시도 할 때 연결이 끊어져 있는지 확인합니다 (올바른 것 같습니다). 그래서 일종의 하트 비트 기능을 유용하게 쓸 수 있습니다.)

편집 : 수신을 시도하여 상대방의 연결이 끊어 졌는지 확인하기 위해 SocketException을 얻을 수도 있습니다 (예전 코드에 대한 기본 테스트를 수행함).

protected void ReceiveCallback(IAsyncResult ar) 
{ 
    var so = (StateObject)ar.AsyncState; 
    if (!so.Socket.Connected) return; 

    try 
    { 
     int read = so.Socket.EndReceive(ar); 
     if (read > 0) 
     ProcessBuffer(so, so.Buffer, read); 

     so.Socket.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, ReceiveCallback, so); 
     } 
     catch (SocketException e) 
     { 
      Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: SocketException"); 
      Output.WriteLine(e.Message); 
      RaiseDisconnected(); 
     } 
     catch (ObjectDisposedException e) 
     { 
      Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: ObjectDisposedException"); 
      Output.WriteLine(e.Message); 
      RaiseDisconnected(); 
     } 
} 

어떤 이유로 다른 쪽이 충돌하면 내 연결 해제 기능이 호출됩니다. 도움이 되었으면

+0

포인터를 제공해 주셔서 감사합니다. 루프백 이외의 다른 주소를 사용해 보겠습니다. –

+1

Markus가 정확합니다. [연결이 여전히 실행 가능한지 여부를 확인하려면 데이터를 보내야합니다.] (http://nitoprograms.blogspot.com/2009/05/detection-of-half-open-dropped.html). –

+0

우수한 링크. 고마워, 스티븐. 나는 하트 비트를 추가했으며 문제는 사라졌다. –

관련 문제