Русский
Русский
English
Статистика
Реклама

Udp

Транспортный протокол QUIC приняли в качестве стандарта RFC 9000

01.06.2021 02:22:59 | Автор: admin


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

Уже много рассказывалось о преимуществах транспорта QUIC, который взят за основу будущего стандарта HTTP/3. В HTTP следующего поколения транспорт TCP меняется на QUIC, что означает автоматическое ускорение соединений и зашифровку всего интернет-трафика, который раньше шёл в открытом виде по TCP. Нешифрованный QUIC не предусмотрен вообще.

В мае 2021 года состоялось знаменательное событие: протокол QUIC принят в качестве официального стандарта RFC9000. Это великолепные новости для всей интернет-экосистемы.

Утверждением таких стандартов занимается Инженерный совет Интернета (IETF). Ранее были оформлены вспомогательные стандарты RFC 9001, RFC 9002 и RFC 8999.

Таким образом, QUIC версии 1 теперь официально принят и утверждён. Все участвующие стороны могут завершить эксперименты с черновиками протокола и перейти на первую официальную версию.

В последние годы QUIC был одним из главных приоритетов IETF. Появившись как эксперимент Google, вскоре разработка QUIC вышла на международный уровень. Она велась почти пять лет. Зафиксировано 26 очных собраний, 1749 задач в трекере и многие тысячи писем в почтовой рассылке.

QUIC очень амбициозный проект, который принесёт большие изменения. Транспортная экосистема интернета за несколько десятилетий закостенела, а QUIC оживит её, пишут инженеры из компании Fastly, которые входят в рабочую группу по разработке протокола.

Окостенение означает, что система с каждым годом становится всё менее гибкой, менее подвижной. QUIC принесёт в транспортный уровень множество инноваций, включая обязательное шифрование, версионность, гораздо более богатый и более производительный набор сервисов, поверх которых будут строиться новые технологии. Предполагается, что QUIC приведёт к появлению нового поколения интернет-инноваций. Это уже начало происходит с расширениями, такими как ненадёжные датаграммы (Unreliable Datagram Extension). Ненадёжные датаграммы открывают двери перед новым классом медиа в реальном времени и другими приложениями, которым нужен более функциональный транспорт, чем обязательная доставка пакетов с обрывом канала при потере нескольких пикселей. Мы уже видим многообещающие технологии, такие как MASQUE и WebTransport.

HTTP/3


Стандарт HTTP/3 (это HTTP поверх QUIC) идёт с небольшим опозданием за QUIC и тоже будет официально принят в самое ближайшее время.


34-й (!) драфт HTTP/3

С момента принятия HTTP/2 прошло шесть лет: спецификация RFC 7540 опубликована в мае 2015-го, но пока не используется повсеместно. Протокол реализован во всех браузерах ещё с конца 2015 года, а спустя три года только 45,4% из 10 млн самых популярных интернет-сайтов поддерживают HTTP/2. Два с половиной года назад таких было 31,2%. Севсем недавно на HTTP/2 перешли сайты Amazon, Paypal, Telegram.org.

Cейчас практически готова третья версия HTTP/3, осталось совсем немного подождать.

QUIC представляет собой замену TCP, которая работает поверх UDP. Изначально эта технология была создана инженерами Google, как и предыдущий протокол SPDY, который стал основой HTTP/2. В первое время QUIC именовали HTTP/2-encrypted-over-UDP.

Затем разработку QUIC передали в IETF для стандартизации. Здесь он разделилcя на две части: транспорт и HTTP. Идея в том, что транспортный протокол можно использовать также для передачи других данных, а не только эксклюзивно для HTTP или HTTP-подобных протоколов. Однако название осталось таким же: QUIC. Разработкой транспортного протокола занимается рабочая группа QUIC Working Group в IETF.

Долгое время версия IETF называлась iQUIC, в то время как Google и другие продолжили работу над собственной реализацией gQUIC, но 7 ноября 2018 года один из ведущих разработчиков протокола Дмитрий Тихонов объявил, что стороны достигли совместимости протоколов, и теперь разработка продолжится в общем русле. QUIC в Chrome включается в настройках chrome://flags. Есть ещё расширение-индикатор, которое показывает, какие сайты поддерживают QUIC.



Встроенная безопасность и производительность


