2

我正在构建一个简单的 TCP 客户端和服务器作为我的网络项目的基础。我计划将异步等待技术用于未来证明和可扩展的服务器。

如果我输入错误的 IP 地址,客户端将无法连接到我的服务器并引发异常。我可以使用 try/catch 捕获异常,但这是推荐的方法吗?

各位看官怎么看实施。有什么意见可以让我改进吗?

我的服务器

    private void startServer_Click(object sender, RoutedEventArgs e)
    {
        if (anyIP.IsChecked == true)
        {
            listener = new TcpListener(IPAddress.Any, Int32.Parse(serverPort.Text));
            Logger.Info("Ip Address : " + IPAddress.Any + " Port : " + serverPort.Text);
        }
        else
        {
            listener = new TcpListener(IPAddress.Parse(serverIP.Text), Int32.Parse(serverPort.Text));
            Logger.Info("Ip Address : " + serverIP.Text + " Port : " + serverPort.Text);
        }
        try
        {
            listener.Start();
            Logger.Info("Listening");
            HandleConnectionAsync(listener, cts.Token);
        }
        //finally
        //{
            //cts.Cancel();
            //listener.Stop();
            //Logger.Info("Stop listening");
        //}

        //cts.Cancel();
    }

    async Task HandleConnectionAsync(TcpListener listener, CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            Logger.Info("Accepting client");
            //TcpClient client = await listener.AcceptTcpClientAsync();
            TcpClient client = await listener.AcceptTcpClientAsync();
            Logger.Info("Client accepted");
            EchoAsync(client, ct);
        }

    }

    async Task EchoAsync(TcpClient client, CancellationToken ct)
    {
        var buf = new byte[4096];
        var stream = client.GetStream();
        while (!ct.IsCancellationRequested)
        {
            var amountRead = await stream.ReadAsync(buf, 0, buf.Length, ct);
            Logger.Info("Receive " + stream.ToString());
            if (amountRead == 0) break; //end of stream.
            await stream.WriteAsync(buf, 0, amountRead, ct);
            Logger.Info("Echo to client");
        }
    }

    private void stopServer_Click(object sender, RoutedEventArgs e)
    {
        cts.Cancel();
        listener.Stop();
        Logger.Info("Stop listening");
    }

我的客户

    private void connect_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        IPAddress ipAddress;
        int port;

        //TODO Check if ip address is valid
        ipAddress = IPAddress.Parse(serverIP.Text);
        //TODO port range is 0-65000
        port = int.Parse(serverPort.Text);

        StartClient(ipAddress, port);
    }

    private static async void StartClient(IPAddress serverIpAddress, int port)
    {
        var client = new TcpClient();
        //can i try/catch to catch await exception?
        try
        {
            await client.ConnectAsync(serverIpAddress, port);
        }
        catch (Exception e)
        {
            Logger.Info(e);                
        }
        Logger.Info("Connected to server");
        using (var networkStream = client.GetStream())
        using (var writer = new StreamWriter(networkStream))
        using (var reader = new StreamReader(networkStream))
        {
            writer.AutoFlush = true;
            for (int i = 0; i < 10; i++)
            {
                Logger.Info("Writing to server");
                await writer.WriteLineAsync(DateTime.Now.ToLongDateString());
                Logger.Info("Reading from server");
                var dataFromServer = await reader.ReadLineAsync();
                if (!string.IsNullOrEmpty(dataFromServer))
                {
                    Logger.Info(dataFromServer);
                }

            }
        }
        if (client != null)
        {
            client.Close();
            Logger.Info("Connection closed");
        }

    }
4

1 回答 1

3

我有一个.NET TCP/IP 常见问题解答,我建议您了解一些基础知识。

在简单地看一下你的代码之后,这些点对我来说很突出:

  1. 您的客户端和服务器都有时间只在读取(而不是写入)。这意味着您会受到半开放情况的影响(正如我在常见问题解答中所描述的那样)。一个健壮的服务器应该定期写入,即使它无话可说。
  2. 您的客户端和服务器都有时间只写(不读)。这意味着如果另一端表现不佳(例如,发送大量数据) ,您将陷入僵局(正如我在我的常见问题解答中所描述的)。但是,您不能只是无限期地阅读,否则您会向 DoS 敞开心扉;所以你应该决定你的限制在哪里,并建立对你的应用程序有意义的读取缓冲区大小(和写入超时)。
  3. 使用ReadLineAsync会使您面临微不足道的 DoS 攻击,因为您无法指定允许的最大线路大小。
  4. 您的代码必须随时为异常做好准备(正如我在常见问题解答中所描述的那样)。显然,ReadAsync并且WriteAsync可能会抛出。不太明显的是任何套接字方法都可能抛出,包括AcceptTcpClientAsync.
  5. 您的代码混合使用了异常处理类型。永远不会等待这些async Task方法,因此那里的异常只是默默地结束该方法。该StartClient方法更有问题,因为它是async void. 您需要考虑应用程序对错误检测和重试策略的需求,并在每个级别应用适当的处理。

最后,我重申我的评论:我强烈建议只使用自托管 SignalR。只有在别无选择的情况下才应使用套接字。

于 2013-11-04T04:17:43.897 回答