Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 18.10.2012, 21:56   #1
LynXzp
Пользователь
 
Аватар для LynXzp
 
Регистрация: 04.10.2012
Сообщений: 95
По умолчанию Правильно закрыть WinAPI Socket

Не вопрос, а скорее утверждение, хотя если кто-то исправит, буду рад.
Цитата:
Есть программа которая скачивает миллионы файлов, так после ее работы Windows ведет себя как будто его побили, пожевали и выплюнули, то скачать файл не может, то flac не открывает, то в paint вставка не работает, то файл не копируется, один раз вместо фонового изображения картину оперативной памяти нарисовало (похоже на белый шум, но с явной периодической корреляцией). Даже после после ее закрытия ничего не приходит в порядок, хотя со временем может, но легче перезагрузится.
Вопрос как правильно закрывать сокет поднимался судя по интернета не раз, но ни одного "авторитетного" или очень аргументированного ответа я не нашел, пока случайно не наткнулся в книге "защищенный код" на:

Цитата:
Основная проблема в том, что даже после завершения работы приложения и закрытия всех описателей сокетов могут сохранится открытые TCP/IP-подключения на уровне операционной системы. Чтобы решить эту проблему, следует вызывать функцию shutdown для закрытия сокета, а затем функцию recv до тех пор, пока данных больше не останется или пока функция не вернет ошибку. После этого необходимо вызвать closesocket
P.S. Цитата из стр. 397 2-го издания книги "Защищенный код" книга Майкла Ховарда и Дэвида Лебланка, на счет этой книги Билл Гейтс сказал "Обязательное чтение для сотрудников Microsoft". И хотя детища Microsoft не считаются самыми защищенными, источник можно считать по крайней мере "авторитетным". (Цитата совершенно не по поводу истощения рес-в операционной системы, а по поводу оставшихся открытых сокетов, хотя они и были закрыты, но это как раз и решает предыдущую проблему)

Пример кода из моего сервера:
Код:
	shutdown(_Client,2);
	while(recv(_Client, _buff, 1024, 0)!=-1);
	closesocket(_Client);
Полет пока нормальный. (не проверил бы не написал) А мне очень нужно чтобы оперционка ХР вела себя стабильно месяцами не выключаясь. Поэтому "такую мелочь" не считаю лишней. (Комп стоит в ГОК и ближайшие администраторы могут находится в соседней области.) И вообще не порядок, что в интернетах в основном мнения почти случайны по поводу как все-таки правильно закрыть сокет. recv конечно не обязателен, если есть уверенность что ничего не получено, но явно не помешает, и с каких это пор "стоит доверять входным данным"? Надеюсь это кому-то еще поможет, тогда не зря писал.
Пишу на чистом С, плюсы спилил.
LynXzp вне форума Ответить с цитированием
Старый 18.10.2012, 22:18   #2
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,330
По умолчанию

Возможно вам так же стои посмотреть в сторону функции linger()
waleri вне форума Ответить с цитированием
Старый 19.10.2012, 14:09   #3
LynXzp
Пользователь
 
Аватар для LynXzp
 
Регистрация: 04.10.2012
Сообщений: 95
По умолчанию

Спасибо за квест по структуре linger и функции setscokopt, за то точно разобрался и код расширился: (тут http://www.irietools.com/iriepascal/progref393.html все отлично описано, а именно перевожу нужную часть)
чтобы сокет закрылся "жестко" перед вызовом closesocket нужно выставить сокету опцию SO_LINGER структуры linger l_onoff в !=0 и l_linger в 0)