В чём преимущества транспортного протокола QUIC перед TCP? Преимуществ очень много. По словам руководителя рабочей группы Марка Ноттингема, переход от устаревшего TCP на новые протоколы просто неизбежен, поскольку сейчас очевидно, что TCP страдает от проблем неэффективности.

Поскольку TCP протокол доставки пакетов по порядку, то потеря одного пакета может помешать доставке приложению последующих пакетов из буфера. В мультиплексированном протоколе это может привести к большой потере производительности, объясняет Марк Ноттингем. QUIC пытается решить эту проблему с помощью эффективной перестройки семантики TCP (вместе с некоторыми аспектами потоковой модели HTTP/2) поверх UDP.

Кроме перехода значительного объёма трафика с TCP на UDP, протокол QUIC требует обязательного шифрования: нешифрованного QUIC не существует вообще. QUIC использует TLS 1.3 для установки ключей сессии, а затем шифрования каждого пакета. Но поскольку он основан на UDP, значительная часть информации о сессии и метаданных, открытых в TCP, шифруется в QUIC.



В статье Будущее интернет-протоколов Марк Ноттингем говорит о значительных улучшениях в безопасности с переходом на QUIC:

На самом деле текущий короткий заголовок iQUIC который используется для всех пакетов, кроме рукопожатия выдаёт только номер пакета, необязательный идентификатор соединения и байт состояния, необходимый для процессов вроде смены ключей шифрования и типа пакета (который тоже может быть зашифрован). Всё остальное зашифровано включая пакеты ACK, что значительно повышает планку для проведения атак с анализом трафика.

Кроме того, становится невозможна пассивная оценка RTT и потерь пакетов путём простого наблюдения за соединением; там недостаточно информации для этого.

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

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

Возможно, принятие стандарта QUIC произошло бы и раньше, если бы компания Google не поспешила внедрить свою реализацию в браузер Chrome, так что случилось раздвоение стандарта.

Тем не менее, прогресс неизбежен и в ближайшие годы обязательно продолжится стандартизация и повсеместное внедрение различных протоколов нового поколения, в том числе HTTP/3 на транспорте QUIC.



См. также:





Отмечайте юбилей GlobalSign и получайте скидки!


Подробнее..

На замену TCP обсуждение протокола QUIC

05.07.2020 14:08:58 | Автор: admin
QUIC новый транспортный протокол, работающий поверх UDP. Некоторые в шутку называют его TCP/2. Расскажем, что сейчас обсуждают, как принять участие и кто внедряет поддержку QUIC.


/ Unsplash / Sticker Mule

Что такое QUIC


Это механизм передачи данных по сети, построенный на протоколе UDP. Позволяет сократить задержку соединения. В отличие от TCP, который использует принцип тройного рукопожатия, в QUIC рукопожатие происходит в один этап со знакомым сервером и в два этапа с незнакомым.

По сравнению с TCP QUIC также обладает большей пропускной способностью. Тесты показали 30-процентное снижение числа ребуферизаций при воспроизведении YouTube-видео.

Какие документы обсуждают


В 2018 году представители Инженерного совета интернета (IETF) отмечали, что QUIC готов для широкомасштабных тестов, но пока не может стать стандартом из-за ряда недостатков. За два года протокол доработали, и группа экспертов готовится оформить его в формате RFC.

Дополнительное чтение из нашего блога на Хабре:


В середине июня сопредседатель рабочей группы в IETF Лукас Пардью (Lucas Pardue) сообщил о начале последнего этапа обсуждения черновиков QUIC. Всего документов шесть, и они посвящены различным аспектам работы протокола:

  • QUIC Transport. Это описание механизмов транспортного протокола QUIC: управление потоками передачи данных и обработки пакетов, согласование версий, открытие защищенного канала связи и обмен криптографическими ключами.
  • QUIC Loss Detection and Congestion Control. Содержит описание методов контроля целостности данных и перегрузки каналов связи.
  • Using TLS to Secure QUIC. Документ, посвященный использованию TLS для защиты QUIC. Есть информация об интерфейсах, работе с ключами и регистрах IANA.
  • Version-Independent Properties of QUIC. Здесь описаны свойства нового протокола, которые должны оставаться неизменными от версии к версии например, заголовки.
  • HTTP/3. Документ, описывающий сопоставление семантики HTTP на QUIC.
  • QPACK Header Compression for HTTP/3. Документ посвящен формату сжатия заголовков QPACK в частности, работе кодировщика и декодировщика.

