2017-10-17 93 views
0

後,我做的服務器請求第二次我的客戶端部分關閉,但沒有出現錯誤,它就消失了:C#異步套接字 - 代碼分析

class Client 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      Console.Title = "Client"; 
      AsyncClient client = new AsyncClient(60101); 
      client.Connect(); 
      Console.Read(); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
      Console.Read(); 
     } 
    } 
} 

public class AsyncClient 
{ 
    private IPAddress ipAddress; 
    private int port; 

    /// <summary> 
    /// Connects to the local IPAddress. 
    /// </summary> 
    /// <param name="port"></param> 
    public AsyncClient(int port) 
    { 
     this.port = port; 
     IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); 
     this.ipAddress = null; 
     for (int i = 0; i < ipHostInfo.AddressList.Length; i++) 
     { 
      if (ipHostInfo.AddressList[i].AddressFamily == AddressFamily.InterNetwork) 
      { 
       this.ipAddress = ipHostInfo.AddressList[i]; 
       break; 
      } 
     } 
     if (this.ipAddress == null) 
      throw new Exception("No IPv4 address has been found"); 
    } 

    public AsyncClient(string ip, int port) 
    { 
     this.port = port; 
     IPAddress.TryParse(ip, out ipAddress); 
    } 

    public async void Connect() 
    { 
     int attempts = 0; 
     TcpClient client = new TcpClient(); 
     while (!client.Connected) 
     { 
      try 
      { 
       attempts++; 
       client.Connect(this.ipAddress, this.port); 
       Console.Clear(); 
       Console.WriteLine("Connected"); 
       await Process(client); 
      } 
      catch (SocketException) 
      { 
       Console.Clear(); 
       Console.WriteLine("Connection Attempts: {0}", attempts); 
      } 
     } 
    } 

    public async Task Process(TcpClient tcpClient) 
    { 
     try 
     { 
      NetworkStream stream = tcpClient.GetStream(); 
      StreamWriter writer = new StreamWriter(stream); 
      StreamReader reader = new StreamReader(stream); 
      writer.AutoFlush = true; 
      while (true) 
      { 
       Console.WriteLine("Enter a Request: "); 
       await writer.WriteLineAsync(Console.ReadLine()); 
       string response = await reader.ReadLineAsync(); 
       if (response != null) 
        Console.WriteLine(response); 
       else 
        break; 
      } 
     } 
     catch (Exception) 
     { 
      // 
     } 
     finally 
     { 
      if (!tcpClient.Connected) 
      { 
       for (int i = 5; i >= 1; i--) 
       { 
        Console.WriteLine($"Connection lost, trying to reconnect in {i}"); 
        Thread.Sleep(1000); 
       } 
       Connect(); 
      } 
     } 
    } 
} 

這下是在服務器端代碼,這是僅用於研究目的。我試圖學習如何使用套接字,並嘗試了許多不同的方式,如「開始」方法等,我覺得我終於找到了正確的方式來做到這一點,因爲與其他人一樣,我遇到了併發訪問,關閉連接等,但這次我相信我說得對。 我錯了還是這次我的代碼真的很好?

class Server 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      Console.Title = "Server"; 
      AsyncServer server = new AsyncServer(60101); 
      server.Run(); 
      Console.Read(); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
      Console.Read(); 
     } 
    } 
} 

public class AsyncServer 
{ 
    private IPAddress ipAddress; 
    private int port; 

