繁体   English   中英

C#在TCP套接字通信上重试

[英]C# Doing retry on tcp socket communication

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

在主要功能中:

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

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

ConnectCallback:

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个连接。

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

没有有效的示例,很难知道。 如果这与您的实际操作很接近,我怀疑有几件事是错误的。 默认情况下,Socket对象似乎处于阻塞状态,但是某些东西正在生成您的异常,并且可能与您想像的不一样。 首先要做的只是捕获SocketException,然后仅在异常表示可能表明重试有效的情况下重试。 请延迟一下,因为如果1毫秒前它不起作用,则可能现在不起作用。 放置一个卫生状况计数器,这样它就可以放弃经过多次尝试的重试。 检查您的协议,以确保您向服务器发送了期望的协议。 首先,关闭插座。

我怀疑您看到的是一堆由异常引起的套接字连接(该异常可能与套接字相关,也可能与套接字无关)。 由于您从不关闭它们,它们只会堆积。 我怀疑最终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"

成功执行如下所示:

info: socketTst.Program[0]
      Attempting connection to www.google.com:80
info: socketTst.Program[0]
      Socket blocking: True
info: socketTst.Program[0]
      Calling BeginConnect
info: socketTst.Program[0]
      BeginConnect complete
info: socketTst.Program[0]
      Calling WaitOne
info: socketTst.Program[0]
      ## ConnectCallback entered
info: socketTst.Program[0]
      ## Connection to server successful at 172.217.15.68:80
info: socketTst.Program[0]
      ## Data returned: HTTP/1.0 400 Bad Request
      Content-Length: 54
      Content-Type: text/html; charset=UTF-8
      Date: Wed, 26 Sep 2018 03:32:39 GMT

      <html><title>Error 400 (Bad Request)!!1</title></html>
Program complete

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM