Addr.sin_addr.S_addr := inet_addr('192.168.200.217');
Функция inet_addr
выполняет простой парсинг строки и не проверяет, существует ли такой адрес на самом деле. Поля адреса можно задавать в десятичном, восьмеричном и шестнадцатеричном форматах. Восьмеричное поле должно начинаться с нуля, шестнадцатеричное — с "0x". Приведенный адрес можно записать в виде "0300.0250.0310.0331" (восьмеричный) или "0xC0.0xA8.0xC8.0xD9" (шестнадцатеричный). Допускается также смешанный формат записи, в котором разные поля заданы в разных системах исчисления. Функция inet_addr
поддерживает также менее распространенные форматы записи IP-адреса в виде трех полей. Подробнее об этом можно прочитать в MSDN.
Примечание
Если строка, переданная функции inet_addr
, не распознается как допустимый адрес, то функция возвращает значение INADDR_NONE
. До пятой версии Delphi включительно эта константа имеет значение $FFFFFFFF
, начиная с шестой версии — значение -1. Поле S_addr
имеет тип u_long
, который, как отмечалось, соответствует типу LongInt
, т.е. является знаковым. Сравнение знакового числа с беззнаковым обрабатывается компилятором особым образом (подробнее об этом написано в разд. 3.1.4 ), поэтому, если просто сравнить S_addr
и INADDR_NONE
в старых версиях Delphi, получится неправильный результат. Перед сравнением константу INADDR_NONE
следует привести к типу u_long
, тогда операция выполнится правильно. В шестой и более поздних версиях Delphi приведение не обязательно, но оно не мешает, поэтому в целях совместимости со старыми версиями его тоже лучше выполнять.
В библиотеке сокетов предусмотрена константа INADDR_ANY
, позволяющая не указывать явно адрес в программе, а оставить его выбор на усмотрение системы. Для этого полю sin_addr.S_addr
следует присвоить значение INADDR_ANY
. Если IP-адрес компьютеру не назначен, то при использовании этой константы сокет будет привязан к локальному адресу 127.0.0.1. Если компьютеру назначен один IP-адрес, сокет будет привязан к этому адресу. Если компьютеру назначено несколько IP-адресов, то будет выбран один из них, причем сама привязка при этом отложится до установления соединения (в случае TCP) или до первой отправки данных через сокет (в случае UDP). Выбор конкретного адреса при этом зависит от того, какой адрес имеет удалённая сторона.
Итак, резюмируем все сказанное. Пусть у нас есть сокет S, который нужно привязать, например, к адресу 192.168.200.217
и порту 3320. Для этого следует выполнить код листинга 2.3.
Листинг 2.3. Привязка сокета к конкретному адресу
Addr.sin_family := PF_INET;
Addr.sin_addr.S_addr := inet_addr('192.168.200.217');
Addr.sin_port := htons(3320);
FillChar(Addr.sin_zero, SizeOf(Addr.sin_zero), 0);
if bind(S, Addr, SizeOf(Addr)) = SOCKET_ERROR then
begin
// какая-то ошибка, анализируем с помощью WSAGetLastError
end;
FillChar
— это стандартная процедура Паскаля, заполняющая некоторую область памяти заданным значением. В данном случае мы применяем ее для заполнения нулями поля sin_zero
. Для этой же цели пригодна функция Windows API ZeroMemory
. В примерах на С/C++ обычно используется функция memset
.
Теперь рассмотрим другой случай: пусть выбор адреса и порта можно оставить на усмотрение системы. Тогда код будет выглядеть так, как показано в листинге 2.4.
Листинг 2.4. Привязка сокета к адресу, выбираемому системой
Addr.sin_family := PF_INET;
Addr.sin_addr.S_addr := INADDR_ANY;
Addr.sin_port := 0;
FillChar(Addr.sin_zero, SizeOf(Addr.sin_zero), 0);
if bind(S, Addr, SizeOf(Addr)) = SOCKET_ERROR then
begin
// какая-то ошибка, анализируем с помощью WSAGetLastError
end;
В случае TCP сервер сам не является инициатором подключения, но может работать с любым подключившимся клиентом, какой бы у него ни был адрес.
Для сервера принципиально, какой порт он будет использовать — если порт не определен заранее, клиент не будет знать, куда подключаться. Поэтому номер порта является важным признаком для сервера. (Иногда, впрочем встречаются серверы, порт которых заранее неизвестен, но в таких случаях всегда существует другой канал передачи данных, позволяющий клиенту до подключения узнать, какой порт задействован в данный момент сервером. С другой стороны, клиенту обычно непринципиально, какой порт будет у его сокета, поэтому чаще всего серверу назначается фиксированный порт, а клиент оставляет выбор системе.
Протокол UDP не поддерживает соединение, но при его применении часто одно приложение тоже можно условно назвать сервером, а другое — клиентом. Сервер создает сокет и ждет, когда кто-нибудь что-нибудь пришлет и высылает что-то в ответ, а клиент сам отправляет что-то куда-то. Поэтому, как и в случае TCP, сервер должен использовать фиксированный порт, а клиент может выбирать любой свободный.
Читать дальше
Конец ознакомительного отрывка
Купить книгу