MyBooks.club
Все категории

А. Григорьев - О чём не пишут в книгах по Delphi

На сайте mybooks.club вы можете бесплатно читать книги онлайн без регистрации, включая А. Григорьев - О чём не пишут в книгах по Delphi. Жанр: Программирование издательство -,. Доступна полная версия книги с кратким содержанием для предварительного ознакомления, аннотацией (предисловием), рецензиями от других читателей и их экспертным мнением.
Кроме того, на сайте mybooks.club вы найдете множество новинок, которые стоит прочитать.

Название:
О чём не пишут в книгах по Delphi
Издательство:
-
ISBN:
-
Год:
-
Дата добавления:
17 сентябрь 2019
Количество просмотров:
244
Читать онлайн
А. Григорьев - О чём не пишут в книгах по Delphi

А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание

А. Григорьев - О чём не пишут в книгах по Delphi - описание и краткое содержание, автор А. Григорьев, читайте бесплатно онлайн на сайте электронной библиотеки mybooks.club
Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.Для программистов

О чём не пишут в книгах по Delphi читать онлайн бесплатно

О чём не пишут в книгах по Delphi - читать книгу онлайн бесплатно, автор А. Григорьев

Функция bind предназначена для сокетов, реализующих разные протоколы из разных стеков, поэтому кодирование адреса в ней сделано достаточно универсальным. Впрочем, следует отметить, что разработчики модуля WinSock для Delphi выбрали не лучший способ перевода прототипа этой функции на Паскаль, поэтому универсальность в значительной мере утрачена. В оригинале прототип функции bind имеет следующий вид:

int bind(SOCKET s, const struct sockaddr FAR* name, int namelen); 

Видно, что второй параметр — это указатель на структуру sockaddr. Однако C/C++ позволяет при вызове функции в качестве параметра передать указатель на любую другую структуру, если будет выполнено явное приведение типов. Для каждого семейства адресов предусмотрена своя структура, и в качестве фактического параметра передастся указатель на эту структурy. Если бы авторы модуля WinSock описали второй параметр как параметр-значение типа указатель, можно было бы поступать точно так же. Однако они описали этот параметр как параметр-переменную. В результате на двоичном уровне ничего не изменилось: и там, и там в стек помещается указатель. Однако компилятор при вызове функции bind не допустит использования никакой другой структуры, кроме TSockAddr, а эта структура не универсальна и удобна, по сути дела, только при использовании стека TCP/IP. В других случаях наилучшим решением будет самостоятельно импортировать функцию bind из wsock32.dll с нужным прототипом. При этом придется импортировать и некоторые другие функции, работающие с адресами. Впрочем мы здесь ограничиваемся только протоколами TCP и UDP, поэтому больше останавливаться на этом вопросе не будем.

Примечание

На самом деле существует способ передать в функцию bind с таким прототипом параметр addr любого типа, совместимого с этой функцией. Если A — некая переменная типа, отличающегося от TSockAddr, то передать в качестве параметра-переменной ее можно так: PSockAddr(@А)^. Однако подобные низкоуровневые операции программу не украшают.

В стандартной библиотеке сокетов (т.е. в заголовочных файлах для этой библиотеки) полагается, что адрес кодируется структурой sockaddr длиной 16 байтов, причем первые два байта этой структуры кодируют семейство протоколов, а смысл остальных зависит от этого семейства. В частности, для стека TCP/IP семейство протоколов задается константой PF_INET. (Ранее мы уже встречались с термином "семейство адресов" и константой AF_INET. В ранних версиях библиотеки сокетов семейства протоколов и семейства адресов были разными понятиями, но затем эти понятия слились в одно, и константы AF_XXX и PF_XXX стали взаимозаменяемыми). Остальные 14 байтов структуры sockaddr занимает массив типа char (напомним, что тип char в C/C++ соответствует одновременно двум типам Delphi: Char и ShortInt). В принципе, в стандартной библиотеке сокетов предполагается, что структура, задающая адрес, всегда имеет длину 16 байтов, но на всякий случай предусмотрен третий параметр функции bind, который хранит длину структуры. В сокетах Windows длина структуры может быть любой (это зависит от протокола), так что этот параметр, в принципе, может пригодиться.

Ранее уже упоминалось, что неструктурированное представление адреса в виде массива из 14 байтов бывает неудобно, и поэтому для каждого семейства протоколов предусмотрена своя структура, учитывающая особенности адреса. В частности, для протоколов стека TCP/IP используется структура sockaddr_in, размер которой также составляет 16 байтов. Из них задействовано только восемь: два для кодирования семейства протоколов, четыре для IP-адреса и два — для порта. Оставшиеся 8 байтов должны содержать нули.

Можно было бы предположить, что типы TSockAddr и TSockAddrIn, описанные в модуле WinSock, соответствуют структурам sockaddr и sockaddr_in, однако это не так. На самом деле эти типы описаны следующим образом (листинг 2.1).

