2017-10-17 92 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 –