Всё о о Microsoft Exchange Server и электронной почте.

Еженедельная онлайн встреча для ИТ специалистов — «Разговоры об ИТ» состоится в среду, 20 октября 2010 в 21:00(MSK)

Еженедельная онлайн встреча для ИТ специалистов Разговоры об ИТ Привет всем! Сегодня среда, а значит вечером мы будем весело и непринужденно болтать на ИТ тематику. Сегодня на нашей встрече докладчика нет, но может это и к лучшему. Судя по последним спорам народ хочет просто пообщаться. 

У нас со Stanky постоянно возникают технического рода споры. По давней традиции я в них проигрываю. Последний раз ругались на тему работы RoundRobin в DNS. Много на эту тему уже сказано, много написано, но на практике никто с этой технологийе не сталкивался. Я долго Сане объяснял разницу между RR и NLB в отказоустойчивости, хотя я уверен, что он прекрасно все знал.  В общем когда дело дошло до демонстрации, то я круто … в общем у меня не заработало.

Вчера он мне сообщил, что RR круто работает.

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

p.s. Надеюсь у меня стенд работает 🙂  

 

Вход на «Разговоры об ИТ»

(начнет работать за 30 мин до встречи)

Для участия нужна гарнитура(наушники+микрофон), Интернет и клиент Live Meeting 2007(15Mb). Желательно использовать USB гарнитуру.

 

Запись встречи:  [Загрузка не найдена].

