Я раньше работал с сетью только через интерфейсы, предоставляемые компонентами. Переживаю сейчас болезненный переход на работу с сокетами через API-интерфейсы. И при этом натыкаюсь на какую-то ерунду. Установил обработчики сообщений из сети в оконной процедуре в серверской части. В МСДН, да и в статье Socket vs Socket здесь на сайте написано, что при вызове accept в обработчике сообщения FD_ACCEPT порождается новый сокет, но TCP-view (sysinternals) показывает создание нового сокета и успешную установку соединения и БЕЗ вызова accept, а просто по событию FD_ACCEPT! Почему? Получается, что accept служит только для реэнейбла нотификации о сетевых сообщениях, а к порождению нового сокета не имеет отношения? И еще... если вызвать closesocket для сокета, созданного с помощью socket, то сокет, порожденный во время события FD_ACCEPT тоже уничтожается (по крайней мере так показывает TCP-view). Я чего-то не досмотрел или об этом тоже нигде ничего не сказано? И пока последний вопрос: можно ли как-нибудь разбиндить сокет или вывести его из слушающего состояния или сокеты... они одноразового использования, т.е. закрывать и создавать новый?
Есть, просто ты думалку не включил Новый сокет порождается до прихода FD_ACCEPT. Когда же приходит FD_ACCEPT, то "рукопожатие" уже прошло, тебе говорят мол, пришёл новый коннект, забирай сокет из ядра accept'ом. Думаю, теперь тут всё ясно?
nester7 Это цитата Вашего сообщения из той темы: Т.е. это я прочитал еще там. А вот цитата из МСДН: Вопрос был в том, неправда ли написана в МСДН, или здесь нету противоречия между действительностью и тем, что написано в МСДН? (наверное опять "думалку не включил" , но вижу противоречие) Так он действительно уничтожается и необходимости в closesocket для дескриптора, полученного через accept, нету? И... что насчет разбиндивания сокета (последний вопрос из первого поста)? Забыл еще спросить: а как мне отказаться-то от установки соединения, если . Может я и не хотел очередное соединение устанавливать.
Это уже вопрос реализации - когда выделять память пол полную структуру сокета. Connection уже есть и оно закрывается после вызова closesocket() на слушающем сокете. Тут я малость сам загнался не в ту сторону, тут имелось ввиду закрытие ассепнутого сокета. Давай вспомним асинхронный AcceptEx(), который требует созданного, но не прибиндиного сокета: Собственно, это тоже вопрос реализации - делать коннект отдельно от сокета и потом их связвать или всё в одной структуре. Как именно сделано под виндовс сказать не могу, нужно книжек читнуть Но на вопросы-то ответы уже найдены?
Насколько знаю, после listen() только closesocket(). А перебиндивать можно - это же просто установка адресов и айпишников для будующих соединений. Бинчить божно и обычные сокеты, но обычно это доверяется операционке. Под *nix точно можно реюзать обычные сокеты (not listen). Под windows - вроде нет, closesocket(), потом socket(), но нужно получше проштудировать MSDN и собсно потестить, думаю, что-то можно придумать
Эээ, не вкурил Если ты вызывал connect(socket), то как мог не хотеть его устанавливать? Если про "обычный" accept(), то проверять адреса подключений и удалять, но можно и через WSAAccept() отфильровать в обработчике.
nester7 Код (Text): Это уже вопрос реализации - когда выделять память пол полную структуру сокета. Connection уже есть и оно закрывается после вызова closesocket() на слушающем сокете. Так под созданием сокета понимается собственно не создание сокета, а только довыделение ресурсов под него? Т.е. в МСДН таки неправда? Да нет. Еще раз вопросы: 1) вопрос выше в этом посте. 2) Я так и не понял, нужно ли вызывать closesocket для дескриптора, полученного через accept, или достаточно закрыть сокет, который я создавал через socket. TCP-view, показывает, что оба сокета исчезают только по closesocket для дескриптора, полученного через socket. Верить ему? Или это зависит от реализации и общего стандарта нету? 3) можно ли разбиндить сокет или вывести его из слушающего состояния или нужно вызывать closesocket и создавать новый незабинденный? 4) У меня сокет на сервере в состоянии listen. Я выполняю accept для одного подключения, но не хочу, чтобы устанавливались новые. Что для этого сделать? Первый-то сокет остается в состоянии listen и, как выяснилось, винде по барабану, аксепчу я новое соединение или нет, а оно все равно устанавливается.
nester7 Спасибо. Вопрос 3 снимаю. Насчет вопроса 4: Т.е. хочу я или не хочу, а соединение все равно установится и только после его установки я могу его разорвать через closesocket? Об этом я читал в вашем посте в теме, ссылку на которую Вы дали.
Обычными средствами вроде никак не запретишь. Мыслей пока что две: - закрывать listen-сокет, а потом его снова создавать/бандить/прислушивать если нужно снова принимать подключения и - сделать listen(socket, 0), но сомневаюсь что прокатит, ибо эта listen-очередь для того и существует, чтобы максимально быстро обрабатывать коннекты от клиентов для высоконагруженных серверов: операционка "заботится" о тебе, ставя их в очередь. Ещё какие-то способы нужно рыть/искать и пробовать, тут я пасс пока что. Давай на "ты", я не тот гусь, чтобы мне "Вы"
Так это вопрос номер 2. У меня по закрытию уже установленное соединение обрывается. Извиняюсь. Привычка.
nester7 Похоже это был мой прогон... хотя странно. В общем, кажется, разобрался. Хотя раньше, кажется, именно так и получалось, но теперь вроде это неправда. Оба сокета исчезают по closesocket только в том случае, если я еще не успел сделать accept для входящего соединения. Иначе исчезает только слушающий сокет, что вполне логично. И тогда вопросы 2 и 4 отпадают. Остается вопрос номер 1.
Возвращение домой немного затянулось приёмом пива, чего и вам, ребят, желаю %) Насчёт стандарта хз - есть RFC по TCP/IP, а каждый реализует его (стек TCP/IP) по-своему. А по поводу нужен ли closesocket() для принятых подключений или нет: Код (Text): #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32") #define SRV_PORT 65000 int main( int argc, char* argv[]) { SOCKET srvsock; SOCKET clisock; SOCKET clisock2; struct sockaddr_in local_addr; struct sockaddr_in client_addr; struct sockaddr_in client_addr2; WSADATA WSAData ; int client_addr_size = sizeof(client_addr); int n; if (WSAStartup(0x0202,(WSADATA *) &WSAData)) { printf("[--] error WSAStartup() %d\n", WSAGetLastError()); return (-1); } if ((srvsock = socket(AF_INET,SOCK_STREAM,0)) < 0) { printf("[--] error socket() %d\n", WSAGetLastError()); WSACleanup(); // Вай-вай какие мы правильные :) return (-1); } local_addr.sin_family = AF_INET; local_addr.sin_port = htons(SRV_PORT); local_addr.sin_addr.s_addr = 0; if (bind(srvsock, (struct sockaddr*) &local_addr, sizeof(local_addr))) { printf("[--] error bind() %d\n", WSAGetLastError()); closesocket(srvsock); WSACleanup(); return (-1); } if (listen(srvsock, 0)) { // Заодно и на нолик листен проверим. printf("[--] error listen() %d\n", WSAGetLastError()); closesocket(srvsock); WSACleanup(); return (-1); } if ((clisock = accept(srvsock, (struct sockaddr*) &client_addr, &client_addr_size)) != INVALID_SOCKET ) { printf("%s\n", "[++] accepted."); } else { printf("[--] error accept() %d\n", WSAGetLastError()); closesocket(srvsock); WSACleanup(); return (-1); } closesocket(srvsock); Sleep(10 * 1000); if ((clisock2 = accept(srvsock, (struct sockaddr*) &client_addr2, &client_addr_size)) != INVALID_SOCKET ) { printf("%s\n", "[++] accepted2!"); } else { printf("[--] error accept() %d\n", WSAGetLastError()); // WSACleanup(); // return (-1); // Тут выходить не будем. } n = send(clisock, (const char*) &client_addr_size, sizeof(client_addr_size), 0); printf("[!!] n = %d, WSALastError() = %d\n", n, WSAGetLastError()); n = recv(clisock, (const char*) &client_addr_size, sizeof(client_addr_size), 0); printf("[!!] n = %d, WSALastError() = %d\n", n, WSAGetLastError()); return (0); } Кмпилим и запускаем: Код (Text): 1) C:\temp\listen.exe 2) [++] accepted. 3) [--] error accept() 10038 (Я: Socket operation on nonsocket!) 4) [!!] n = 4, WSALastError() = 0 5) _ В строке 1) идёт запуск "сервера", после чего на другом компьютере делаются два сеанса telnet server 65000. Появляется строка 2), второй сеанс сразу же при этом закрывается и появляется трока 3), тоесть ассептить больне низя наразу (но всётаки можно изначально, невзирая на listen(, 0) ), потом срока 4) и висим на recv() (строка 5), то есть сокет жив и будет жить Если бы он закрылся, то мы получили бы 10038. Вывод: нужно явно закрывать. Что собственно логично - закрывая листен-сокет мы отрубаем возможность принимать подключения, но зачем при этом закрывать существующие? Вот они и живут.
Не, создание сокета и есть выделение ресурсов под него, любой объект ОС - это структура, под которую выделяется память и которая инициализируется нужным способом (для файла - текущие указатели, для сокета - буферы приема и передачи, и т.п.). И что значит "довыделение"? Думаю, что правда. "Соединение" - это, видимо, отдельный объект (адреса и порты), который связывается с сокетом в момент accept'a или коннекта (отдельные же действия socket() и connect()!), и в том же AcceptEx'e мы выдаем запросы с "голым" сокетом (not bound or connected) и получаем в случае успеха приконнекченый сокет, но "соединение" не может быть отвязано от сокета и привязано к другому, соответственно, закрывая сокет ОС закрывает и соединение, связанное с сокетом.
Так я еще в посте 14 с этим разобрался. Что-то я не понимаю, нафига такое соединение, к которому нету интерфейсов работы (в данном случае в виде сокета). Лучше было бы, что нет сокета - нет соединения. Соответственно accept создает сокет и соединение устанавливается. А так получается, что соединение повисает в воздухе, пока я не выполню accept... глупо. Кроме того: TCP View отображает "TCP and UDP endpoints". Я так понимаю, что под этим подразумеваются именно сокеты, а не соединения (хотя, может как раз соединения, тогда все ясно). Так вот в списке появляется очередной сокет еще до того, как я вызову accept. Хотя в МСДН написано, что именно accept создает новый сокет.
Почему глупо? Для соединения нужно не так много информации - адреса пиров (endpoints), TCP/IP seq's (что там ещё у нас?) и несколько указателей, чтобы этим хозяйством управлять, в то время как для сокета нужно два буфера (SND и RCV), которые не так уж и мало весят (32К вроде по дефолту) - задосить систему было бы куда проще в таком случае. [added] Ну а где противоречие? В AcceptEx() мы его сами делаем и отдаём для "подконнекчивания", в обычном аccept() мы никаких сокетов не передаем, поэтому ей приходится делать это за нас и возвращать нам его, что бы мы могли с ним работать. Документация не врёт [added2]
Да. Но ее было бы сложнее "задосить", если бы при запросе на соединение вообще ничего не создавалось... ни соединения, ни сокета... флаг какой-нибудь, который дает знать, что есть запрос... да и все. А тот, кто пытается "задосить", пусть ждет таймаута, чтобы узнать, что я его запрос не эксептил. Но собственно с вопросом я разобрался... точнее Вы меня разобрали. Как это где? TCP View говорит, что сокет УЖЕ создан. А МСДН говорит, что сокет только БУДЕТ создан, когда я вызвову accept. Собственно я понял, что Вы хотели сказать. В общем в TCP View все-таки не сокеты показаны, а хм... не знаю, что это такое ... пиры ... ну буду считать, что соединения. Меня ввела в заблуждения фраза из местной статьи (http://www.wasm.ru/article.php?article=socketvssocket): Хотя на самом деле там отслеживается эффект создания нового соединения... причем не зависимо от того, была выполнена функция accept или нет. Премного благодарен за подробные разъяснения по теме.
из книги: Он не сокеты показывает, а соединения с хостами из ядра добытые. Флагом не обойтись - слишком мало информации, поэтому создаются полные блоки для 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.