簡體   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