Похожие посты:

  • Oleg Krylov

    RR ни разу не балансировщик. WNLB конечно тоже так себе балансировщик, но там он хотя бы рудиментарный. Если мы выберем No Affinity в WNLB — получится прямо настоящая (ну почти) балансировка. В RR мы мерзко вяжемся на локальный кэш. Но ни одна из технологий не идет в сравнение с балансировщиками L3. Но тогда мы теряем одну из прелестей — авторизацию по IP. И с логами коннектов сложнее. Другие балансировщики, основанные на диспетчере, работают мега круто, но имеют узкое место — этот самый диспетчер. Короче с удовольствием с вами бы поспорил — но у меня нет нормального интернета. Только 3G-модем. А это не комильфо. Так что давайте запись 😀

  • http://www.exchangerus.ru Pavel Nagaev

    Вот у меня тоже все уперлось в локальный кеш, но ведь должно же работать.

  • Stanky

    Блин, вот же ш подстава!

  • Stanky

    Все прям такие умные, но ни один на встречу не явился, а отмазались, как смогли!

    А, вообще, про балансировку — это Паша перегнул. Я же говорил про это в совершенно ином контексте. Например, когда у нас два провайдера и необходимо иметь возможность доступа к OWA даже тогда, когда один из них упал. NLB тут ни как не поможет, а всякого рода железки будут безумно дорогущими. А вот, как его назвал Паша, RoundRobin — дёшево и сердито и не для самых бедных. Уточняю — я говорил о том, что если в DNS'е на одно и то же имя заведено несколько IP'шников, то при попытке доступа на нерабочий, клиент потупит-потупит и полезет на другой. О балансировке здесь и речи не шло, только про "отказоустойчивость". Сам RoundRobin, посути, здесь вообще ни при чём.

  • Alexander Trofimov

    Эх… не успеваю я к вам со своим расписанием… Так что я "шекспира не читал, но мнение имею:" RR это для бедных, которым все пофик, Олег все правильно написал.
    =)

  • Alexander Trofimov

    Не могу у среду =(
    Понедельник, вторник, при удачном стечении обстоятельств — тяпница. Но не перестраиваться жеж ради меня =)
    По RR — зависит, если все по-дефолту, то там TTL 1 день в локальном кеше и 1 час в DNS зоне, кажется. Тогда никуда он не полезет — DNS возвратил нужное значение, а не negative, а на уровне приложения DNS не смотрится. А если подкрутить TTL, то оно будет то работать, то не работать… Тоже не айс. В общем — проще сделать низкий TTL и переключение записей в DNS вручную или автоматом. ИМХО, ессно =)

  • http://www.exchangerus.ru Pavel Nagaev

    Встреча получилась офигенно познавательной на мой взгляд. 13 человек просидело до самого конца, причем вопросов практически не было и мы вещали вдвоем с Сашей.

    Мне лично очень понравилось. Напишу пост по результатам наших исследований и выложу видео.

  • Сергей Иванов

    Как то раньше народ лучше объяснял. Вот статейка старая, всё просто и понятно:
    http://info.nic.ru/st/8/out_263.shtml

  • Stanky

    to Alexander Trofimov:

    Федя, ты неправ! RoundRobin — это порядок выдачи IP-адресов DNS-сервером, а клиент просто перебирает их в том порядке, как их выдал DNS-сервер, пока не найдётся первый рабочий. Для справки: DNS-сервер в одном ответе возвращает сразу все IP-адреса.

    to Сергей Иванов:

    Ждём от вас на следующую встречу интересный доклад, да так чтоб было просто, понятно, позновательно и применимо в рабочей среде с базами, которые съедают по 100 ГБ памяти…

  • http://www.exchangerus.ru Pavel Nagaev

    У Вас всегда есть возможность оборвать нас и рассказать лучше и понятно. Тем более мы не ставили задачу научить или объяснить, как это работает. Мы пытались разобраться сами, что и было сделано. Главное — посмотрели вживую.

  • Stanky

    Паша, ты ври, да не заверайся — .NET тогда и в помине не было, когда сети появлялись. Это функция по работе с сокетами, которая в конечном итоге вызывает WSAConnect из WinAPI.

  • Stanky

    Повторюсь: connect — это работа с сокетами, которые по древности появления в системах сравнимы с выделениями мамонтов.

  • Stanky

    to Сергей Иванов:

    О да! Все сразу же всё поняли — проще и наглядней просто некуда.

    Хотите сделать наглядно, тогда уж сделайте ещё и обёрточку для это кода, чтоб каждый смог его работоспособность проверить…

  • Сергей Иванов

    Обрывать зачем? Было интересно. Конечно ожидал увидеть несколько большее. Просто из беседы не понял всё-таки как это работает до конца. Оказалось всё просто, "клиенту" просто передаёт DNS список адресов. Он его перебирает в том порядке как прислал DNS. А round robin это получается алгоритм,  который определяет в каком порядке прислать этот список.

  • http://ystartsev.wordpress.com Yegor Startsev

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

  • http://www.exchangerus.ru Pavel Nagaev

    "А round robin это получается алгоритм,  который определяет в каком порядке прислать этот список. "  Да, правильно.

    Теперь надо узнать, на каком уровне это работает. Клиент обрабатывает или все же Windows.  Вчера сошлись на то, что Windows. Мне уже программеры утилиту написали, проверю вечерком.

  • Сергей Иванов

    Перебирает клиент естественно.

  • http://www.exchangerus.ru Pavel Nagaev

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

    Т.е. при подключении  приложение вызывает функцию и передает ей имя сервера и номер порта. Если это две записи, то DNS сервер их вернет. Функция Connect  попробует установить соединение. Если будет ошибка, то она молча попробует установить соединение со вторым IP. Если не получится, то только тогда вернет ошибку.

    Поскольку подключение происходит по IP и номеру порта, то если скажем IIS отвалился, то подключение произойдет на другой сервер. Это очень клево, т.к. "отслеживается" состояние сервиса.

     

  • http://www.exchangerus.ru Pavel Nagaev

    Еще по поводу балансировки. Механизм RR можно считать только "условной" балансировкой, т.к. не отслеживается реальная загруженность хостов. Например, утро, клиенты начинают подключаться к двум веб серверам по одному имени и раскидываются DNS сервером 50 на 50. Потом некоторые отключаются, некотороые работают. И через время мы получим, что на одном сервере сидит 90% пользователей, а на другом 10%.  А раскидвание происходит 50 на 50.

    Какая же это балансировка? 🙂

  • Сергей Иванов

    "Перебирает функция connect из библиотеки дотнет"- А функция connect это что? А если дотнета нету?

  • Сергей Иванов

    Для наглядности кусок java-апплета, который о dot.net никакого представления не имеет:
     
    // функция определяет адрес веб-сервера
    public Host getHostBase(){

    Host base = new Host();

    // берем DNS-имя веб-сервера
    base.name = getDocumentBase().getHost();
    //
    порт, по которому он работает
    base.port = getDocumentBase().getPort();

    if( base.port == -1 )
    base.port = 80; // на заметку: -1 = 80

    base.ip = "unknown";

    // создаем подключение к серверу по его DNS,
    //
    чтобы получить IP-адрес сервера:
    try
    {
    Socket connection = new Socket(base.name, base.port);
    byte[] ip = connection.getInetAddress().getAddress();
    base.ip = getStringIP(ip); // превращаем массив в строку

    }catch(Exception e){ base.ip = "error"; };

    return base;
    }

  • http://www.exchangerus.ru Pavel Nagaev

    ".NET тогда и в помине не было, когда сети появлялись" это я прекрасно знаю. Вчера мы вроде бы обсуждали, это работа логики приложения, функции подсоединения или стека TCP/IP. Вроде выяснили, что функции подсоединения. Теперь получилось, что их две. дот нет верхний уровень и WSAConnect более нижний.

    Сейчас проверю работу WSAConnect из WinAPI.

  • http://www.exchangerus.ru Pavel Nagaev

    Из кода видна, что логика, реализованная в функции дотнета(! может и в WSAConnect из WinAPI) реализуется в приложении. Но это на самом деле не плюс приложению.

  • Сергей Иванов

    Я думал этого достаточно 🙂
    В инете куча исходников функций, которые возвращают список всех айпишников для хоста в массив.
    Вот например:
    http://www.cflib.org/index.cfm?event=page.udfbyid&udfid=1310
    Джава привёл специально, чтобы абстрагироваться от винды. Естественно что делать с этим массивом разбирает программа, а не винда или linux.

  • Stanky

    Ну что ж, значит, это, всё же, сам клиент должен реализовать проход по всей цепочке серверов, если он использует обычные Socket'ы.

    В .NET'е этот недочёт исправили.

  • Сергей Иванов

    Я так понимаю, что RR- это как раз больше балансировка  и мне кажется Павел прав.

  • Stanky

    RoundRobin — балансировка (хоть и паршивая). Кто-то с этим спорит?

  • http://www.exchangerus.ru Pavel Nagaev

    Любопытно, что

    int WSAConnect(
      __in   SOCKET s,
      __in   const struct sockaddr *name,
      __in   int namelen,
      __in   LPWSABUF lpCallerData,
      __out  LPWSABUF lpCalleeData,
      __in   LPQOS lpSQOS,
      __in   LPQOS lpGQOS
    );
     

    name [in]

    A pointer to a sockaddr structure that specifies the address to which to connect. For IPv4, the sockaddr contains AF_INET for the address family, the destination IPv4 address, and the destination port.

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

     

  • http://www.exchangerus.ru Pavel Nagaev

     

    Вот что написал наш программер:

    Говорим о библиотеке winsock2 (winsock2.h)

     Функция WSAConnect объявлена как

    int WSAAPI WSAConnect(IN SOCKET s, __in_bcount(namelen) const struct sockaddr FAR * name, IN int namelen, __in_opt LPWSABUF lpCallerData, __out_opt LPWSABUF lpCalleeData, __in_opt LPQOS lpSQOS, __in_opt LPQOS lpGQOS);

    параметр *name – указатель на структуру, в которой сидит информация, с чем соединяемся:

    typedef struct sockaddr_in {

        ADDRESS_FAMILY sin_family;

        USHORT sin_port;

        IN_ADDR sin_addr;

        CHAR sin_zero[8];

    } SOCKADDR_IN, *PSOCKADDR_IN;


    sin_addr – структура, в недрах которой  должен сидеть IP-адрес в формате ULONG. Т.е. ты должен как-то отресловить имя в IP, прежде чем вызывать WSAConnect

  • Stanky

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

    А в кавычках отказоустойчивость потому что настоящей отказоустойчивостью это не является — всего лишь механизм обхода проблемы.

    Надеюсь, теперь всё просто и понятно?

  • Stanky

    В общем, надо бы поднять литературу по сетевому программированию, которую я в своё время читал и найти, что именно там было написано, когда в DNS'е имеется несколько записей на одно имя…

  • http://www.exchangerus.ru Pavel Nagaev

     

    Вот еще от коллеги:


     Я посмотрел рефлектором, как работает .Net-реализация Connect-а. Она берет входной параметр hostname. Если может его понять, как IP адрес, коннектится по нему. Если нет – пытается отресолвить hostname в _список_  IP адресов функцией Dns.GetHostAddresses(hostname). Для каждого IP в цикле вызывает WSAConnect из WinSocket до первого успешного соединения.

  • Alexander Trofimov

    Да, я неправ. Каюсь, совсем забыл, надо будет почитать снова =)
    Впрочем, я знаю, откуда у меня эта мчсль дурная взялась — я неоднократно натыкался на клиентов, которые неправильно это дело обрабатывали. =(

  • http://www.exchangerus.ru Pavel Nagaev

     

    Когда в .NET вызываем Connect(String host,int port), работает этот код (везде ниже убрал несущественные куски кода, добавил камменты)

     

    public void Connect(string host, int port)

    {

       IPAddress[] hostAddresses = Dns.GetHostAddresses(host);

    // Здесь уже вызов Connect с первым параметром – массивом IP адресов

        this.Connect(hostAddresses, port);

    }

     

    Dns.GetHostAdresses(host) делает следующее:

     

    public static IPAddress[] GetHostAddresses(string hostNameOrAddress)

    {

        IPAddress address;

        IPAddress[] addressList;

       // Если в строке передали IP адрес, возвращаем его

        if (IPAddress.TryParse(hostNameOrAddress, out address))

        {

            addressList = new IPAddress[] { address };

        }

        else

        {

            addressList = InternalGetHostByName(hostNameOrAddress, true).AddressList;

        }

        return addressList;

    }

     

     InternalGetHostByName(hostNameOrAddress, true) возвращает структуру Host, у которой есть поле списка адресов. Она уже напрямую обращается к UnsafeNclNativeMethods.OSSOCK.gethostbyname(hostName), которая является обвязкой WinAPI-функции из winsocket:

    [DllImport("ws2_32.dll", CharSet=CharSet.Ansi, SetLastError=true)]

    internal static extern IntPtr gethostbyname([In] string host);

     

    После получения списка адресов вызывается Connect, у которого первый параметр – этот список:

     

    public void Connect(IPAddress[] addresses, int port)

    {

        foreach (IPAddress address in addresses)

        {

                    // В цикле вызываются Connect-ы с параметром IPEndPoint (точкой конечного соединения), который создается из IP адреса и порта

                    this.Connect(new IPEndPoint(address, port));

                    break;

        }

    }

     

    Если ты еще не офигел от вложенности вызовов, то терпи: осталось немного. Вызов метода Connect с параметром EndPoint:

     

    public void Connect(EndPoint remoteEP)

    {

           // Наконец добрались до внутренней функции DoConnect

            this.DoConnect(point2, socketAddress);

    }

     

    Внутрення ф-я DoConnect уже напрямую вызывает UnsafeNclNativeMethods.OSSOCK.WSAConnect – обвязку WinAPI-функции из winsockets:

    [DllImport("ws2_32.dll", SetLastError=true)]

    internal static extern SocketError WSAConnect([In] IntPtr socketHandle, [In] byte[] socketAddress, [In] int socketAddressSize, [In] IntPtr inBuffer, [In] IntPtr outBuffer, [In] IntPtr sQOS, [In] IntPtr gQOS);

     

     

     private void DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)

    {

        if (UnsafeNclNativeMethods.OSSOCK.WSAConnect(this.m_Handle.DangerousGetHandle(), socketAddress.m_Buffer, socketAddress.m_Size, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) != SocketError.Success)

        {

        }

    }

  • Сергей Иванов

    Stanky пишет: RoundRobin – балансировка (хоть и паршивая). Кто-то с этим спорит?
    Не знаю. Это я на это ответил фразу: "А, вообще, про балансировку – это Паша перегнул. "
    И совсем не понял про "отказоустойчивость." даже в кавычках. По сути под "отказоустойчивостью" понимается несколько ip на hostname. И порядок перебора никак не влияет на "отказоустойчивость". 
    Про то что RR совсем уж паршив тоже не очень согласен. Примитивен да. Но 90%:10% очень трудно будет получить. Данные будут браться из кэша. И отключившийся клиент возьмёт тот же порядок ip  из кэша. 

  • Сергей Иванов

    Так понятно 🙂 Много букв просто.
    Подведу итог чего я понял 😉
     Если упал сервак или отвалился провайдер, то юзеруу, работающему с программой, которая умеет перебирать адреса выданные DNS сервером достаточно перезапустить программу или в случае браузера обновить страничку. Юзеру, работающему с программой, не умеющей перебирать адреса нужно зафлушить кэш DNS, чтобы работающий ip-шник был первым.

  • http://www.exchangerus.ru Pavel Nagaev

    "программой, не умеющей перебирать адреса нужно зафлушить кэш DNS"

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

    даже если удалить запись из кеша и получить валидную А запись, на работающий сервер, то рано или поздно она протухнет и приложение получит в кеш IP адрес неработающего сервера. И в приложении возникнет ошибка.

     

     

  • Сергей Иванов

    "даже если удалить запись из кеша и получить валидную А запись, на работающий сервер, то рано или поздно она протухнет и приложение получит в кеш IP адрес неработающего сервера. И в приложении возникнет ошибка."-  с этим не согласен, судя по тому что я видел на вашей демонстрации, если сервер "упал", то DNS возвращал адрес работающего сервера первым. 

  • http://www.exchangerus.ru Pavel Nagaev

    DNS сервер не умеет отслеживать состояние сервера, он просто выплевывает один адрес, потом другой, потом опять первый.

    Это уже внутри клиента есть логика, которая если не получилось установить коннект на первый IP в DNS ответе, то устанавливает на второй.

    Если приложение не умеет это делать, то будет возвращена ошибка. 

  • http://ystartsev.wordpress.com Yegor Startsev

    Сергей, неверный вывод из увиденного. В случае DNS-сервера на базе Windows он выдает адреса циклически переставляя их в ответе. Без вариантов. И это вчера проговаривалось.

  • Stanky

    На самом деле, "выход" из данной ситуации, прост как три копейки — если сервер упал всерьёз и надолго, необходимо удалить запись о нём из DNS'а.

  • Сергей Иванов

    "В случае DNS-сервера на базе Windows он выдает адреса циклически переставляя их в ответе. Без вариантов. И это вчера проговаривалось."- это понятно RR включён. Ну тогда флушить до посинения 🙂
    Короче, технология старая, поэтому все серьёзные приложения наверно уже переписали на перебор.

  • Евгений

    <<Но ни одна из технологий не идет в сравнение с балансировщиками L3. Но тогда мы теряем одну из прелестей – авторизацию по IP>>
    а где про это почитать, я имею ввиду про ограничения, и сравнения L3 балансировщика и обычного виндового NLB. Я предполагал (но еще не вчитывался) железяка всё закроет, а оказывается есть ограничения.

  • Дмитрий

    Забавная получилась у вас встреча. 🙂 К сожалению только сегодня просмотрел.
    Меня как и многих заинтриговало рассуждение на тему кто же разрешает вопрос с выборкой рабочего хоста из предоставленного DNS списка ip-адресов — потому что, я знаю правильный ответ на этот вопрос. 🙂
    Понимаю, что несколько запоздало и уже многие высказались по этому поводу в правильном направлении, хочется поставить все точки над "i" и пресечь неверное толкование вопроса.
    В действительности приложение само должно найти рабочий хост из списка полученных адресов от ОС.
    Рассмотрим пример на C.
    Беру уровень Berkeley реализации сокетов, дабы избежать какой-либо путаницы, как в случае с .NET.
    Сопоставление имен узлов и ip-адресов выполняет функция gethostbyname.
    struct hostent * gethostbyname(const char * hostname);
    При успешном выполнении она возвращает указатель на структуру hostent, содержащую все адреса для узла.
    struct hostent
    {
    char *h_name; // официальное (каноническое) имя узла
    char **h_aliases; // указатель на массив указателей на псевдонимы
    int h_addrtype; // тип адреса узла: AF_INET
    int h_length; // длина адреса: 4
     char **h_addr_list; // указатель на массив указателей с адресами IP
    }

    Клиент test_dns.cpp — получает список адресов от ОС, пытается подключиться на каждый к порту 80:

    #include <WinSock2.h>
    #include <Ws2tcpip.h>
    #include <stdio.h>
    #include <string.h>

    #pragma comment(lib, "Ws2_32.lib")

    int main(int argc, char** argv)
    {
    // Declare variables

    WSADATA wsaData;
    char *ptr, **pptr;
    struct in_addr **ppt;
    struct in_addr *inetaddrp[2];
    struct in_addr inetaddr;
    sockaddr_in servaddr;
    char str[16];
    struct hostent *hptr;
    SOCKET sockfd = INVALID_SOCKET;
    int index = 0, iResult;
    // Initialize Winsock
    iResult = WSAStartup( MAKEWORD(1,0), &wsaData );
    if (argc < 2)
    {
    printf("usage: test_dns_answer <hostname> \n");
    return 0;
    }
    // Функция gethostbyname вызывается для каждого аргумента командной строки
    while (—argc > 0)
    {
    ptr = *++argv;
    if ( (hptr = gethostbyname(ptr)) == NULL)
    {
    if (inet_addr(ptr) == 0)
    {
    printf("gethostbyname error for host: %s\n", ptr);
    return 0;
    }
    else
    {
    inetaddrp[0] = &inetaddr;
    inetaddrp[1] = NULL;
    ppt = inetaddrp;
    }
    }
    else ppt = (struct in_addr **) hptr->h_addr_list;
    // Выдается каноническое имя узла, за которым идёт список alias'ов
    printf("official hostname: %s\n", hptr->h_name);
    for (pptr = hptr->h_aliases; *pptr != NULL; pptr++)
    printf("\talias: %s\n", *pptr);

    switch (hptr->h_addrtype)
    {
    case AF_INET:
    pptr = hptr->h_addr_list;
    // Выводим каждый полученный ip-адрес
    for ( ; *pptr != NULL; pptr++)
    {
    index++;
    printf("\taddress %d: %s\n",
    index, inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
    }
    break;
    default:
    printf("unknown address type\n");
    break;
    }
    index = 0;
    //Пытаемся подключиться к 80-му порту каждого полученного ip-адреса
    for ( ; *ppt != NULL; ppt++)
    {
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(80);
    memcpy(&servaddr.sin_addr, *ppt, sizeof(struct in_addr));
    printf("trying connect to address: %s",
    inet_ntoa(servaddr.sin_addr));

    if (connect(sockfd, (SOCKADDR*) &servaddr, sizeof(servaddr)) == 0) printf(" — success\n");
    else printf("\tconnect error\n");
    closesocket(sockfd);
    }
    }
    return 0;
    }

    Результатом выполнения  программы для хоста ya.ru будет следующее:
    C:\test_dns1.exe" ya.ru
    official hostname: ya.ru
    address 1: 93.158.134.3
    address 2: 213.180.204.3
    address 3: 77.88.21.3
    address 4: 87.250.250.3
    address 5: 87.250.251.3
    trying connect to address: 93.158.134.3 — success
    trying connect to address: 213.180.204.3 — success
    trying connect to address: 77.88.21.3 — success
    trying connect to address: 87.250.250.3 — success
    trying connect to address: 87.250.251.3 — success

    Как видим из примера — система нам возвращает только список ip-адресов, но находить доступный наше приложение должно самостоятельно.

  • Stanky

    Спасибо, Дмитрий 🙂 !

    На самом деле, чуть выше по комментариям мы уже "докопались" до правды, что в случае с сокетами это обязанность клиента, а в случае .NET'а это уже реализовано внутри. Но тем не менее спасибо 😉 .

  • http://www.exchangerus.ru Pavel Nagaev

    А обсуждение то какое было.

     

    Дмитрий, спасибо большое за ответ, Вас бы на встречу, то копий бы сломали меньше 🙂