Азы работы с сокетами.

Тема в разделе "WASM.BEGINNERS", создана пользователем l_inc, 18 окт 2007.

  1. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Я раньше работал с сетью только через интерфейсы, предоставляемые компонентами. Переживаю сейчас болезненный переход на работу с сокетами через API-интерфейсы. И при этом натыкаюсь на какую-то ерунду.
    Установил обработчики сообщений из сети в оконной процедуре в серверской части. В МСДН, да и в статье Socket vs Socket здесь на сайте написано, что при вызове accept в обработчике сообщения FD_ACCEPT порождается новый сокет, но TCP-view (sysinternals) показывает создание нового сокета и успешную установку соединения и БЕЗ вызова accept, а просто по событию FD_ACCEPT! Почему? Получается, что accept служит только для реэнейбла нотификации о сетевых сообщениях, а к порождению нового сокета не имеет отношения?
    И еще... если вызвать closesocket для сокета, созданного с помощью socket, то сокет, порожденный во время события FD_ACCEPT тоже уничтожается (по крайней мере так показывает TCP-view). Я чего-то не досмотрел или об этом тоже нигде ничего не сказано?
    И пока последний вопрос: можно ли как-нибудь разбиндить сокет или вывести его из слушающего состояния или сокеты... они одноразового использования, т.е. закрывать и создавать новый?
     
  2. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    http://www.wasm.ru/forum/viewtopic.php?id=21601
     
  3. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Да это все я уже читал. Нету там ответов на мои вопросы.
     
  4. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Есть, просто ты думалку не включил :)

    Новый сокет порождается до прихода FD_ACCEPT. Когда же приходит FD_ACCEPT, то
    "рукопожатие" уже прошло, тебе говорят мол, пришёл новый коннект, забирай
    сокет из ядра accept'ом.


    Думаю, теперь тут всё ясно?
     
  5. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    nester7
    Это цитата Вашего сообщения из той темы:
    Т.е. это я прочитал еще там. :) А вот цитата из МСДН:
    Вопрос был в том, неправда ли написана в МСДН, или здесь нету противоречия между действительностью и тем, что написано в МСДН? (наверное опять "думалку не включил" :), но вижу противоречие)
    Так он действительно уничтожается и необходимости в closesocket для дескриптора, полученного через accept, нету?
    И... что насчет разбиндивания сокета (последний вопрос из первого поста)?

    Забыл еще спросить: а как мне отказаться-то от установки соединения, если
    . Может я и не хотел очередное соединение устанавливать.
     
  6. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Это уже вопрос реализации - когда выделять память пол полную структуру сокета.
    Connection уже есть и оно закрывается после вызова closesocket() на слушающем сокете.

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

    Давай вспомним асинхронный AcceptEx(), который требует созданного, но не прибиндиного сокета:

    Собственно, это тоже вопрос реализации - делать коннект отдельно от сокета и потом их связвать
    или всё в одной структуре. Как именно сделано под виндовс сказать не могу, нужно книжек читнуть :)


    Но на вопросы-то ответы уже найдены?
     
  7. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Насколько знаю, после listen() только closesocket(). А перебиндивать можно - это же просто установка адресов
    и айпишников для будующих соединений. Бинчить божно и обычные сокеты, но обычно это доверяется операционке.

    Под *nix точно можно реюзать обычные сокеты (not listen). Под windows - вроде нет, closesocket(), потом socket(), но нужно
    получше проштудировать MSDN и собсно потестить, думаю, что-то можно придумать :)
     
  8. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Эээ, не вкурил :)
    Если ты вызывал connect(socket), то как мог не хотеть его устанавливать?
    Если про "обычный" accept(), то проверять адреса подключений и удалять,
    но можно и через WSAAccept() отфильровать в обработчике.
     
  9. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    nester7
    Код (Text):
    1. Это уже вопрос реализации - когда выделять память пол полную структуру сокета.
    2. Connection уже есть и оно закрывается после вызова closesocket() на слушающем сокете.
    Так под созданием сокета понимается собственно не создание сокета, а только довыделение ресурсов под него? Т.е. в МСДН таки неправда?
    Да нет. Еще раз вопросы:
    1) вопрос выше в этом посте. :)
    2) Я так и не понял, нужно ли вызывать closesocket для дескриптора, полученного через accept, или достаточно закрыть сокет, который я создавал через socket. TCP-view, показывает, что оба сокета исчезают только по closesocket для дескриптора, полученного через socket. Верить ему? Или это зависит от реализации и общего стандарта нету?
    3) можно ли разбиндить сокет или вывести его из слушающего состояния или нужно вызывать closesocket и создавать новый незабинденный?
    4) У меня сокет на сервере в состоянии listen. Я выполняю accept для одного подключения, но не хочу, чтобы устанавливались новые. Что для этого сделать? Первый-то сокет остается в состоянии listen и, как выяснилось, винде по барабану, аксепчу я новое соединение или нет, а оно все равно устанавливается.
     
  10. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    nester7
    Спасибо. Вопрос 3 снимаю. :)
    Насчет вопроса 4:
    Т.е. хочу я или не хочу, а соединение все равно установится и только после его установки я могу его разорвать через closesocket?
    Об этом я читал в вашем посте в теме, ссылку на которую Вы дали. :)
     
  11. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Обычными средствами вроде никак не запретишь. Мыслей пока что две:
    - закрывать listen-сокет, а потом его снова создавать/бандить/прислушивать если нужно снова принимать подключения и
    - сделать listen(socket, 0), но сомневаюсь что прокатит, ибо эта listen-очередь для того и существует,
    чтобы максимально быстро обрабатывать коннекты от клиентов для высоконагруженных серверов: операционка
    "заботится" о тебе, ставя их в очередь.


    Ещё какие-то способы нужно рыть/искать и пробовать, тут я пасс пока что.

    Давай на "ты", я не тот гусь, чтобы мне "Вы" :)
     
  12. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Так это вопрос номер 2. У меня по закрытию уже установленное соединение обрывается.
    Извиняюсь. Привычка.
     
  13. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Чорт, уже туплю :)
    До дома доберусь с работы - покумекую ещё.
     
  14. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    nester7
    Похоже это был мой прогон... хотя странно. В общем, кажется, разобрался.
    Хотя раньше, кажется, именно так и получалось, но теперь вроде это неправда. Оба сокета исчезают по closesocket только в том случае, если я еще не успел сделать accept для входящего соединения. Иначе исчезает только слушающий сокет, что вполне логично. И тогда вопросы 2 и 4 отпадают. Остается вопрос номер 1.
     
  15. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Возвращение домой немного затянулось приёмом пива,
    чего и вам, ребят, желаю %)

    Насчёт стандарта хз - есть RFC по TCP/IP, а каждый реализует его (стек TCP/IP) по-своему.

    А по поводу нужен ли closesocket() для принятых подключений или нет:

    Код (Text):
    1. #include <winsock2.h>
    2. #include <windows.h>
    3.  
    4. #pragma comment(lib, "ws2_32")
    5.  
    6. #define SRV_PORT 65000
    7.  
    8.  
    9. int
    10. main(
    11.     int argc,
    12.     char* argv[])
    13. {
    14.  
    15.     SOCKET srvsock;
    16.     SOCKET clisock;
    17.     SOCKET clisock2;
    18.    
    19.     struct sockaddr_in local_addr;
    20.     struct sockaddr_in client_addr;
    21.     struct sockaddr_in client_addr2;
    22.    
    23.     WSADATA WSAData ;
    24.     int client_addr_size = sizeof(client_addr);
    25.     int n;
    26.    
    27.    
    28.  
    29.     if (WSAStartup(0x0202,(WSADATA *) &WSAData)) {
    30.         printf("[--] error WSAStartup() %d\n", WSAGetLastError());
    31.         return (-1);
    32.     }
    33.  
    34.     if ((srvsock = socket(AF_INET,SOCK_STREAM,0)) < 0) {
    35.         printf("[--] error socket() %d\n", WSAGetLastError());
    36.         WSACleanup(); // Вай-вай какие мы правильные :)
    37.         return (-1);
    38.     }
    39.  
    40.     local_addr.sin_family      = AF_INET;
    41.     local_addr.sin_port        = htons(SRV_PORT);
    42.     local_addr.sin_addr.s_addr = 0;
    43.    
    44.     if (bind(srvsock, (struct sockaddr*) &local_addr, sizeof(local_addr))) {
    45.         printf("[--] error bind() %d\n", WSAGetLastError());
    46.         closesocket(srvsock);
    47.         WSACleanup();
    48.         return (-1);
    49.     }
    50.  
    51.     if (listen(srvsock, 0)) { // Заодно и на нолик листен проверим.
    52.         printf("[--] error listen() %d\n", WSAGetLastError());
    53.         closesocket(srvsock);
    54.         WSACleanup();
    55.         return (-1);
    56.     }
    57.  
    58.  
    59.     if ((clisock = accept(srvsock, (struct sockaddr*) &client_addr, &client_addr_size)) != INVALID_SOCKET ) {
    60.         printf("%s\n", "[++] accepted.");
    61.     }
    62.     else {
    63.         printf("[--] error accept() %d\n", WSAGetLastError());
    64.         closesocket(srvsock);
    65.         WSACleanup();
    66.         return (-1);
    67.     }
    68.    
    69.     closesocket(srvsock);
    70.  
    71.     Sleep(10 * 1000);
    72.    
    73.     if ((clisock2 = accept(srvsock, (struct sockaddr*) &client_addr2, &client_addr_size)) != INVALID_SOCKET ) {
    74.         printf("%s\n", "[++] accepted2!");
    75.     }
    76.     else {
    77.         printf("[--] error accept() %d\n", WSAGetLastError());
    78.         // WSACleanup();
    79.         // return (-1);  // Тут выходить не будем.
    80.     }
    81.    
    82.     n = send(clisock, (const char*) &client_addr_size, sizeof(client_addr_size), 0);
    83.    
    84.     printf("[!!] n = %d, WSALastError() = %d\n", n, WSAGetLastError());
    85.    
    86.     n = recv(clisock, (const char*) &client_addr_size, sizeof(client_addr_size), 0);
    87.    
    88.     printf("[!!] n = %d, WSALastError() = %d\n", n, WSAGetLastError());
    89.  
    90.     return (0);
    91. }
    Кмпилим и запускаем:

    Код (Text):
    1. 1) C:\temp\listen.exe
    2. 2) [++] accepted.
    3. 3) [--] error accept() 10038 (Я: Socket operation on nonsocket!)
    4. 4) [!!] n = 4, WSALastError() = 0
    5. 5) _
    В строке 1) идёт запуск "сервера", после чего на другом компьютере делаются два сеанса
    telnet server 65000. Появляется строка 2), второй сеанс сразу же при этом закрывается и появляется
    трока 3), тоесть ассептить больне низя наразу (но всётаки можно изначально, невзирая на listen(, 0) ),
    потом срока 4) и висим на recv() (строка 5), то есть сокет жив и будет жить :)
    Если бы он закрылся, то мы получили бы 10038.

    Вывод:
    нужно явно закрывать. Что собственно логично - закрывая листен-сокет мы отрубаем возможность
    принимать подключения, но зачем при этом закрывать существующие? Вот они и живут.
     
  16. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Не, создание сокета и есть выделение ресурсов под него, любой объект ОС - это структура,
    под которую выделяется память и которая инициализируется нужным способом (для файла - текущие указатели,
    для сокета - буферы приема и передачи, и т.п.). И что значит "довыделение"?

    Думаю, что правда. "Соединение" - это, видимо, отдельный объект (адреса и порты),
    который связывается с сокетом в момент accept'a или коннекта (отдельные же действия socket() и connect()!),
    и в том же AcceptEx'e мы выдаем запросы с "голым" сокетом (not bound or connected) и получаем в случае
    успеха приконнекченый сокет, но "соединение" не может быть отвязано от сокета
    и привязано к другому, соответственно, закрывая сокет ОС закрывает и соединение, связанное с сокетом.
     
  17. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Так я еще в посте 14 с этим разобрался. :)
    Что-то я не понимаю, нафига такое соединение, к которому нету интерфейсов работы (в данном случае в виде сокета). Лучше было бы, что нет сокета - нет соединения. Соответственно accept создает сокет и соединение устанавливается. А так получается, что соединение повисает в воздухе, пока я не выполню accept... глупо.
    Кроме того:
    TCP View отображает "TCP and UDP endpoints". Я так понимаю, что под этим подразумеваются именно сокеты, а не соединения (хотя, может как раз соединения, тогда все ясно). Так вот в списке появляется очередной сокет еще до того, как я вызову accept. Хотя в МСДН написано, что именно accept создает новый сокет.
     
  18. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    Почему глупо? Для соединения нужно не так много информации - адреса пиров (endpoints),
    TCP/IP seq's (что там ещё у нас?) и несколько указателей, чтобы этим хозяйством управлять,
    в то время как для сокета нужно два буфера (SND и RCV), которые не так уж и мало
    весят (32К вроде по дефолту) - задосить систему было бы куда проще в таком случае.

    [added]
    Ну а где противоречие? В AcceptEx() мы его сами делаем и отдаём для "подконнекчивания",
    в обычном аccept() мы никаких сокетов не передаем, поэтому ей приходится делать это за нас
    и возвращать нам его, что бы мы могли с ним работать. Документация не врёт :)

    [added2]
     
  19. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Да. Но ее было бы сложнее "задосить", если бы при запросе на соединение вообще ничего не создавалось... ни соединения, ни сокета... флаг какой-нибудь, который дает знать, что есть запрос... да и все. А тот, кто пытается "задосить", пусть ждет таймаута, чтобы узнать, что я его запрос не эксептил. Но собственно с вопросом я разобрался... точнее Вы меня разобрали. :)
    Как это где? TCP View говорит, что сокет УЖЕ создан. А МСДН говорит, что сокет только БУДЕТ создан, когда я вызвову accept.
    Собственно я понял, что Вы хотели сказать. В общем в TCP View все-таки не сокеты показаны, а хм... не знаю, что это такое ... пиры ... ну буду считать, что соединения. Меня ввела в заблуждения фраза из местной статьи (http://www.wasm.ru/article.php?article=socketvssocket):
    Хотя на самом деле там отслеживается эффект создания нового соединения... причем не зависимо от того, была выполнена функция accept или нет.

    Премного благодарен за подробные разъяснения по теме.
     
  20. nester7

    nester7 New Member

    Публикаций:
    0
    Регистрация:
    5 дек 2003
    Сообщения:
    720
    Адрес:
    Russia
    из книги:
    Он не сокеты показывает, а соединения с хостами из ядра добытые.

    Флагом не обойтись - слишком мало информации, поэтому создаются полные блоки для ip протокола и tcp, а потом уже связываются с сокетом, либо по таймауту отваливаются если им никто не сделал accept().


    ещё по поводу буферов и стрруктур из той же книги:
    Non-paged pool is the portion of memory that is always resident in physical memory and can never be paged out. Kernel- mode operating system components, such as a driver, typically use the non-paged pool that includes Winsock and the protocol drivers such as tcpip.sys. Each socket created consumes a small portion of non-paged pool that is used to maintain socket state information. When the socket is bound to an address, the TCP/IP stack allocates additional non-paged pool for the local address information. When a socket is then connected, a remote address structure is also allocated by the TCP/IP stack. In all, a connected socket consumes about 2 KB of non-paged pool and a socket returned from accept or AcceptEx uses about 1.5 KB of non-paged pool (because an accepted socket needs only to store the remote address). In addition, each overlapped operation issued on a socket requires an I/O request packet to be allocated, which uses approximately 500 non-paged pool bytes.