SD_RECEIVE = 0;
SD_SEND = 1;
SD_BOTH = 2;
Для освобождения ресурсов, связанных с сокетом, служит функция closesocket
, которая освобождает память, выделенную для буферов, и порт. Ее единственный параметр задает сокет, который требуется закрыть, а возвращаемое значение — ноль или SOCKET_ERROR
. После вызова этой функции соответствующий дескриптор сокета перестает иметь смысл, и использовать его больше нельзя.
По умолчанию функция closesocket
немедленно возвращает управление вызвавшей ее программе, а процесс закрытия сокета начинает выполняться в фоновом режиме. Под закрытием подразумевается не только освобождение ресурсов, но и отправка данных, которые остались в выходном буфере сокета. Вопрос о том, как изменить поведение функции closesocket
, будет обсуждаться в разд. 2.1.17. Если сокет закрывается одной нитью в тот момент, когда другая нить пытается выполнить какую-либо операцию с этим сокетом, то эта операция завершается с ошибкой.
Функция shutdown
нужна в первую очередь для того, чтобы заранее сообщить партнеру по связи о намерении завершить связь, причем это имеет смысл только для протоколов, поддерживающих соединение. В случае UDP функцию shutdown вызывать практически бессмысленно, можно сразу вызывать closesocket
. При использовании TCP удаленная сторона получает сигнал о выключении партнера, но стандартная библиотека сокетов не позволяет программе обнаружить его получение (такие функции есть в сокетах Windows, о чем мы будем говорить далее). Но этот сигнал может быть важен для внутрисистемных функций, реализующих сокеты. Windows-версия библиотеки сокетов относится к отсутствию данного сигнала достаточно либерально, поэтому вызов shutdown в том случае, когда и клиент, и сервер работают под управлением Windows, не обязателен. Но реализации TCP в других системах не всегда столь же снисходительно относятся к подобной небрежности. Результатом может стать долгое (до двух часов) "подвешенное" состояние сокета в той системе, когда с ним и работать уже нельзя, и информации об ошибке программа не получает. Поэтому в случае TCP лучше не пренебрегать вызовом shutdown
, чтобы сокет на другой стороне не имел проблем.
MSDN рекомендует следующий порядок закрытия TCP-сокета. Во-первых, сервер не должен закрывать свой сокет по собственной инициативе, он может это делать только после того, как был закрыт связанный с ним клиентский сокет. Клиент начинает закрытие сокета с вызова shutdown
с параметром SD_SEND
. Сервер после этого сначала получает все данные, которые оставались в буфере сокета клиента, а затем получает от клиента сигнал о завершении передачи. Тем не менее сокет клиента продолжает работать на прием, поэтому сервер при необходимости может на этом этапе послать клиенту какие-либо данные, если это необходимо. Затем сервер вызывает shutdown
с параметром SD_SEND
, и сразу после этого — closesocket
. Клиент продолжает читать данные из входящего буфера сокета до тех пор, пока не будет получен сигнал о завершении передачи сервером. После этого клиент также вызывает closesocket
. Такая последовательность гарантирует, что данные не будут потеряны, но, как мы уже обсуждали ранее, она не может быть реализована в рамках стандартных сокетов из-за невозможности получить сигнал о завершении передачи, посланный удаленной стороной. Поэтому на практике следует реализовывать упрощенный способ завершения связи: клиент вызывает shutdown
с параметром SD_SEND
или SD_BOTH
и сразу после этого — closesocket
. Сервер при попытке выполнить операцию с сокетом получает ошибку, после которой также вызывает closesocket
. Вызов shutdown
на стороне сервера при этом не нужен, т.к. в этот момент соединение уже потеряно, и высылать данные из буфера вместе с сигналом завершения уже некуда.
2.1.9. Передача данных при использовании UDP
Мы наконец-то добрались до изучения того, ради чего сокеты и создавались: как передавать и получать с их помощью данные. По традиции начнем рассмотрение с более простого протокола UDP. Функции, которые рассматриваются в этом разделе, могут работать и с другими протоколами, и от этого их поведение может меняться. Мы здесь описываем только их поведение при использовании UDP.
Для передачи данных удалённому сокету предусмотрена функция sendto
, описанная следующим образом:
function sendto(s: TSocket; var Buf; len, flags: Integer; var addrto: TSockAddr; tolen: Integer): Integer;
Первый параметр данной функции задаёт сокет, который служит для передачи данных. Здесь нужно указать значение, полученное ранее от функции socket
. Параметр Buf
задаёт буфер, в котором хранятся данные для отправки, а параметр len
— размер этих данных в байтах. Параметр flags
позволяет указать некоторые дополнительные опции, которых мы здесь касаться не будем, т.к. в большинстве случаев они не нужны. Пока следует запомнить, что параметр flags
в функции sendto
, а также в других функциях, где он встречается, должен быть равен нулю. Параметр addrto
задает адрес (состоящий из IP-адреса и порта) удаленного сокета, который должен получить эти данные. Значение параметра addrto
должно формироваться по тем же правилам, что значение аналогичного параметра функции bind, за исключением того, что IP-адрес и порт должны быть заданы явно (т.е. не допускаются значения INADDR_ANY
и нулевой номера порта). Параметр tolen
задает длину буфера, отведенного для адреса, и должен быть равен SizeOf(TSockAddr)
. Один вызов функции sendto
приводит к отправке одной дейтаграммы. Данные, переданные в sendto
, никогда не разбиваются на несколько дейтаграмм, и данные, переданные последовательными вызовами sendto
, никогда не объединяются в одну дейтаграмму.
Читать дальше
Конец ознакомительного отрывка
Купить книгу