Листинг 2.1. Типы TSockAddr и TSockAddrIn

SunB = packed record

 s_b1, s_b2, s_b3, s_b4: u_char;

end;


SunW = packed record

 s_w1, s_w2: u_short;

end;


in_addr = record

 case Integer of

 0: (S_un_b: SunB);

 1: (S_un_w: SunW);

 2: (S_addr: u_long);

end;

TInAddr = in_addr;


sockaddr_in = record

 case Integer of

 0: (

  sin_family: u_short;

  sin_port: u_short;

  sin_addr: TInAddr;

  sin_zero: array[0..7] of Char);

 1: (

  sa_family: u_short;

  sa_data: array[0..13] of Char);

end;

TSockAddrIn = sockaddr_in;

TSockAddr = sockaddr_in;

Таким образом, типы TSockAddr и TSockAddrIn — это синонимы типа sockaddr_in (но не того sockaddr_in, который имеется в стандартной библиотеке сокетов, а типа sockaddr_in, описанного в модуле WinSock). А тип sockaddr_in из WinSock является вариантной записью, и в случае 0 соответствует типу sockaddr_in из стандартной библиотеки сокетов, а в случае 1 — sockaddr из этой же библиотеки. Вот такая несколько запутанная ситуация, хотя на практике все выглядит не так страшно.

Примечание

Из названия типов можно сделать вывод, что тип u_short — это Word, а u_long — Cardinal. На самом деле u_short — это действительно Word, а вот u_long — это LongInt. Сложно сказать почему выбран знаковый тип там, где предполагается беззнаковый. Видимо, это осталось в наследство от старых версий Delphi, которые не поддерживали тип Cardinal в полном объеме. Кстати, тип u_char — это Char, а не Byte.

Перейдем, наконец, к более практически важному вопросу: какими значениями следует заполнять переменную типа TSockAddr, чтобы при передаче ее в функцию bind сокет был привязан к нужному адресу. Так как мы ограничиваемся рассмотрением протоколов TCP и UDP, нас не интересует та часть вариантной записи sockaddr_in, которая соответствует случаю 1, т.е. мы будем рассматривать только те поля этой структуры, которые имеют префикс sin.

Поле sin_zero, очевидно, должно содержать массив нулей. Это то самое поле, которое не несет никакой смысловой нагрузки и служит только для увеличения размера структуры до стандартных 16 байтов. Поле sin_family, должно иметь значение PF_INET. В поле sin_port записывается номер порта, к которому привязывается сокет. Номер порта должен быть записан в сетевом формате, т.е. здесь необходимо прибегать к функции htons, чтобы из привычной нам записи номера порта получить число в требуемом формате. Номер порта можно оставить нулевым, тогда система выберет для сокета свободный порт с номером от 1024 до 5000.

IP-адрес для привязки сокета задается полем sin_addr, которое имеет тип TInAddr. Этот тип сам является вариантной записью, которая отражает три способа задания IP-адреса: в виде 32-битного числа, в виде двух 16-битных чисел или в виде четырех 8-битных чисел. На практике чаще всего встречается формат в виде четырех 8-битных чисел, реже — в виде 32-битного числа. Задание адресов в виде двух 16-битных чисел или двух 8-битных и одного 16-битного числа относится к очень редко встречающейся экзотике.

Пусть у нас есть переменная Addr типа TSockAddr, и нам требуется ее поле sin_addr записать адрес 192.168.200.217. Это можно сделать так, как показано в листинге 2.2.

Листинг 2.2. Прямое присваивание IP-адреса

Addr.sin_addr.S_un_b.s_b1 := 192;

Addr.sin_addr.S_un_b.s_b2 := 168;

Addr.sin_addr.S_un_b.s_b3 := 200;

Addr.sin_addr.S_un_b.s_b4 := 217;

Существует альтернатива такому присвоению четырех полей по отдельности — функция inet_addr. Эта функция в качестве входного параметра принимает строку, в которой записан IP-адрес, и возвращает этот IP-адрес в формате 32-битного числа. С использованием функции inet_addr приведенный в листинге 2.2 код можно переписать так:

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 приведение не обязательно, но оно не мешает, поэтому в целях совместимости со старыми версиями его тоже лучше выполнять.


А. Григорьев читать все книги автора по порядку

А. Григорьев - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки mybooks.club.


О чём не пишут в книгах по Delphi отзывы

Отзывы читателей о книге О чём не пишут в книгах по Delphi, автор: А. Григорьев. Читайте комментарии и мнения людей о произведении.

Прокомментировать
Подтвердите что вы не робот:*
Подтвердите что вы не робот:*
Все материалы на сайте размещаются его пользователями.
Администратор сайта не несёт ответственности за действия пользователей сайта..
Вы можете направить вашу жалобу на почту librarybook.ru@gmail.com или заполнить форму обратной связи.