Обсуждение закончат на следующей неделе 8 июля. Через какое-то время после этого спецификация QUIC получит одобрение IETF и будет опубликована. Принять участие в обсуждении могут все желающие свои замечания и предложения можно оставить на GitHub.

Сейчас ведется дискуссия, посвященная выбору между AAD или nonce. Но возвращаться к вопросам, по которым уже был достигнут консенсус, будут только по веским причинам.

Кто уже внедряет протокол


Несмотря на то что QUIC пока не является стандартом, его используют некоторые ИТ-компании. С ним начали работать CDN-сервисы, включая Cloudflare и Verizon Digital Media Services (VDMS).


/ Unsplash / Nathan Dumlao

Экспериментальную поддержку HTTP/3 уже добавили в Chrome и Firefox. В последнем случае работа протокола строится на проекте Neqo (есть на GitHub). Это реализация клиента и сервера для QUIC.

Черновики IETF использовали и в NGINX в середине июня компания представила превью-версию прокси-сервера с поддержкой QUIC и HTTP/3. В конце мая Microsoft также объявили, что открывают код библиотеки MsQuic с реализацией протокола. Библиотека кроссплатформенная можно запустить на Windows и Linux, используя Schannel и OpenSSL соответственно (для TLS 1.3). Эксперты прогнозируют, что с принятием стандарта QUIC свои реализации выпустит еще больше компаний.

О чем мы пишем в корпоративном блоге:

Подробнее..

Реализация Minecraft Query протокола в .Net Core

23.02.2021 20:21:55 | Автор: admin

Minecraft Server Query это простой протокол, позволяющий получить актуальную информацию о состоянии сервера путём отправки пары-тройки незамысловатых UDP-пакетов.

На вики есть подробное описание этого протокола с примерами реализации на разных языках. Однако меня поразило, насколько куцые реализации для .Net существуют на данный момент. Поискав некоторое время, я наткнулся на несколько репозиториев. Предлагаемые решения либо содержали банальные ошибки, либо имели урезанный функционал, хотя, казалось бы, куда ещё больше урезать-то.

Так было принято решение написать свою реализацию.

Скажи мне, кто ты...

Для начала, посмотрим, что из себя представляет сам протокол Minecraft Query. Согласно вики, мы имеем в распоряжении 3 вида пакетов запросов и, соотвественно, 3 вида пакетов ответа:

  • Handshake

  • BasicStatus

  • FullStatus

Первый тип пакета используется для получения ChallengeToken, необходимого для формирования других двух пакетов. Привязывается он к IP-адресу отправителя на 30 секунд. Смысловая нагрузка оставшихся двух ясна из названий.

Стоит отметить, что хотя последние два запроса отличаются друг от друга лишь выравниванием на концах пакетов, присылаемые ответы отличаются способом представления данных. Для примера, вот так выглядит ответ BasicStatus

Ответ на запрос BasicStatusОтвет на запрос BasicStatus

А вот так FullStatus

Ответ на запрос FullStatusОтвет на запрос FullStatus

Все данные, помимо тех, что хранятся в short, представлены в big-endian. А для поля SessionId, которое постоянно в рамках одного клиент-сервер соединения, должно выполняться условие SessionId & 0x0F0F0F0F == SessionId.

В общем виде запрос выглядит так

Запрос в общем видеЗапрос в общем виде

Более подробно об этом об этом можно почитать на вики.

И я скажу тебе, как тебя распарсить

Для начала, определимся, что мы хотим получить на выходе. Готовая библиотека должна предоставлять API для отправки любого из 3 видов пакетов и получения результата в распаршеном виде.

При этом, я хочу больше свободы в плане поддержания жизнеспособности сокетов и обновления ChallengeToken. Если я буду запрашивать состояние сервера каждые 3 секунды, то я не хочу, чтобы вместо одного пакета запроса отправлялось два: хэндшейк и состояние. И наоборот, если я опрашиваю сервер раз в час, зачем мне слать запросы каждые 30 секунд? Поэтому работа с библиотекой будет происходить в "ручном" режиме.

Итак, определившись, можем уже представить, как будет выглядеть архитектура классов. Я вижу работу примерно таким образом

