Поддержание TCP-соединения живым - PullRequest
0 голосов
/ 11 апреля 2019

Это, вероятно, глупый вопрос, но выслушайте меня.

На моем удаленном сервере я запускаю программу со следующим кодом:

private const int BufferSize = 1024;
private static readonly byte[] BytesBeingReceived = new byte[BufferSize];

static void Main(string[] args)
{
    TcpListener tcpListener = new TcpListener(localaddr: IPAddress.Any, port: 8080);
    tcpListener.Start();

    clientSocket = tcpListener.AcceptSocket();
    clientSocket.Receive(BytesBeingReceived); // Receives header first

    ReceiveAndUnzip(GetZippedFolderSizeInBytes()); 
    // GetZippedFolderSizeInBytes() reads the header, which contains information about the folder size

    Console.WriteLine("Press any key to exit the program.");
    Console.ReadLine();
}

Как и ожидалось, программазавершает работу, как только он подключается к клиентскому сокету и получает все данные от клиента (пока клиент всегда является моей локальной машиной).

Однако я хотел бы, чтобы программа работала, пока сервер доступен (т.е. не перезапускается, не находится в спящем режиме, не выключается и т. Д.).После получения всех данных от клиента, я хотел бы сохранить TcpListener активным и ждать, возможно, больше данных.Если клиент отключается, TcpListener должен все еще оставаться в живых и ждать будущих ожидающих соединений.

Любые предложения о том, как я могу это сделать?

ОБНОВЛЕНИЕ:

Вот реализация GetZippedFolderSizeInBytes():

private static int GetFileSizeInBytes()
{
    return Convert.ToInt32(Encoding.ASCII.GetString(BytesBeingReceived)
                                         .Replace("\0", Empty)
                                         .Split(new[] { Environment.NewLine, ":" }, StringSplitOptions.RemoveEmptyEntries)[1]);
}

Вот реализация метода ReceiveAndUnzip(int numBytesExpectedToReceive):

private static void ReceiveAndUnzip(int numBytesExpectedToReceive)
{
    int numBytesLeftToReceive = numBytesExpectedToReceive;

    using (MemoryStream zippedFolderStream = new MemoryStream(new byte[numBytesExpectedToReceive]))
    {
        while (numBytesLeftToReceive > 0)
        {
            Array.Clear(BytesBeingReceived, 0, BufferSize);
            int numBytesReceived = clientSocket.Receive(BytesBeingReceived, SocketFlags.Partial);
            zippedFolderStream.Write(
                BytesBeingReceived, 
                0, 
                numBytesLeftToReceive < BufferSize ? numBytesLeftToReceive : BufferSize);
            numBytesLeftToReceive -= numBytesReceived;
        }

        zippedFolderStream.Unzip(afterReadingEachDocument: DoMoreStuff);
    }
}

1 Ответ

2 голосов
/ 11 апреля 2019

Вы должны изменить свой код, чтобы принимать новых клиентов в цикле.Возможно, вы захотите использовать некоторые асинхронные версии Receive и Accept и порождать некоторые задачи для обработки клиентов также в цикле, если вы хотите, чтобы ваши клиенты были подключены - это не совсем оптимальный способ, но он должен работать.

Сетевое программирование сложно.Вот несколько советов.


1.Дефрагментация сообщения

У вас нет гарантии, что один Отправить в клиенте приведет к одному Приему на сервере.Ваше сообщение может быть разбито на фрагменты или, если вы быстро отправите пару сообщений, все они могут быть объединены и получены с помощью одного приема вызова.

Одно решениек этой проблеме относится префикс сообщения с заголовком, например, с 4-байтовым целым числом.Это целое число является заголовком вашего сообщения и содержит информацию о длине вашего сообщения (в байтах).

То, что вы хотите сделать, это получать до тех пор, пока вы не получите этот 4-байтовый целочисленный заголовок, а затем получить остальную часть сообщения - теперь вы знаете, как долго это сообщение будет продолжаться, пока вы не получите все сообщение.Повторите этот процесс для каждого нового сообщения.

Это называется дефрагментация или дефрагментация .Это можно реализовать различными способами: разделителями, сообщениями с префиксами заголовков фиксированного размера, некоторым сочетанием этих двух.

2.Механизм поддержания активности / сердцебиения

В tcp возникают проблемы с полуоткрытыми и полузакрытыми соединениями.Короче говоря, это просто означает, что вы не можете быть уверены, что соединение все еще живое - одноранговый / клиент подключен, если вы не обмениваетесь сообщениями.Когда одноранговый / клиент отключается, тогда ваш Receive вызов должен возвращать 0 - это означает, что соединение закрыто, , но способ TCP работает не всегда , и вы должны учитывать этоИзбегайте всевозможных проблем.

Простейший механизм Keep-Alive, который вы можете реализовать, это просто некоторая логика тайм-аута, например, отсутствие сообщения в течение 2 минут означает, что узел / клиент отключен.Или добавьте дополнительное сообщение сердцебиения, отправляемое каждые X секунд, и установите тайм-аут на одноранговый узел / клиент, если он не отправил его в течение некоторого промежутка времени.

3.Используйте некоторую хорошо протестированную библиотеку

Если вы не реализуете какой-либо пользовательский протокол и не хотите самостоятельно решать все проблемы с TCP, которые уже были решены некоторыми умными парнями, ищите библиотеку.

...