简体   繁体   English

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

[英]C# Doing retry on tcp socket communication

I'm trying to do a retry coding if my client fail to connect to my server. 如果客户端无法连接到服务器,我正在尝试重试编码。 Below is how i do: 以下是我的方法:

In main function: 在主要功能中:

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

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

ConnectCallback: 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);
    }
}

I will catch the exception in ConnectCallback when connection fail and do retry. 当连接失败并重试时,我将在ConnectCallback中捕获异常。

But I found out that, if it retried 10 times, when server is up, the server will get 10 connection from that same client. 但是我发现,如果重试10次,则在服务器启动时,该服务器将从同一客户端获得10个连接。 If it retry 50 times, when server is up, then server will get 50 connections. 如果重试50次,则服务器启动后,服务器将获得50个连接。

Is my coding something wrong? 我的编码有问题吗? Seems like everytime it retry, my server will get a new connection. 似乎每次重试时,我的服务器都会获得一个新的连接。

Without a functioning example it's difficult to know. 没有有效的示例,很难知道。 If this is close to what you are actually doing, I suspect a couple of things are wrong. 如果这与您的实际操作很接近,我怀疑有几件事是错误的。 The Socket object appears to be blocking by default, but something is generating your exception and it may not be what you think it is. 默认情况下,Socket对象似乎处于阻塞状态,但是某些东西正在生成您的异常,并且可能与您想像的不一样。 First thing to do is only catch the SocketException and then only retry when the exception represents something that might indicate a retry would work. 首先要做的只是捕获SocketException,然后仅在异常表示可能表明重试有效的情况下重试。 Put in a delay because if it didn't work 1 ms ago, it probably won't work now. 请延迟一下,因为如果1毫秒前它不起作用,则可能现在不起作用。 Put in a sanity counter so it gives up retrying after so many tries. 放置一个卫生状况计数器,这样它就可以放弃经过多次尝试的重试。 Examine your protocol to make sure you are sending the server what it expects. 检查您的协议,以确保您向服务器发送了期望的协议。 Above all Close your sockets. 首先,关闭插座。

What I suspect you are seeing is a bunch of Socket connections caused by an exception (which may or may not be related to the sockets). 我怀疑您看到的是一堆由异常引起的套接字连接(该异常可能与套接字相关,也可能与套接字无关)。 Since you never close them they just build up. 由于您从不关闭它们,它们只会堆积。 I suspect that eventually the GC might kick in and run finalizers on the objects which would then drop their connections. 我怀疑最终GC可能会启动并在对象上运行终结器,然后这些终结器将断开它们的连接。 More likely the server would drop the connections. 服务器更有可能断开连接。 Either way if you aren't explicitly closing your Socket then it will hang around until something times out. 无论哪种方式,如果您没有显式关闭Socket,它都会一直挂到发生超时为止。

Here is a working example that demonstrates what I think you are asking. 这是一个工作示例,演示了我认为您要问的问题。 Again you need to decide under what conditions you should retry because retrying if anything goes wrong is not good. 同样,您需要决定在什么条件下应该重试,因为如果出现任何问题,请重试是不好的。 It may cause your program to constantly churn threads and possibly even connections. 这可能会导致程序不断搅动线程,甚至可能搅动连接。

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");
        }
    }
}

I've tested this code using .NET Core 2.1 and you'll need the following nuget packages to run it: 我已经使用.NET Core 2.1测试了此代码,并且您需要以下nuget包来运行它:

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

Successful execution looks like this: 成功执行如下所示:

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