public static async Task<ServerState> DoSomething(IPAddress host, int port) {var mcQuery = new McQuery(host, port);  mcQuery.InitSocket();  await mcQuery.GetHandshake();  return await mcQuery.GetFullStatus();}

Здесь создаётся разовое соединение. Для долгоживущего потребуется проверять состояние сокета и инициализировать заново (об этом в конце статьи).

Для того, чтобы пакет отправить, его надо для начала сформировать. Этим будет заниматься класс Request.

public class Request{// Набор констант для формирования пакета    private static readonly byte[] Magic = { 0xfe, 0xfd };    private static readonly byte[] Challenge = { 0x09 };    private static readonly byte[] Status = { 0x00 };      public byte[] Data { get; private set; }        private Request(){}    public byte RequestType => Data[2];    public static Request GetHandshakeRequest(SessionId sessionId)    {        var request = new Request();              // Собираем пакет        var data = new List<byte>();        data.AddRange(Magic);        data.AddRange(Challenge);        data.AddRange(sessionId.GetBytes());                request.Data = data.ToArray();        return request;    }    public static Request GetBasicStatusRequest(SessionId sessionId, byte[] challengeToken)    {        if (challengeToken == null)        {            throw new ChallengeTokenIsNullException();        }                    var request = new Request();                var data = new List<byte>();        data.AddRange(Magic);        data.AddRange(Status);        data.AddRange(sessionId.GetBytes());        data.AddRange(challengeToken);                request.Data = data.ToArray();        return request;    }        public static Request GetFullStatusRequest(SessionId sessionId, byte[] challengeToken)    {        if (challengeToken == null)        {            throw new ChallengeTokenIsNullException();        }                var request = new Request();                var data = new List<byte>();        data.AddRange(Magic);        data.AddRange(Status);        data.AddRange(sessionId.GetBytes());        data.AddRange(challengeToken);        data.AddRange(new byte[] {0x00, 0x00, 0x00, 0x00}); // Padding                request.Data = data.ToArray();        return request;    }}

Здесь всё просто. Храним все константы внутри класса и формируем пакет в трёх статических методах. Можно ещё заметить класс SessionId, который может давать как байтовое, так и строковое представление по необходимости.

public class SessionId{    private readonly byte[] _sessionId;    public SessionId (byte[] sessionId)    {        _sessionId = sessionId;    }// Случайный SessionId    public static SessionId GenerateRandomId()    {        var sessionId = new byte[4];        new Random().NextBytes(sessionId);        sessionId = sessionId.Select(@byte => (byte)(@byte & 0x0F)).ToArray();        return new SessionId(sessionId);    }    public string GetString()    {        return BitConverter.ToString(_sessionId);    }    public byte[] GetBytes()    {        var sessionId = new byte[4];        Buffer.BlockCopy(_sessionId, 0, sessionId, 0, 4);        return sessionId;    }}

Дождавшись ответа сервера, мы получаем последовательность байт, которые хотим привести к человекочитаемому виду. Для этого служит класс Response, который представляет набор "парсеров" в виде статических полей.

public static class Response{public static byte ParseType(byte[] data){return data[0];}  // public static SessionId ParseSessionId(byte[] data){if (data.Length < 1) throw new IncorrectPackageDataException(data);var sessionIdBytes = new byte[4];Buffer.BlockCopy(data, 1, sessionIdBytes, 0, 4);return new SessionId(sessionIdBytes);}public static byte[] ParseHandshake(byte[] data){if (data.Length < 5) throw new IncorrectPackageDataException(data);var response = BitConverter.GetBytes(int.Parse(Encoding.ASCII.GetString(data, 5, data.Length - 6)));if (BitConverter.IsLittleEndian){response = response.Reverse().ToArray();}return response;}public static ServerBasicState ParseBasicState(byte[] data){if (data.Length <= 5)throw new IncorrectPackageDataException(data);var statusValues = new Queue<string>();short port = -1;data = data.Skip(5).ToArray(); // Skip Type + SessionIdvar stream = new MemoryStream(data);var sb = new StringBuilder();int currentByte;int counter = 0;while ((currentByte = stream.ReadByte()) != -1){if (counter > 6) break;      // Парсим нормер портаif (counter == 5){byte[] portBuffer = {(byte) currentByte, (byte) stream.ReadByte()};if (!BitConverter.IsLittleEndian)portBuffer = portBuffer.Reverse().ToArray();port = BitConverter.ToInt16(portBuffer); // Little-endian shortcounter++;continue;}      // Парсим параметры-строкиif (currentByte == 0x00){string fieldValue = sb.ToString();statusValues.Enqueue(fieldValue);sb.Clear();counter++;}else sb.Append((char) currentByte);}var serverInfo = new ServerBasicState{Motd = statusValues.Dequeue(),GameType = statusValues.Dequeue(),Map = statusValues.Dequeue(),NumPlayers = int.Parse(statusValues.Dequeue()),MaxPlayers = int.Parse(statusValues.Dequeue()),HostPort = port,HostIp = statusValues.Dequeue(),};return serverInfo;}  // "Секции" пакета резделены константными последовательностями байт,  // это можно испльзовать для проверки, что мы всё сделали правильноpublic static ServerFullState ParseFullState(byte[] data){var statusKeyValues = new Dictionary<string, string>();var players = new List<string>();var buffer = new byte[256];Stream stream = new MemoryStream(data);stream.Read(buffer, 0, 5); // Read Type + SessionIDstream.Read(buffer, 0, 11); // Padding: 11 bytes constantvar constant1 = new byte[] {0x73, 0x70, 0x6C, 0x69, 0x74, 0x6E, 0x75, 0x6D, 0x00, 0x80, 0x00};for (int i = 0; i < constant1.Length; i++)Debug.Assert(constant1[i] == buffer[i], "Byte mismatch at " + i + " Val :" + buffer[i]);var sb = new StringBuilder();string lastKey = string.Empty;int currentByte;while ((currentByte = stream.ReadByte()) != -1){if (currentByte == 0x00){if (!string.IsNullOrEmpty(lastKey)){statusKeyValues.Add(lastKey, sb.ToString());lastKey = string.Empty;}else{lastKey = sb.ToString();if (string.IsNullOrEmpty(lastKey)) break;}sb.Clear();}else sb.Append((char) currentByte);}stream.Read(buffer, 0, 10); // Padding: 10 bytes constantvar constant2 = new byte[] {0x01, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x5F, 0x00, 0x00};for (int i = 0; i < constant2.Length; i++)Debug.Assert(constant2[i] == buffer[i], "Byte mismatch at " + i + " Val :" + buffer[i]);while ((currentByte = stream.ReadByte()) != -1){if (currentByte == 0x00){var player = sb.ToString();if (string.IsNullOrEmpty(player)) break;players.Add(player);sb.Clear();}else sb.Append((char) currentByte);}ServerFullState fullState = new(){Motd = statusKeyValues["hostname"],GameType = statusKeyValues["gametype"],GameId = statusKeyValues["game_id"],Version = statusKeyValues["version"],Plugins = statusKeyValues["plugins"],Map = statusKeyValues["map"],NumPlayers = int.Parse(statusKeyValues["numplayers"]),MaxPlayers = int.Parse(statusKeyValues["maxplayers"]),PlayerList = players.ToArray(),HostIp = statusKeyValues["hostip"],HostPort = int.Parse(statusKeyValues["hostport"]),};return fullState;}}

Таким образом мы получаем полный инструментарий для формирования пакетов, их отправки, получения ответов и извлечения из них необходимой информации.

Долгоживущие приложения на основе библиотеки

Вернёмся к том, о чем я говорил выше. Это можно реализовать таким образом. Код взят из моего нотификатора пользовательской активности. Здесь каждые 5 секунд запрашивается FullStatus, поэтому имеет смысл обновлять ChallengeToken периодически сразу после истечения предыдущего. Всего приложение имеет 2 режима работы: штатный и режим восстановления соединения.

В штатном режиме приложение по таймерам обновляет токен и запрашивает FullStatus. При обнаружении упавшего сервера/оборванного соединения/etc (5 попыток передачи) приложение переходит в режим восстановления соединения и при удачной попытке получения сообщения снова возвращается в штатный режим.

Для начала напишем конструктор и два метода для запуска прослушивания сервера и окончания.

public StatusWatcher(string serverName, string host, int queryPort){    ServerName = serverName;    _mcQuery = new McQuery(Dns.GetHostAddresses(host)[0], queryPort);    _mcQuery.InitSocket();}public async Task Unwatch(){    await UpdateChallengeTokenTimer.DisposeAsync();    await UpdateServerStatusTimer.DisposeAsync();}public async void Watch(){  // Обновляем challengetoken по таймеру каждые 30 секунд    UpdateChallengeTokenTimer = new Timer(async obj =>    {        if (!IsOnline) return;                if(Debug)            Console.WriteLine($"[INFO] [{ServerName}] Send handshake request");        try        {            var challengeToken = await _mcQuery.GetHandshake();                      // Если всё ок, говорим, что мы в онлайне и сбрасываем счетчик попыток            IsOnline = true;                      lock (_retryCounterLock)            {                RetryCounter = 0;            }                        if(Debug)                Console.WriteLine($"[INFO] [{ServerName}] ChallengeToken is set up: " + BitConverter.ToString(challengeToken));        }              // Если что-то не так, увеличиваем счетчик неудачных попыток        catch (Exception ex)        {            if (ex is SocketException || ex is McQueryException || ex is ChallengeTokenIsNullException)            {                if(Debug)                    Console.WriteLine($"[WARNING] [{ServerName}] [UpdateChallengeTokenTimer] Server doesn't response. Try to reconnect: {RetryCounter}");                if(ex is McQueryException)                    Console.Error.WriteLine(ex);                                lock (_retryCounterLock)                {                    RetryCounter++;                    if (RetryCounter >= RetryMaxCount)                    {                        RetryCounter = 0;                        WaitForServerAlive(); // Переходим в режим восстановления соединения                    }                }            }            else            {                throw;            }        }            }, null, 0, GettingChallengeTokenInterval);            // По таймеру запрашиваем текущее состояние    UpdateServerStatusTimer = new Timer(async obj =>    {        if (!IsOnline) return;                if(Debug)            Console.WriteLine($"[INFO] [{ServerName}] Send full status request");        try        {            var response = await _mcQuery.GetFullStatus();                        IsOnline = true;            lock (_retryCounterLock)            {                RetryCounter = 0;            }                        if(Debug)                Console.WriteLine($"[INFO] [{ServerName}] Full status is received");                        OnFullStatusUpdated?.Invoke(this, new ServerStateEventArgs(ServerName, response));        }              // По аналогии с предыдущим        catch (Exception ex)        {            if (ex is SocketException || ex is McQueryException || ex is ChallengeTokenIsNullException)            {                if(Debug)                    Console.WriteLine($"[WARNING] [{ServerName}] [UpdateServerStatusTimer] Server doesn't response. Try to reconnect: {RetryCounter}");                if(ex is McQueryException)                    Console.Error.WriteLine(ex);                                lock (_retryCounterLock)                {                    RetryCounter++;                    if (RetryCounter >= RetryMaxCount)                    {                        RetryCounter = 0;                        WaitForServerAlive();                    }                }            }                        else            {                throw;            }        }            }, null, 500, GettingStatusInterval);}

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

public async void WaitForServerAlive(){    if(Debug)        Console.WriteLine($"[WARNING] [{ServerName}] Server is unavailable. Waiting for reconnection...");  // Отключаем отслеживание    IsOnline = false;    await Unwatch();    _mcQuery.InitSocket(); // Пересоздаём сокет    Timer waitTimer = null;    waitTimer = new Timer(async obj => {        try        {            await _mcQuery.GetHandshake();          // Говорим, что можно возвращаться в штатный режим и отключаем таймер            IsOnline = true;            Watch();            lock (_retryCounterLock)            {                RetryCounter = 0;            }            waitTimer.Dispose();        }            // Пересоздаем сокет каждые 5 (настраивается) неудачных соединений        catch (SocketException)        {            if(Debug)                Console.WriteLine($"[WARNING] [{ServerName}] [WaitForServerAlive] Server doesn't response. Try to reconnect: {RetryCounter}");            lock (_retryCounterLock)            {                RetryCounter++;                if (RetryCounter >= RetryMaxCount)                {                    if(Debug)                        Console.WriteLine($"[WARNING] [{ServerName}] [WaitForServerAlive] Recreate socket");                    RetryCounter = 0;                    _mcQuery.InitSocket();                }            }        }    }, null, 500, 5000);}
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru