【问题标题】:C# Doing retry on tcp socket communicationC#对tcp套接字通信进行重试
【发布时间】:2018-09-25 21:46:47
【问题描述】:

如果我的客户端无法连接到我的服务器,我正在尝试进行重试编码。以下是我的做法:

在主函数中:

Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);

连接回调:

private void ConnectCallback(IAsyncResult ar)
{
    try
    {
        // Retrieve the socket from the state object.  
        Socket client = (Socket)ar.AsyncState;

        // Complete the connection.  
        client.EndConnect(ar);
        _logger.Info("## Connection to server successful at " + strServerIP + ":" + strServerPort);
    }
    catch (Exception ex)
    {
        _logger.Info("## Connection to server failed. Retrying...");

        Socket client = (Socket)ar.AsyncState;
        IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(strServerIP), Convert.ToInt32(strServerPort));

        client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
    }
}

连接失败时我会在 ConnectCallback 中捕获异常并重试。

但我发现,如果它重试 10 次,当服务器启动时,服务器将从同一个客户端获得 10 个连接。如果重试 50 次,当服务器启动时,服务器将获得 50 个连接。

我的编码有问题吗?似乎每次重试时,我的服务器都会获得新的连接。

【问题讨论】:

  • 你为什么不尝试阻塞“连接”,然后重试???
  • 您是否看到任一日志消息。我怀疑连接成功,但您的 try 块抛出并进行另一个连接

标签: c# .net sockets asynchronous tcp


【解决方案1】:

如果没有有效的示例,就很难知道。如果这与您实际在做的事情接近,我怀疑有几件事是错误的。默认情况下, Socket 对象似乎是阻塞的,但有些东西正在生成您的异常,它可能不是您认为的那样。首先要做的是只捕获 SocketException,然后仅在异常表示可能表明重试可行的情况时重试。延迟,因为如果它在 1 毫秒前不起作用,它现在可能不会起作用。放入一个理智计数器,以便在多次尝试后放弃重试。检查您的协议以确保您正在向服务器发送它所期望的内容。最重要的是关闭你的套接字。

我怀疑您看到的是由异常引起的一堆 Socket 连接(可能与套接字相关,也可能不相关)。由于您从不关闭它们,因此它们只会累积。我怀疑最终 GC 可能会启动并在对象上运行终结器,然后会断开它们的连接。服务器更有可能断开连接。无论哪种方式,如果您没有明确关闭您的 Socket,那么它会一直挂起,直到出现超时。

这是一个工作示例,它演示了我认为您在问什么。同样,您需要决定在什么条件下应该重试,因为如果出现任何问题重试是不好的。它可能会导致您的程序不断搅动线程,甚至可能导致连接。

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Microsoft.Extensions.Logging;

namespace socketTst {
class Program {
    static ILoggerFactory loggerFactory = new LoggerFactory().AddConsole().AddDebug();
    static ILogger _logger;
    static AutoResetEvent finish = new AutoResetEvent(false);
    static String Hostname = "www.google.com";
    static int Port = 80;
    static int RetryCount = 0;

    static void ConnectCallback(IAsyncResult ar) {
        _logger.LogInformation($"## ConnectCallback entered");
        // Retrieve the socket from the state object.  
        Socket client = (Socket) ar.AsyncState;
        try {
            // Complete the connection.  
            client.EndConnect(ar);
            var s = new byte[] { 1 };
            client.Send(s);

            var buf = new byte[1024];
            var cnt = client.Receive(buf);

            _logger.LogInformation($"## Connection to server successful at {client.RemoteEndPoint}");
            if (cnt > 0) {
                var returned = Encoding.UTF8.GetString(buf, 0, cnt);
                _logger.LogInformation($"## Data returned: {returned}");
                }
            else {
                _logger.LogInformation($"## No data returned");
                }
            finish.Set(); // signal end of program
            }
        catch (SocketException sockExcep) {
            _logger.LogInformation($"## Exception: {sockExcep.Message}");
            _logger.LogInformation("## Connection to server failed. Retrying...");
            // This is a bad idea.  You don't know what is wrong so retrying might not be useful.
            // What if this is an unknown host or some other error that isn't likely to be
            // resolved by a retry ???
            RetryCount++;
            if (RetryCount > 10) {
                _logger.LogInformation("## Not able to reach host after 10 tries");
                finish.Set(); // signal end of program
                return; // give up
                }
            Thread.Sleep(797); // wait a bit
            var dest = new DnsEndPoint(Hostname, Port);
            client.BeginConnect(dest, new AsyncCallback(ConnectCallback), client);
            }
        catch (Exception ex) {
            _logger.LogInformation($"## Exception: {ex.Message}");
            }
        _logger.LogInformation($"## ConnectCallback exited");
        }

    static void Main(string[] args) {
        _logger = loggerFactory.CreateLogger<Program>();
        Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        client.Blocking = true;
        var dest = new DnsEndPoint(Hostname, Port);

        _logger.LogInformation($"Attempting connection to {dest.Host}:{dest.Port}");
        _logger.LogInformation($"Socket blocking: {client.Blocking}");

        _logger.LogInformation("Calling BeginConnect");
        var thd = client.BeginConnect(dest, new AsyncCallback(ConnectCallback), client);
        _logger.LogInformation("BeginConnect complete");

        _logger.LogInformation("Calling WaitOne");
        finish.WaitOne(); // don't let program end until connection is made
        _logger.LogInformation("WaitOne complete");

        client.Close();
        Thread.Sleep(25); // if you don't do this the program ends before all the log output can be written
        Console.WriteLine("Program complete");
        }
    }
}

我已经使用 .NET Core 2.1 测试了这段代码,你需要以下 nuget 包来运行它:

Microsoft.Extensions.Logging Microsoft.Extensions.Logging.Console Microsoft.Extensions.Logging.Debug"

成功执行如下:

信息:socketTst.Program[0] 尝试连接到 www.google.com:80 信息:socketTst.Program[0] 套接字阻塞:真 信息:socketTst.Program[0] 呼叫 BeginConnect 信息:socketTst.Program[0] 开始连接完成 信息:socketTst.Program[0] 呼叫 WaitOne 信息:socketTst.Program[0] ## ConnectCallback 输入 信息:socketTst.Program[0] ## 在 172.217.15.68:80 连接到服务器成功 信息:socketTst.Program[0] ## 返回的数据:HTTP/1.0 400 错误请求 内容长度:54 内容类型:文本/html;字符集=UTF-8 日期:格林威治标准时间 2018 年 9 月 26 日星期三 03:32:39 错误 400(错误请求)!!1 程序完成

【讨论】:

  • Dweeberly,我看到你在 main() 中执行了 client.close()。是你最后断开了服务器吗?但我需要继续始终连接到服务器。
  • Dweeberly,我看到你在 main() 中执行了 client.close()。是你最后断开了服务器吗?但我需要继续始终连接到服务器。因为服务器会随时向客户端发送数据
  • 代码当然是基于您最初的问题的示例。如果您只想打开一个连接,则必须关闭您可能创建的任何其他连接。当遇到任何异常时,您的 catch 将打开新连接,而不会关闭现有连接。如果您打算让套接字长时间打开,您可能想看看这个问题:stackoverflow.com/questions/158674/tcp-connection-life
猜你喜欢
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多