Несколько вопросов про Windows Sockets 2

Тема в разделе "WASM.NETWORKS", создана пользователем s3dworld, 30 авг 2011.

  1. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Всем доброго утра!

    Хочу уточнить несколько деталей при работе с Windows Sockets 2. В первую очередь хочу проконсультироваться по поводу ошибок и как вести себя программе, в случае их возникновения. Сразу весь список вопросов писать не буду, буду выкладывать последовательно. И так, приступим!

    Представим себе такую ситуацию. В системе имеется одна сетевая карта. Компьютер подключён к Интернету через PPPoE. Я запускаю программу и она инициализирует работу с библиотекой Windows Sockets 2 через функцию WSAStartup(). Далее она создаёт два TCP-сокета и обоих подключает через connect() к разным серверам (кстати, до подключения не используется связывание через функцию bind(), так как в случае клиентского сокета, связывание произойдёт автоматически при подключении). Далее уже программа отправляет/принимает данные через эти сокеты двум разным серверам. И на одном из сокетов происходит ошибка в результате выполнения одной из функций Windows Sockets 2. Функция вернула SOCKET_ERROR. Через WSAGetLastError() я получаю код ошибки. Он равен WSAENETDOWN. Пропустим это сообщение через FormatMessage(), я получу:

    Код (Text):
    1. Операция на сокете обнаружила отключение сети.
    Что делать программе? Понятное дело что данному сокету пришёл конец. Вопрос в следующем: можно смело завершать работу с двумя сокетами? Просто я не понимаю суть ошибки. Означает ли она что разорвано соединение с Интернетом или там что-то с сетевой картой стало. Я ведь специально привёл пример с одной сетевой картой. Было бы у меня их две и сокеты прикреплены к разным картам, тогда уже можно было бы продолжить работу другого сокета?

    В чём соль: по сути можно ждать ошибку для каждого сокета. Но я пишу для себя сетевой движок для работы с TCP-сокетами. Он уже практически написан, осталось лишь правильно обрабатывать ошибки. И если пользователь создаст 20 сокетов, я не хочу чтобы ему приходило 20 раз сообщение для каждого сокета, когда можно было бы объединить все эти сообщения в одно - типа накрылась сеть.

    Что вообще означает данная ошибка?
     
  2. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    При разрыве единственного PPPoE соединения или даже при физическом падении единственной сетевухи, на которой IP-адреса непосредственно, происходят сразу две ошибки:
    1). перестает существовать локальный адрес привязки сокета, от имени которого он обращался наружу.
    2). исчезает способ достижения места назначения, поскольку из таблицы активных маршрутов исчезат как default gateway, так и даже connected-сеть (что имеет значение в том случае, когда место назначения прямо тут, в локалке).

    Это некорректное понятие. В любом случае у вас есть loopback-интерфейс, который никогда не накрывается. Соединение может быть установлено от клиента с лупбэка на сервер на этой машине на лупбэке же, и в таком случае сетевые карты пофиг.
    Но и помимо этого, даже через одну физическую сетевую карту может быть как несколько VLAN-субинтерфейсов (если таковое поддерживает драйвер сетевухи), так и несколько одновременных PPPoE-соединений (встроенное вроде не позволяет, но при установленном RASPPPOE такое вполне возможно) или несколько PPTP-соединений (если работает нижележащий транспорт). Или, например, даже при одной сетевухе и одном PPPOE-соединении через нее у вас уже есть два сетевых интерфейса (помимо лупбэка), причем оба вполне функционирующих, и вполне сокеты могут оказаться на разных интерфейсах, скажем, в случае, если один сервак прямо в локалке, а другой - в интернете, доступном через PPPOE. Если происходит падение PPPOE, то у сокета, работающего с сервером в локалке, не происходит ни одной из вышеперечисленных ошибок.

    Так что случай только с одним интерфейсом - оооочень частный, поэтому лучше работать с каждым сокетом индивидуально.
     
  3. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    Спасибо большое!
     
  4. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    Но по крайней мере, если я являюсь сервером и у меня произошла ошибка WSAENETDOWN на слушающем сокете, либо на любом из сокетов клиента, который слушающий сокет принял, то мне можно смело закрывать слушающий сокет и все сокеты клиента, которые он принял?
     
  5. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    Если упал слушающий сокет, то да, все сокеты передачи данных также станут нерабочими. Но вообще система вас известит о каждом сокете индивидуально. Поскольку вам в любом случае индивидуальные извещения надо обрабатывать (на случай отвала конкретного клиента), то нет смысла делать еще и обработку общего.
     
  6. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    Я просто не могу правильно продумать архитектуру. Смысл такой, моя движок будет позволять создавать множество сокетов (ограничение накладывает только ОС и сама библиотека Windows Sockets 2):

    Код (Text):
    1. Result CreateServer(...);
    2. Result CreateClient(...);
    Пользователь может создать сколько угодно сервером и клиентов. Если он создал, например 2 клиента, и какой-то из них рухнул, то я не буду проверять второй сокет рухнул ли он. Здесь система сама известит и я пользователю это предоставлю узнать.

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

    В общем при рушении одного из сокетов сервера, можно смело остальные рушить и писать пользователю что весь сервер накрылся. Правильно?
     
  7. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    Я не могу ответить на ваш вопрос, поскольку не знаю, что инкапсулирует в себе ваш объект "Сервер", и как пользователь работает с этим объектом, в особенности, что и как делает пользователь объекта "Сервер" с конкретными подключениями к данному серверу.
     
  8. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    Когда пользователь создаёт клиента (CreateClient()), то он просто получает указатель на созданный экземпляр класса по работе с сокетом для клиентских нужд. В качестве возможностей входит:

    Код (Text):
    1. Result Start(...);
    2. Result Bind(...);
    3. Result Connect(...);
    4. Result Send(...);
    5. Result Recv(...);
    6. Result Stop(...);
    А в функцию Connect() будут передаваться адреса на функции обратного вызова при наступлении следующих событий:

    Код (Text):
    1. DISCONNECT - отключён сервером
    2. DISCONNECT_ERROR - отключён в результате ошибки
    3. MESSAGE - пришло сообщение от сервера
    4. ERROR - проблема с сетью (как раз этот же WSAENETDOWN)
    Вот пришло сообщение MESSAGE, значит пользователь может вызвать Recv() для данного клиента. Если пришло ERROR, то только данный клиент рушится (остальные клиенты и сервера работают).

    Работа с сервером аналогичная (CreateServer()):

    Код (Text):
    1. Result Start(...);
    2. Result Bind(...);
    3. Result Listen(...);
    4. Result Stop(...);
    А в функцию Listen() будут передаваться адреса на функции обратного вызова при наступлении следующих событий:

    Код (Text):
    1. CONNECT - подключился клиент
    2. DISCONNECT - отключился клиент
    3. DISCONNECT_ERROR - отключился клиент в результате ошибки
    4. MESSAGE - пришло сообщение от клиента
    5. ERROR - проблема с сетью (как раз этот же WSAENETDOWN)
    Ну и сам сервер не будет через этот интерфейс общаться с клиентами. При наступлении события CONNECT будет создан новый экземпляр класса Client с заполненными переменными и возвращён на него управление. И уже сервер через указатель на клиента будет с ним производить операции:

    Код (Text):
    1. Result Send(...);
    2. Result Recv(...);
    3. Result Stop(...);
    И если на сервер придёт сообщение MESSAGE, то в качестве параметра будет передан указатель на экземпляр объекта Client, куда поступило сообщение. В случае если любой из сокетов сервера (слушающий сокет сервера, сокеты, созданные accept() слушающим сокетом) вернёт WSAENETDOWN, то я закрываю все сокеты данного сервера и сам слушающий сокет. Верно?
     
  9. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    Если так
    то
    да.
     
  10. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    Спасибо!

    Сейчас на ум пришло сразу несколько ситуаций:

    1. Если я один раз вызвал WSAStartup() с успешным завершением, то в случае же каких-то неполадок в сети, но не разу не вызывал WSACleanup(), мне может потребоваться ещё раз вызвать WSAStartup()? По сути я думал что это просто инициализация работы с библиотекой, а не с сетевыми устройствами, но увидев в функции WSACleanup() возможную ошибку WSAENETDOWN, меня это озадачило. Получается что раз сеть "полетела", то я не могу вызвать правильно WSACleanup(). Следовательно если мне пришла где-то WSAENETDOWN, то для работы дальше с сетью мне нужно заново вызывать WSAStartup() что ли?! В общем не совсем понятна ситуация. Поясните.

    2. Множество функций Windows Sockets возвращают ошибку, по причине лимита каких-то объектов. Например слишком много процессов используют библиотеку Windows Sockets или же достигнут предел на количество сокетов в системе. Это всё понятно и в случае такой ошибки программе лучше проинформировать пользователя, чтобы он позакрывал лишние программы. А вот есть такая ошибка WSAENOBUFS (Невозможно выполнить операцию на сокете, т.к. буфер слишком мал или очередь переполнена). О каком буфере идёт речь? Не те ли самые SO_RCVBUF/SO_SNDBUF для setsockopt()? Что вообще программе нужно делать при наступлении такой ошибки?

    3. Если при вызове функции connect() возвращается ошибка WSAETIMEDOUT, то есть за отведённое время сервер не ответил или ответил не правильно - соединение не установлено. И в функциях recv()/send() тоже есть такая ошибка. Будет ли это для них значит что настал конец соединение или просто нужно повторить операцию позже?
     
  11. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    1. Не надо вам повторно вызывать WSAStartup(). Достаточно вначале один раз. WSAENETDOWN в WSACleanup() обозначает серьезную ошибку сети, скажем, неработоспособность вообще стека протоколов по каким-то внутренним причинам и дальнейшая работа с сетью вообще невозможна, рестартовать бесполезно.

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

    3. Для send/recv получение WSAETIMEDOUT обозначает, что соединение было разорвано некорректно из-за того, что другая сторона перестала отвечать не прислав запрос завершения соединения (где-то по пути возникли проблемы с сетью или на другой стороне произошел какой-то серьезный сбой, скажем, выключение питания).
     
  12. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    ОГРОМНОЕ СПАСИБО!

    Если будут вопросы, надеюсь Вы поможете.
     
  13. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Я сейчас ещё раз посмотрел возможные причины ошибки для функций connect() и accept() и обнаружил что там тоже есть WSAENOBUFS. А для них она в каких случаях может прийти и что делать если пришла?
     
  14. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
  15. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Всем доброго дня!

    Создал сокет:

    Код (Text):
    1. SOCKET entitySocket=0;
    2.  
    3. entitySocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    Связывать его не обязательно, он не будет слушающим. Далее подключаюсь:

    Код (Text):
    1. sockaddr_in socketAddress;
    2.  
    3. memset(&socketAddress,0,sizeof(sockaddr_in));
    4.  
    5. socketAddress.sin_family=AF_INET;
    6. socketAddress.sin_addr.S_un.S_addr=inet_addr("46.33.224.184");
    7. socketAddress.sin_port=htons(27015);
    8.  
    9. if(connect(entitySocket,(sockaddr*)&socketAddress,sizeof(sockaddr_in))==SOCKET_ERROR)
    10. {
    11.     result=WSAGetLastError();
    12.  
    13.     ....
    14. }
    Получаю ошибку номер 10035. По коду получаю описание: "Операция на незаблокированном сокете не может быть завершена немедленно." - то есть ошибка WSAEWOULDBLOCK.

    Код (Text):
    1. Resource temporarily unavailable.
    2.  
    3. This error is returned from operations on nonblocking sockets that cannot be completed immediately,
    4. for example recv when no data is queued to be read from the socket. It is a nonfatal error, and the
    5. operation should be retried later. It is normal for WSAEWOULDBLOCK to be reported as the result
    6. from calling connect on a nonblocking SOCK_STREAM socket, since some time must elapse for the
    7. connection to be established.
    Но сокет у меня блокируемый. Тогда почему полезла эта ошибка и что она значит? Что программе делать дальше?
     
  16. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Всё, разобрался!