Цитата:
SO_LINGER Enabled
Если опция SO_LINGER включена в сокете с нулевым временным интервалом (в структуре linger параметр l_onoff выставлен не в ноль и l_linger в ноль) тогда закрытие сокета функцией closesocket не будет блокировано даже есть есть данные для передачи. Это называется жестким закрытием, потому что соединение обрывается немедленно и все не переданные данные теряются. Все вызов recv на противоположной стороне ничего не дадут и функция WSAGetLastError вернет результат WSAECONNRESET.
http://nginx.org/ru/docs/http/ngx_http_core_module.html
Цитата:
После чего при закрытии сокета клиенту отсылается TCP RST, а вся память, связанная с этим сокетом, освобождается. Это позволяет избежать длительного нахождения уже закрытого сокета в состоянии FIN_WAIT1 с заполненными буферами.
Дальше предлагается три других варианта этих опций с "грациозным" дисконектом который в Windows стоит по умолчанию - при этом функция closesocket выполняется, но сокет остается открытым, и все данные которые ожидают передачи будут переданы.


Собственно код измененный:

Код:
shutdown(_Client,2);
while(recv(_Client, _buff, 1024, 0)!=-1);
linger l; l.l_linger=0; l.l_onoff=1;
setsockopt(_Client,SOL_SOCKET,SO_LINGER,&1,sizeof(l));
closesocket(_Client);
Еще немного сомневаюсь о идеальной очередности строк, но все уже похоже на убийство муравья - молотком, поджариванием, помещением в вакуум и взрывом динамитом.
Возможно стоит сделать Sleep() перед закрытием чтобы все данные отправились, от куда мне знать что там из них queued. С другой стороны, попробовал без слипа несколько передач прошли на ура с точной передачей всех данных. Пока буду без Sleep(). Наверное при большой нагрузке "хвост" будет теряться, но с другой стороны "обрывание очередей" улучшит устойчивость к большим нагрузкам.
P.S. Еще если все это проверить с достоверной статистикой можно писать научную работу по закрытию сокета =)
Пишу на чистом С, плюсы спилил.
LynXzp вне форума Ответить с цитированием
Старый 19.10.2012, 15:25   #4
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,330
По умолчанию

Я думаю надо делать shutdown(socket, SD_SEND) (а не как у вас recv), что пошлет на "ту сторону" сигнал, что больше данных нет, т.е. там будет recv 0, после чего та стороно закроет сокет, после чего ваш recv вернет 0, после чего я так понимаю можно закрывать сокет.

Может стоит также посмотреть в сторону асинхронного I/O а там есть хорошая функция DisconnectEx()
waleri вне форума Ответить с цитированием
Старый 19.10.2012, 17:22   #5
LynXzp
Пользователь
 
Аватар для LynXzp
 
Регистрация: 04.10.2012
Сообщений: 95
По умолчанию

shutdown(socket, SD_SEND)
у меня
shutdown(_Client,2) т.е. SD_BOTH
Код:
SD_RECEIVE = 0	- Shutdown receive operations.
SD_SEND = 1	- Shutdown send operations.
SD_BOTH = 2	- Shutdown both send and receive operations.
Как минимум не хуже предложенного Вами варианта. (recv отношения не имеет, возможно и не нужно, но автор говорящий нужно довольно авторитетный + http://ru.wikipedia.org/wiki/Пари_Паскаля = пусть будет)

Цитата:
Может стоит также посмотреть в сторону асинхронного I/O а там есть хорошая функция DisconnectEx()
Спасибо, ознакомился, пишут хорошая вещь, но у меня вебсервер готов и со всеми потоками все стабильно разбито. А еще много предстоит работы, глобальная переделка вебсерверной части не попадает туда, по крайней мере пока. И судя по найденным источникам закрытие сокета как у меня вполне можно считать надежным.
Пишу на чистом С, плюсы спилил.

Последний раз редактировалось LynXzp; 19.10.2012 в 17:25.
LynXzp вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как правильно выделить буфер для приёма строки от WinApi функции c0mp Win Api 14 03.08.2012 17:10
Различие кода mfc и WinApi(mfc ручками набили, а WinApi автоматически с генерировался!! нужно явное отличие, не могу найти) Артём1991 Помощь студентам 0 25.03.2012 17:13
Не правильно (или правильно?) работает позиционирование в WebKit mutabor HTML и CSS 5 09.12.2010 09:54
Работа с Socket-ом c помощю WinAPI 69 region Помощь студентам 13 11.04.2008 17:22