C # повторение соединения через tcp-сокет - PullRequest
0 голосов
/ 26 сентября 2018

Я пытаюсь выполнить повторное кодирование, если мой клиент не может подключиться к моему серверу.Вот как я это делаю:

В основной функции:

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 подключений.

Что-то не так в моей кодировке?Похоже, что при каждой попытке мой сервер получит новое соединение.

1 Ответ

0 голосов
/ 26 сентября 2018

Без действующего примера это трудно понять.Если это близко к тому, что вы на самом деле делаете, я подозреваю, что пара вещей не так.Объект Socket, по-видимому, блокируется по умолчанию, но что-то генерирует ваше исключение, и это может быть не то, что вы думаете.Первое, что нужно сделать, это только перехватить SocketException, а затем только повторную попытку, когда исключение представляет что-то, что может указывать на то, что повторная попытка будет работать.Задержка, потому что если она не сработала 1 мс назад, то, вероятно, не сработает сейчас.Вставьте счетчик здравомыслия, чтобы он прекратил попытки после стольких попыток.Проверьте ваш протокол, чтобы убедиться, что вы отправляете серверу то, что он ожидает.Прежде всего, закройте ваши сокеты.

Я подозреваю, что вы видите набор соединений сокетов, вызванных исключением (которое может или не может быть связано с сокетами).Так как вы никогда не закрываете их, они просто накапливаются.Я подозреваю, что в конечном итоге GC может запустить и запустить финализаторы на объектах, которые затем потеряют свои соединения.Скорее всего, сервер потеряет связь.В любом случае, если вы явно не закрываете сокет, он будет зависать до тех пор, пока не произойдет тайм-аут.

Вот рабочий пример, который демонстрирует то, о чем я думаю, вы спрашиваете.Опять же, вам нужно решить, при каких условиях вы должны повторить попытку, потому что повторная попытка, если что-то пойдет не так, не годитсяЭто может привести к тому, что ваша программа будет постоянно чередовать потоки и, возможно, даже соединения.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...