TcpClient связь с сервером, чтобы сохранить соединение в C #? - PullRequest
4 голосов
/ 26 июня 2010

У меня есть этот код TcpClient, который отлично работает.Он подключается к серверу Perl в системе Linux и получает все, что сервер отправляет на него.Работает красиво.

 public static void Main() {
         foreach (ProtocolConnection tcpConnection in TcpConnectionsList) {
                ProtocolConnection connection = tcpConnection;
                ThreadPool.QueueUserWorkItem(_ => {
                                                 ThreadTcpClient(connection);
                                                 ManualResetEventTcp.Set();
                                             });
         }
         ... Some code...      
 }

 public static void TcpConnect(ProtocolConnection varConnection) {
        int retryCountSeconds = varConnection.RetryEverySeconds*Program.MilisecondsMultiplier;
        int count = 0;
        while (true) {

            try {
                using (var client = new TcpClient(varConnection.IpAddress.ToString(), varConnection.Port) { NoDelay = true })
                using (var stream = client.GetStream()) {
                    var data = new Byte[256];
                    while (!Program.PrepareExit) {
                        Int32 bytes = stream.Read(data, 0, data.Length);
                        string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim();
                        if (varReadData != "" && varReadData != "PONG") {
                            VerificationQueue.EnqueueData(varReadData);
                            Logging.AddToLog("[TCP][" + varConnection.Name + "][DATA ARRIVED]" + varReadData);
                        } else {
                            Logging.AddToLog("[TCP]" + varReadData);
                        }
                    }
                }
            } catch (Exception e) {
                if (e.ToString().Contains("No connection could be made because the target machine actively refused it")) {
                    Logging.AddToLog("[TCP][ERROR] Can't connect to server (" + varConnection.Name + ") " + varConnection.IpAddress + ":" + varConnection.Port );
                } else {
                    Logging.AddToLog(e.ToString());
                }

            }
            DateTime startTimeFunction = DateTime.Now;
            do {
                Thread.Sleep(1000);
            } while (((DateTime.Now - startTimeFunction).TotalSeconds < retryCountSeconds));
        }
    }

Однако в определенных условиях у меня возникают некоторые проблемы с ним:

  1. Мое рабочее соединение часто разрывает соединение после некоторого простоя, поэтому я внедрил его на сервере, когдаон получает PING, он отвечает PONG.Я могу отправить PING с UDP на сервер, и он ответит PONG по tcp, но я бы предпочел встроенный способ в клиент tcp, поэтому он отправляет PING каждые 60 секунд или около того.Даже если бы решение UDP было приемлемым, у меня нет тайм-аута на string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim();, поэтому, когда PONG не приходит, мой клиент все равно даже не замечает этого.Он просто продолжает ждать ... что подводит меня к ...
  2. Другая моя проблема в том, что в какой-то момент string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim(); это все время ждет данных.Когда сервер падает или отключает мой клиент, я даже не замечаю этого.Я бы хотел, чтобы на сервере был какой-то timeout или проверка, активно ли соединение.Если он не активен, он должен попытаться восстановить соединение.

Какой самый простой способ исправить этот TcpClient?Как мне реализовать двустороннюю связь, чтобы убедиться, что если сервер разорвет мои соединения или моя сеть отключится, клиент заметит это и восстановит соединение?

Ответы [ 2 ]

5 голосов
/ 26 июня 2010

Это не Encoding.ASCII.GetString(data, 0, bytes).Trim();, который блокирует навсегда, это stream.Read() Если вы читаете, вы не можете легко отличить сервер (или любой другой шлюз NAT), разрывающий ваше соединение, и тот случай, когда сервер простоне имеет ничего, чтобы отправить вас.По крайней мере, в случае, когда пакеты TCP FIN / RST не доходят до вашего клиента в случае сбоя, или если шлюз NAT молча прерывает ваше соединение.

Что вы можете сделать;

  • Установите Send / ReceiveTimeout и отправьте эхо-запрос на сервер, если истечет время ожидания, или внедрите собственные сообщения пульса через TCP-соединение.Восстановите или выполните другие действия, если вы не получили пульс в течение разумного времени.
  • Установите параметр TCP keepalive и положитесь на него, чтобы сообщить, что сервер пропал.Смотрите код здесь .

Последний пункт скажет вам, если соединение tcp не удалось, он не скажет вам, если сервер несколько отказал - например, если вы CTRL + Z ваш сервер Perl, он просто будет сидеть тамничего не делать, когда окно tcp закрывается, поэтому вам может потребоваться реализовать собственные сообщения теплового удара, чтобы покрыть и такой случай, если вам нужно.

2 голосов
/ 26 июня 2010

Вы должны избавиться от попытки сердцебиения UDP и ввести реальный пульс TCP . «Пинговать» сервер по протоколу UDP практически бессмысленно.

Ваш протокол также отсутствует фрейм сообщения .

Внимательно прочитайте обе эти связанные статьи (особенно сообщения). Протокол, который вы используете в настоящее время, нуждается в серьезной переработке.

...