2017-05-12 7 views
0

사용자 지정 TCP/IP 클라이언트/서버 응용 프로그램을 만드는 중이며 서버를 중지하려고 할 때 방해가됩니다. 원래, 내 코드는 지정된 포트에서 수신 한 TcpListener를 사용하고, 내 (편의를 위해 간체) 코드는 시작하고 다음과 같이 서버가 갔다 중지 :TCP 수신기가 닫힌 경우 감지 문제

private bool state; 
    private TcpListener listener; 
    private CancellationTokenSource tokenSource; 
    private Dictionary<string, ConnectedClient> clients; 
    private List<Task> clientTasks; 
    private ConnectedClient command_client; 

    public async Task RunServer() { 
     if (!state) { 

      state = true; 

      tokenSource = new CancellationTokenSource(); 
      listener = new TcpListener(IPAddress.Any, 55001); 
      listener.Start(); 

      while (true) { 
       try { 
        TcpClient socketClient = await listener.AcceptTcpClientAsync(); 
        ConnectedClient client = new ConnectedClient(socketClient); 
        clients.Add(client.id, client); 
        client.task = ProcessClientAsync(client, tokenSource.Token); 
        clientTasks.Add(client.task); 


       } 
       catch (ObjectDisposedException) { 
        //Server stopped by user 
        //exit while 
        break; 
       } 
      } 

      /* Server has been stopped; close all connections */ 
      CloseAll(); 
     } 
     else { 
      /* Stop the server */ 
      tokenSource.Cancel(); 
      listener.Stop(); 
      /* Clean up of currently connected clients is handled in CloseAll, handled upon ObjectDisposedException above */ 
     } 
    } 

ConnectedClient 내가에 대한 몇 가지 정보를 개최 쓴 클래스입니다 개별 클라이언트를 편리하게 사용할 수 있으며 데이터 수신시 발생하는 상황을 처리하는 기능을 제공합니다. 간결하게하기 위해 몇 가지 사항을 남겨 두었습니다.하지만이 코드는 내가 원하는 것을 정확히 수행합니다. 서버는 연결을 기다리고, 수신 된 연결을 처리하기 위해 ConnectedClient 객체를 생성하고, 대기 상태로 돌아갑니다. 서버가 이미 청취 중일 때이 함수가 호출되면 리스너가 중지되어 리스너가 예외를 throw하여 루프를 중단하고 모든 연결을 닫습니다.

두 개의 서로 다른 포트에서 수신 대기하는 서버를 만들려고 시도 할 때 발생합니다. 다르게 처리해야하는 포트입니다. 여기

은 (는 시도)에 대한 내 코드 즉 :

private bool state; 
    private Dictionary<string, ConnectedClient> clients; 
    private TcpListener command_listener; 
    private TcpListener query_listener; 
    private CancellationTokenSource tokenSource; 
    private List<Task> clientTasks; 

    public async Task RunServer() { 
     if (!state) { 

      state = true; 

      tokenSource = new CancellationTokenSource(); 
      command_listener = new TcpListener(IPAddress.Any, 55001); 
      command_listener.Start(); 
      query_listener = new TcpListener(IPAddress.Any, 55002); 
      query_listener.Start(); 

      Task prevCommand = null; 
      Task prevQuery = null; 
      while (true) { 
       try { 

        if (prevCommand == null || prevCommand.IsCompleted) { 
         prevCommand = waitForConnections(command_listener); 
        } 

        if (prevQuery == null || prevQuery.IsCompleted) { 
         prevQuery = waitForConnections(query_listener); 
        } 
        await Task.WhenAny(prevCommand, prevQuery); 
       } 
       catch (ObjectDisposedException) { 
        //Server stopped by user 
        //exit while 
        break; 
       } 
      } 

      /* Server has been stopped; close all connections */ 
      CloseAll(); 
     } 
     else { 
      /* Stop the server */ 
      tokenSource.Cancel(); 
      command_listener.Stop(); 
      query_listener.Stop(); 
      /* Clean up of currently connected clients is handled in CloseAll, handled upon ObjectDisposedException above */ 
     } 
    } 