    public AsyncServer(int port) 
    { 
     this.port = port; 
     IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); 
     this.ipAddress = null; 
     for (int i = 0; i < ipHostInfo.AddressList.Length; i++) 
     { 
      if (ipHostInfo.AddressList[i].AddressFamily == AddressFamily.InterNetwork) 
      { 
       this.ipAddress = ipHostInfo.AddressList[i]; 
       break; 
      } 
     } 
     if (this.ipAddress == null) 
      throw new Exception("No IPv4 address for server"); 
    } 

    public async void Run() 
    { 
     TcpListener listener = new TcpListener(this.ipAddress, this.port); 
     listener.Start(); 
     Console.WriteLine($"Server is now online on Port: {this.port}"); 
     Console.WriteLine("Hit <Enter> to stop the service"); 
     while (true) 
     { 
      try 
      { 
       TcpClient tcpClient = await listener.AcceptTcpClientAsync(); 
       Process(tcpClient); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 
    } 

    private async void Process(TcpClient tcpClient) 
    { 
     string clientEndPoint = tcpClient.Client.RemoteEndPoint.ToString(); 
     Console.WriteLine($"Received connection request from {clientEndPoint}"); 
     try 
     { 
      NetworkStream networkStream = tcpClient.GetStream(); 
      StreamReader reader = new StreamReader(networkStream); 
      StreamWriter writer = new StreamWriter(networkStream); 
      writer.AutoFlush = true; 
      while (true) 
      { 
       string request = await reader.ReadLineAsync(); 
       if (request != null) 
        Handle(request, writer); 
       else 
        break; 
      } 
     } 
     catch (Exception) 
     { 
      // 
     } 
     finally 
     { 
      if (tcpClient.Connected) 
       tcpClient.Close(); 
      Console.WriteLine($"{clientEndPoint} has closed the connection, aborting operation"); 
     } 
    } 

    private string Response(string request) 
    { 
     Thread.Sleep(10000); 
     if (request.ToLower() == "get time") 
      return DateTime.Now.ToLongTimeString(); 
     else 
      return $"\"{request}\" is a invalid request"; 
    } 

    private async void Handle(string request, StreamWriter writer) 
    { 
     try 
     { 
      Console.WriteLine($"Received request: {request}"); 
      string response = Response(request); 
      Console.WriteLine($"Computed response is: {response}"); 
      await writer.WriteLineAsync(response); 
     } 
     catch (Exception) 
     { 
      // 
     } 
    } 
} 

另外,我想知道,如果我想讓它在我的外部IP的工作,所以從不同的IP脂肪酶可以使用它,我應該怎麼改?

回答

1

我做服務器的請求的第二 時間後,我的客戶端部分關閉,但沒有出現錯誤,它就消失了:

這樣做的原因是,你的客戶端調用異步方法client.Connect() ,但沒有(a)等待此方法,因此主線程上的執行繼續到下一行Console.Read(),該行僅在第二次按[ENTER](第一次[ENTER]被消耗通過Console.ReadLine()Process()方法中)。然後主線程沒有任何操作,主線程(以及整個客戶端應用程序)退出。

作爲一個方面說明,最好是命名所有異步方法,使其名稱以'Async'結尾,以便這種方法的調用者知道它是異步行爲並且不會忘記(a )等待該方法。因此,您應將Connect重命名爲ConnectAsyncProcessProcessAsync

解決辦法是改變返回類型Connect方法來Task,使得方法awaitable(它是強烈反對爲異步方法返回void反正):

public async Task ConnectAsync() 

並在Main方法,添加.Wait()這阻止主線程,直到ConnectAsync()退出。

client.ConnectAsync().Wait(); 

在C#7.1,你也可以使用異步主代替:

static async Task Main(string[] args) 
{ 
    ... 
    await client.ConnectAsync(); 
    ... 
} 

另外,我想知道,如果我想讓它在我的外部 IP工作,所以PPL來自不同的IP可以使用它,我應該改變什麼?

只要確保如果服務器有多個IP地址,在正確的TcpListener監聽,並啓用端口或應用防火牆。

+0

正式指出有關命名約定的提示。 我不知道我可以在任務上使用「.Wait」,這節省了我的生命,非常感謝。 關於使主方法異步,它不允許我,我不知道爲什麼,我的視覺工作室100%更新,但它仍然失敗,即使我改變任務爲虛空,它無法編譯,但沒有警告或錯誤信息。 我如何知道哪一個是正確的IP地址? –

+0

即使最新的Visual Studio默認使用C#7.0。您必須在項目屬性中手動啓用C#7.1 - 請參閱https://stackoverflow.com/a/46290692/1286670 –