waitForConnections의 목적은 하나 개의 포트에 연결을 기다리는 것은 또한 다른 한편으로 연결을 차단하지 않도록 연결 요청을 처리하는 것입니다 하나의 연결 만이 함께 포트 55001.

public async Task waitForConnections(TcpListener listener) { 
     TcpClient socketClient = await listener.AcceptTcpClientAsync(); 

     if (((IPEndPoint)listener.LocalEndpoint).Port == 55001) { 
      if (command_client == null) { 
       command_client = new ConnectedClient(socketClient, onClientUpdate, onResend); 

       clients.Add(command_client.id, command_client); 
       command_client.task = ProcessClientAsync(command_client, tokenSource.Token); 
       clientTasks.Add(command_client.task); 
      } 
      else { 
       //only one client allowed on this port, reject the connection 
       socketClient.Close(); 
      } 
     } 
     else { 
      ConnectedClient client = new ConnectedClient(socketClient, onClientUpdate, onResend); 

      clients.Add(client.id, client); 
      client.task = ProcessClientAsync(client, tokenSource.Token); 
      clientTasks.Add(client.task); 
     } 
    } 

에 할 수 있도록, 나는 차단, 그러나 다시이 함수를 호출하고 리스너를 중지하는 원인이하지 않는 것없이 두 개의 포트에 클라이언트를 연결할 수 있어요 ObjectDisposedException를 사용하여 예상대로 던져져서 전체 프로그램이 멈추고 아무것도하지 않게됩니다. 이것은 비동기 함수의 무책임한 사용으로 인한 것 같지만 어떻게 해결할 수 있습니까?

+0

ObjectDisposedExceptions을 강제 종료하여 어떤 형태의 연결 종료를 나타내는 의도 된 접근 방식이 완전히 잘못되었습니다. 어쨌든, TcpListener 객체를 멈추더라도 그 객체는 처리되지 않습니다. – elgonzo

+0

공정하게, 예외 상황을 추적하는 것은 내가 생각하기에 나쁜 습관입니다. 그리고 처분이 끝날 때까지 나는 그 질문과 관련이 있다고 생각하지 않았기 때문에 간결하게 그 것을 버렸다. 내 진짜 질문은, 왜, 예외는 첫 번째 경우에는 던져 지겠지만, 두 번째에는 던지지 않는 이유일까요? – Cobalt

+0

죄송합니다, 다시 나는 명확하지 않았습니다. 왜 내 마음을 읽을 수 없니? ;) 첫 번째 경우에는 TcpListener.Stop() 호출만으로 ObjectDisposedException을 던졌지만 두 번째 경우에는 그렇지 않습니다. 청취자를 중지시키지 않아도 청취자가 그것을 처분하지 않는다면, 왜 그것이 처음부터 효과가 있었 을까? – Cobalt

답변

0

대기중인 listener.AcceptTcpClientAsync() 호출은 루프 내부가 아닌 비동기 작업에 있기 때문에 수신기가 중지 될 때 발생하는 예외로 인해 작업이 "오류"상태로 반환됩니다. 예외가 잡히지 않기 때문에 루프가 계속되고 오류가 발생한 작업은 완료된 작업으로 간주되므로 중지 된 수신기에도 불구하고 연결을 수신 대기하는 것으로 돌아갑니다 (차례대로 오류가 다시 발생할 가능성이 높습니다) .

예외를 잡는 대신 오류가있는 작업을 확인하여이 문제를 해결할 수 있었지만 대신 루프를 끊는 서버를 중지하려고 할 때 플래그를 설정하여 프로그램이 의도 한대로 연결을 닫을 수 있도록했습니다.