Последние записи
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
- Определить текущую ОС
- Автоматическая смена языка (раскладки клавиатуры)
- Сравнение языков на массивах. Часть 2
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
22nd
Авг
Беспроводная сеть масштаба микрорайона. Часть 1
Общаясь с друзьями и знакомыми, работающими в области ИТ, не раз приходилось слышать заявление, что Wi-Fi подходит только для сетей масштаба квартиры или небольшого офиса. В качестве аргументов приводились малая дальность, зависимость от погодных условий, ненадежность оборудования. Примерно такое же мнение бытует о том, что для провайдерских сетей не подходит платформа Windows. Аргументы: высокая ресурсоемкость, ненадежность и вообще неспособность работать в качестве системы биллинга.
Александр
by WildHunter http://airnet.sytes.net
А так ли это? Этот вопрос нас всерьез заинтересовал и подтолкнул к попытке создания беспроводной сети как раз на базе Wi-Fi и Windows. Оговорюсь, что некоторый опыт создания беспроводных сетей у нас уже был, правда, немного другого назначения – корпоративных.
Начали мы естественно с постановки самим себе техзадания:
- Сетевая технология WiFi IEEE 802.11b/g, возможно в будущем 802.11a и 802.11n википедия
- Серверы на базе Windows Server. В качестве основной ОС был выбран Windows Server 2003, как достаточно надежная и неприхотливая система, при этом обеспечивающая нужный функционал википедия
- Максимальная простота развертывания, эксплуатации и администрирования сети.
- Минимальные затраты, поскольку проект некоммерческий и финансирования со стороны не будет, придется обходиться собственными силами и средствами.
- Надежность, производительность и пропускная способность, достаточные для обслуживания городского микрорайона.
Выбор оборудования
Основательно изучив рынок предложений недорогого Wi-Fi оборудования, мы остановили свой выбор на нескольких моделях точек доступа (ТД) от D-Link: DWL-2100AP, DAP-1150 и DAP-1160 http://www.d-link.ru. Основными причинами выбора именно этих точек стали их приемлемая цена, накопленный в России и на Украине большой опыт их эксплуатации, а также наличие большого количества альтернативных прошивок, как от различных производителей оборудования, так и open-source.
Для тестирования было закуплено несколько экземпляров указанных выше точек доступа, а также различных антенн к ним. Началось тестирование в различных режимах, на разных расстояниях, с различными антеннами и прошивками. От «родных» прошивок мы отказались практически сразу, поскольку они рассчитаны как раз на применение в домашних или условиях небольшого офиса, не позволяют менять многие важные параметры (ACK timeout, мощность передатчика, чувствительность приемника и т.д.), а также практически не обеспечивают возможности мониторинга беспроводных соединений.
Точки DWL-2100AP оказались хороши для построения мостов на большие расстояния (до 50 км), но капризными в эксплуатации и несовместимыми со многими другими моделями Wi-Fi устройств. Также у них обнаружилась неприятная проблема: довольно часто при большой нагрузке «слетали» прошивки, а их восстановление оказалось довольно непростой процедурой. В итоге DWL-2100AP была признана нами годной к применению только для организации мостов. Хотя вполне возможно, что нам просто достались неудачные экземпляры.
DAP-1150 – очень простое и надежное устройство, тем не менее отвечающее всем основным критериям хотспота. А с немного доработанной прошивкой от Conceptronic http://www.conceptronic.net эта точка стала практически полностью соответствовать нашим требованиям.
Рис. 1. Точка доступа DAP-1150
Основные характеристики DAP-1150 с прошивкой Conceptronic:
- Стандарты: 802.11b/g, 802.3/802.3u 10Base- T/100Base-TX Ethernet, ANSI/IEEE 802.3 NWay auto-negotiation.
- Интерфейсы: 802.11b/g беспроводная LAN, 1 порт 10/100Base-TX Ethernet LAN
- Диапазон частот: 2.4 – 2.4835 ГГц
- Количество каналов: 13 (ETSI)
- Схемы модуляции:
802.11b: DQPSK, DBPSK, DSSS, CCK
802.11g: BPSK, QPSK, 16QAM, 64QAM, OFDM - Режимы работы: Station – Ad Hoc, Station – Infrastructure, AP, AP Bridge – Point to Point, AP Bridge – Point to MultiPoint, AP Bridge – WDS, Universal Repeater.
- Скорость передачи данных:
802.11g: 6, 9, 12, 18, 24, 36, 48, 54 Мбит/с
802.11b: 1, 2, 5.5, 11 Мбит/с - Чувствительность приемника: до -100dBm
- Выходная мощность передатчика: 20dBm
- Безопасность: WEP, WPA/WPA2, фильтрация МАС-адресов, SSID broadcast disable.
- Дополнительные возможности: IAPP, встроенный Radius-сервер.
DAP-1160 с прошивкой AProuter http://aprouter.com.br превратилась в довольно серьезное устройство с широким функционалом, включающим в себя массу полезных возможностей, от контроля качества WDS- соединений википедия до многофункционального шейпера википедия.
Рис. 2. Точка доступа DAP-1160
Основные характеристики DAP-1160 с прошивкой AProuter:
- Стандарты: 802.11b/g, 802.3/802.3u 10Base- T/100Base-TX Ethernet, ANSI/IEEE 802.3 NWay auto-negotiation.
- Интерфейсы: 802.11b/g беспроводная LAN, 2 порта 10/100Base-TX Ethernet LAN
- Диапазон частот: 2.4 – 2.4835 ГГц
- Количество каналов: 13 (ETSI)
- Схемы модуляции:
802.11b: DQPSK, DBPSK, DSSS, CCK
802.11g: BPSK, QPSK, 16QAM, 64QAM, OFDM - Режимы работы роутера: WISP Client, Bridge, Gateway, Router (WAN Ethernet), Router (WAN Wireless).
- Режимы работы Wireless: Wireless Client, AP, WDS+AP, WDS-Bridge.
- Скорость передачи данных:
802.11g: 6, 9, 12, 18, 24, 36, 48, 54 Мбит/с
802.11b: 1, 2, 5.5, 11 Мбит/с - Чувствительность приемника: до -100dBm
- Выходная мощность передатчика: 20dBm
- Безопасность: WEP, WPA/WPA2, фильтрация МАС-адресов, SSID broadcast disable.
- Дополнительные возможности: IP Aliases, IAPP, Block Relay, Firewall, Traffic Control (шейпер), DDNS, Watchdog.
Антенны ANT24-0700C производства D-Link были выбраны по наилучшему показателю цена/качество в своем классе и из-за небольших размеров:
Рис. 3. Антенна ANT24-0700C
Основные характеристики ANT24-0700C:
- Диапазон частот: 2,4-2,5ГГц
- Сопротивление: 50 Ом
- VSWR: 1.92 (макс.)
- Максимальное усиление: 7dBi
- Допустимая мощность: 1 Вт
- Диаграмма направленности в вертикальной плоскости (Вектор Е): 24 градуса.
- Диаграмма направленности в горизонтальной плоскости (Вектор Н): 360 градусов.
- Разъем: Reverse SMA «мама» (встроенный в антенну), переходник с RP-SMA на RP-TNC (внешний).
- Материал корпуса: ABS, ABS+PC
- Рабочая температура: От -20 до 65 градусов.
Построение сети
После завершения тестирования мы приступили к созданию собственно сети. В качестве базовой была выбрана классическая топология «звезда», центральной точкой которой стала DAP-1160 в режиме Bridge WDS, а «лучами» DAP-1150 в режиме WDS + AP. Использование технологии WDS позволяет получить на каждой точке доступа скорость передачи данных на уровне 1.2-1.4 Мбайт/с, максимальная скорость у клиентов составляет порядка 550-600 кБайт/с. Реальная скорость передачи данных зависит от типа клиентского оборудования и качества беспроводного соединения клиент-ТД, а также от количества клиентов и общей загрузки сети.
Рис. 4. Общая схема сегмента сети
Покрытие каждой ТД составляет (в радиусе, значения указаны при наличии прямой видимости или незначительных препятствий, например деревьев):
- Мобильные устройства (смартфоны, КПК, PSP и т.п.) – до 200м
- Ноутбуки, нетбуки, USB-устройства со встроенными антеннами – до 300м
- Ноутбуки, USB-устройства с внешними (или внутренними дипольными) антеннами – до 1500м
- ТД в режиме клиента с направленными антеннами – до 10км
Установка точек доступа
Естественно, что мы старались устанавливать ТД как можно выше, но не всегда это удается. Кроме того, при установке ТД на самую высокую мачту в округе, есть риск получить прямое попадание в нее молнии, от которого не спасет никакая грозозащита. Поэтому второй фактор, который мы старались учитывать – наличие поблизости громоотводов или более высоких мачт.
Все наши хотспоты стоят на мачтах высотой 4-6 метров, которые расположены на крышах зданий высотой в 5-6 этажей. Рядом есть небольшой массив из 9-этажных зданий, но нам не удалось договориться с местным ОСМД*.
Питание на точки подается по витой паре, обычно из квартир наших клиентов (чтобы избежать бюрократии с ЖЕКами и ОСМД), по этому же кабелю клиенты входят в сеть. Взаимовыгодное сотрудничество: мы получаем место для установки и запитки точки, а клиенты получают VIP-доступ к сети
* Комментарий автора. Объединение совладельцев многоквартирного дома (ОСМД). Юридическое лицо, созданное владельцами для содействия использованию их собственного имущества и управления, содержания и использования неделимого и общего имущества.
Качество
На следующем скриншоте показаны характеристики соединения с ТД на расстоянии ~1000 метров при прямой видимости (см. рисунок 5):
Рис. 5. Качественные характеристики соединения в Atheros Client Utility
ТД: DAP-1150, мощность 20dBm, антенна штыревая 7dBi. Клиент: ноутбук Asus X51RL, WiFi-плата Atheros AR2425, антенна встроенная дипольная 4dBi.
Линк стабильный, текущая скорость линка «гуляет» в пределах 18-36 Мбит/с, это нормально. Скорость загрузки файла с сервера сети держится в пределах 400-500 Кбайт/с. Может показаться, что при средней скорости линка в 24 мегабита это немного, но нужно учитывать особенности технологии Wi-Fi, в которой мегабиты совсем другие, чем в кабельных сетях. Кроме того, режим WDS также вносит свои коррективы, и не в большую сторону. Если вы ожидаете от Wi-Fi волоконно-оптических скоростей, то вам действительно лучше посмотреть в сторону оптики. А Wi-Fi это простая, недорогая, удобная и достаточно качественная связь для всех.
Как это ни странно, но на качество связи почти не влияют погодные условия. Если линк стабилен в хорошую погоду, то он таким и останется в дождь, снег или туман. Возможно, немного снизится скорость, но связь будет. Наш опыт практической эксплуатации показывает следующие результаты: в дождь (даже сильный) или снег уровень сигнала между ТД снижается в среднем всего на 1-4 dBm, туман никакого заметного влияния вообще не оказывает. Замечу, что все точки стоят на примерно одинаковом расстоянии от центральной, которое составляет 800-1200 метров. Для Wi-Fi при прямой видимости это расстояние несерьезное. Так что мнение о влиянии погоды на Wi-Fi сильно преувеличено. Влияние погодных условий становится заметным только на больших расстояниях, от 5 км и более. А вот что действительно отрицательно влияет**: преграды, помехи в эфире, избыточная мощность радиоустройств. Данные факторы обсуждались и не раз, например на http://www.lan23.ru, поэтому подробно мы их здесь рассматривать не будем.
** Комментарий автора. В последнее время технология Wi-Fi в наших краях сделала явный рывок, но непонятно, вперед, назад или «налево». В эфире полный бардак, огромное количество различных устройств, большинство из которых настроены как попало или вообще не настроены. Все это снижает качество связи, а если так и будет продолжаться дальше – связь вообще станет невозможной… Вот какая картина открывается с некоторых наших хотспотов:
Каждая ТД нормально выдерживает до 20 одновременно работающих клиентов, если нет «тяжелого» трафика, в первую очередь P2P. Если есть, то даже один клиент в состоянии через минуту сделать для всех остальных вход в сеть недоступным, а минуты через 3-5 отправить точку на перезагрузку. ТД почти не критичны к скорости или общему объему трафика, но очень критичны к количеству подключений. Это не значит, что использовать «тяжелый» трафик невозможно. Вполне возможно, если предпринять определенные меры для защиты сети от перегрузок. К таким методам защиты, например, относятся ограничение количества подключений на хост, работа по VPN, а также использование адаптивных шейперов и QoS. Но об этом поговорим в другой раз, так как это более относится к серверным технологиям, а не к Wi- Fi.
Несколько слов о клиентских устройствах
Сейчас на рынке представлена масса самых разнообразных устройств 802.11b/g/n. Из них довольно трудно, но возможно выбрать оптимальный*** вариант. Некоторые устройства, например USB-брелки со встроенной антенной, рассчитаны на работу только внутри помещения и обладают очень ограниченным радиусом действия. Даже одна стена или дерево под окном, для них уже неодолимая преграда.
Другой класс – это устройства с внешними антеннами. Даже со штатными антеннами они показывают неплохую дальность при прямой видимости, а при установке хорошей антенны вполне могут работать на расстояниях, измеряемых километрами. Поэтому при выборе Wi-Fi устройства стоит обратить внимание не только на дизайн, размеры и цену, но в первую очередь – на технические характеристики.
При покупке ноутбука, КПК или любого другого устройства, оснащенного встроенным Wi-Fi, также обязательно интересуйтесь характеристиками Wi- Fi платы, типом и характеристиками встроенной антенны. Зачастую, встроенные платы подходят только для связи внутри комнаты или на расстояниях до 100 метров на открытой местности. Хотя есть и большое количество устройств, оснащенных качественным Wi-Fi и вполне способных поддерживать связь на приличных расстояниях.
Основные характеристики Wi-Fi устройств
- Стандарты, поддерживаемые устройством. От этого зависит универсальность и максимальная скорость передачи данных. В настоящее время в Европе поддерживаются следующие стандарты:
- 802.11b (максимальная скорость 11 Мбит/с)
- 802.11g (максимальная скорость 54 Мбит/с)
- 802.11n (максимальная скорость 150/300 Мбит/с)
- Мощность передатчика и чувствительность приемника. Не стоит чрезмерно увлекаться мощностью, слишком мощный сигнал в городских условиях скорее вреден, так как вызывает многократные отражения и наложения, а также создает массу помех. Да и для здоровья он не очень полезен. На наш взгляд оптимальные значения составляют до 100 мВт (20 dBm) мощности и -95 dBm чувствительности.
- Возможность подключения внешней антенны и характеристики штатной. «Главная часть любого радиоустройства – антенна», это вам скажет любой специалист по любому виду радиосвязи. К Wi-Fi это тоже относится в полной мере. Для нормальной работы (приличной дальности и скорости) любого Wi-Fi-устройства необходима антенна с коэффициентом усиления не менее 4dBi.
- Функционал устройства. К этому относится все, что устройство умеет делать и насколько удобно его использовать. По этому критерию выбор огромный, от самых простых до сложнейших устройств, которые «умнее» вашего компьютера. Главное – достаточно четко представлять, чего вы хотите и сколько вы согласны за это заплатить.
*** Комментарий редакции. Многие задаются вопросом: «Как увеличить дальность связи?». Перво-наперво, при выборе WLAN-аппарата, обратите внимание на то, чтобы выходная мощность была как можно ближе к разрешенной 20 dB. Следующим решающим фактором является чувствительность. У лучших современых аппаратов она находится на уровне -97 dB. Чем выше чувствительность к слабым сигналам, тем выше дальность. Но это палка о двух концах, так как мы не учитываем при этом помеховую обстановку вокруг.
Как влияют эти величины на дальность связи? К примеру, аппарат с мощностью в 20 dB сможет обеспечить в два раза большую дальность приема по сравнению с 14 dB, т.е. разница в 6 dB дает двойной выигрыш. Если к этому прибавить, что аппарат с чувствительностью -97 dB, позволяет получить выигрыш в 4 раза по сравнению с аппаратом, у которого чувствительность равна -76 dB, то общий выигрыш будет 8-ми кратным.
Заключение
Это основные характеристики, но существует еще масса различных тонкостей, специфических моментов и технических особенностей. Рассказать здесь обо всех нереально, поэтому выбор оборудования – совсем непростое дело, в котором не помешает консультация специалиста. Если вы, конечно, сами не являетесь таковым
Продолжение следует…
Ресурсы
- Сетевая аутентификация на практике http://www.citforum.ru/nets/articles/authentication
- Крупнейший и самый активный сайт рунета по Wi-Fi http://www.lan23.ru/index.html
- Антенны, много антен… хороших и разных http://radiowolna.narod.ru
Статья из пятого выпуска журнала “ПРОграммист”.
Скачать этот номер можно по ссылке.
Ознакомиться со всеми номерами журнала.
Обсудить на форуме — Беспроводная сеть масштаба микрорайона. Часть 1
8th
Авг
Определние локального и внутреннего IP
function GetLocalIPs: TStringList;
type
TaPInAddr = Array[0..10] of PInAddr;
PaPInAddr = ^TaPInAddr;
var
phe: PHostEnt;
pptr: PaPInAddr;
Buffer: Array[0..63] of Char;
I: Integer;
GInitData: TWSAData;
IPs: TStringList;
begin
IPs := TStringList.Create;
WSAStartup($101, GInitData);
GetHostName(Buffer, SizeOf(Buffer));
phe := GetHostByName(buffer);
if phe = nil then IPs.Add(’No IP found’)
else
begin
pPtr := PaPInAddr(phe^.h_addr_list);
I := 0;
while pPtr^ nil do
begin
IPs.Add(inet_ntoa(pptr^^));
Inc(I);
end;
end;
WSACleanup;
Result := IPs;
end;
27th
Июл
Как сделать общий доступ к папке в сети по паролю?
Sparky:
Вообщем вдруг кому-то пригодиться пишу что нужно сделать:
1. «Панели управления» и отмечаем чекбокс “Общий доступ к файлам и принтерам”
2. Воспользоваться компонентом “Одноранговая сеть”
3. включить “Служба общего доступа к файлам и принтерам”
4. выбрать Сетевой доступ: модель совместного доступа…”
5. отключить гостя
6. назначить права на доступ к папке, в доступе удалить “все”
7. на 2 машине в cmd ввести
Код:net use x: \\имя_компа_где_лежит_папка\ имя_папки пароль_пользователя /user:имя_компа_где_лежит_папка\пользователь
Все
17th
Июн
ICQ Client Delphi
27th
Май
Передача звука по сети. Прототип VoIP телефона
Данная статья будет полезна начинающим программистам, которые никогда не имели дело со звуком и его передачей по сети. Смысл этой статьи заключается в изучении и применении: WINAPI функций ввода и вывода звука WaveIn() и WaveOut() в среде разработки Delphi 7.0, самих компонентов TIdUDPServerSocket и TIdUDPClientSocket. Данные компоненты можно найти в библиотеке Indy, которая в свою очередь находится в свободном распространении на просторах Internet’а.
Передача звука по сети. Прототип VoIP телефона
Уколов Александр Владимирович
by ImmortalAlexSan st_devil@mail.ru
Комментарий автора.
Если вы никогда не программировали в Delphi 7.0, версиями ниже или выше, если вы вообще никогда не программировали на подобных ЯВУ, то эта статья не для вас.
Введение
К написанию программы для передачи звука по сети меня побудило желание получить-таки зачет по УИРС (это что-то вроде НИР – научно исследовательской работы студента) у преподавателя, ведущего мой основной предмет, и являющимся моим дипломным руководителем. Перед тем как сесть за Delphi и начать набирать код, предварительно, я изучил кучу литературы в бумажном и электронном виде о принципах упаковки звука и его передачи, о функциях ввода и вывода в самом Delphi и многом другом [1, 2]. Именно ввод и вывод заставил меня задуматься о сложности преподносимого материала. Для человека, никогда не имевшего с этим дело, разобраться в этой области очень сложно, имея под рукой множество кода без комментариев с непонятными процедурами и функциями непонятного WIN API, а если эти процедуры и функции описаны, то это описание предназначено не для начинающих программистов, приходилось все додумывать самому: смотреть подноготную каждой процедуры, и методом проб и ошибок идти медленно, но уверенно к вершине созидания. Но в конечном итоге я добился поставленной цели. И сейчас, разложив всю информацию, предоставленную мне в кашеобразном виде, по полочкам, я готов поделиться своими знаниями с вами, дорогие читатели! Итак, приступим…
Средства разработки
Прежде всего, для работы нам понадобится:
. IDE Delphi версии 7.0 и выше
. Библиотека Indy для Delphi 7.0 (TIdUDPServerSocket и TIdUDPClientSocket) [3, 4]
. колонки и микрофон
Сразу же перейдем к практической части. По мере появления неизвестных функций и процедур в листинге, они будут незамедлительно описываться…
Практическая часть. Создадим клиента
Передача звука в моей программе осуществляется с клиента на сервер, т.е. в одном направлении. Клиент может только писать и передавать, сервер – только принимать и воспроизводить. Первым делом начнем писать клиент.
Для этого, создадим новый проект в Дельфи, разместим на форме кнопку TButton и изменим ее свойство Caption на «начать отправку». После чего, разместим на форме компонент из библиотеки Indy TIdUDPClientSocket (см. рисунок 1):
Так как тестирование программы будет проводиться на локальном компьютере, то изменим значение свойства Host компонента TIdUDPClientSocket на «localhost». Далее я просто перечислю свойства компонента и их значения, что должны быть установлены: Active (false), BroadCastEnabled (false), BufferSize (8192), Name (IdUDPClient1), Port (0), ReceiveTimeOut (-2), Tag (0).
Примечание: описание некоторых вышеуказанных свойств выходит за рамки данной статьи.
Теперь, нажимаем двойным щелчком по вынесенному на форму компоненту TButton и появится обработчик события Button1Click(), где Button1 – это значение свойства Name данного компонента. В этом обработчике пишем или копируем следующий код:
procedure TForm1.Button1Click(Sender: TObject);
begin
// если на кнопке написано «начать отправку» то
If button1.Caption='Начать отправку' then Begin // выполняем этот код, где:
// готовим заголовок для буфера, здесь WaveIn – переменная типа интегер, для
// указания идентификатора устройства ввода (микрофона например), @WaveHdr –
// указатель на структуру TWaveHdr, sizeof(Twavehdr) – размер данной структуры в байтах.
waveInPrepareHeader(waveIn,@WaveHdr,sizeof(Twavehdr));
// заносим данные в буфер
waveInAddBuffer(wavein,@WaveHdr,sizeof(TwaveHdr));
// активируем сокет клиента
IdUDPClient1.Active:= true;
// считываем данные с микрофона
waveInStart(waveIn);
// в едит для наглядности заносим количество записанных байт (делал для себя,
// чтобы проверять, пишется звук или нет)
Edit1.Text:= inttostr(WaveHdr.dwBufferLength);
// меняем название кнопки, чтобы создать возможность прервать отправку пакетов
button1.Caption:='Остановить отправку'
end else Begin // если название кнопки «остановить отправку» то
//переименовываем её
button1.Caption:='Начать отправку';
//закрываем сокет клиента
IdUDPClient1.Active:=false;
//разгружаем буфер
waveInUnprepareHeader(Wavein,@WaveHdr,sizeof(TwaveHdr));
// приостанавливаем считывание. ЗАМЕТЬТЕ! ПРИОСТАНАВЛИВАЕМ! Если мы
// напишем waveInClose(Wavein), то устройство будет закрыто, и при повторном
// нажатии на кнопку, не будет никакого результата.
waveInStop(Wavein);
// смотрим кол-во не записанных байт
Edit1.Text:=inttostr(Wavehdr.dwBytesRecorded);
end
end;
Вы спросите, а что же такое waveInPrepareHeader? Это функция, выполняющая подготовку буфера для операции загрузки данных. Общий вид:
function waveInPrepareHeader(
hWaveIn: HWAVEIN;
lpWaveInHdr: PWaveHdr;
uSize: UINT
): MMRESULT; stdcall;
Здесь:
HWaveIn – идентификатор открытого устройства
LpWaveInHdr – адрес структуры WaveHdr
type TWaveHdr = record
lpData: PChar; { указатель на буфер}
dwBufferLength: DWORD; { длина буфера }
dwBytesRecorded: DWORD; { записанный байты }
dwUser: DWORD; { переменная для использования её пользователем }
dwFlags: DWORD; { флаги }
dwLoops: DWORD; { контролер }
lpNext: PWaveHdr; { переменная для драйвера }
reserved: DWORD; { переменная для драйвера }
end;
Здесь:
lpData – адрес буфера для загрузки данных
dwBufferLength – длина буфера в байтах
dwBytesRecorded – для режима загрузки данных определяет количество загруженных в буфер байт
dwUser – пользовательские данные
dwFlags – флаги. Могут иметь следующие значения: WHDR_DONE устанавливается
драйвером при завершении загрузки буфера данными
WHDR_PREPARED – устанавливается системой. Показывает готовность буфера к загрузке данных
WHDR_INQUEUE – устанавливается системой, когда буфер установлен в очередь
dwLoops – используется только при воспроизведении. При записи звука всегда 0
lpNext – зарезервировано
reserved – зарезервировано
uSize – размер структуры WaveHdr в байтах
Функция waveInPrepareHeader вызывается только один раз для каждого устанавливаемого в очередь загрузки буфера. Что такое waveInAddBuffer()? Функция waveInAddBuffer() ставит в очередь на загрузку данными буфер памяти. Когда буфер заполнен, система уведомляет об этом приложение:
function waveInAddBuffer(
hWaveIn: HWAVEIN;
lpWaveInHdr: PWaveHdr;
uSize: UINT
): MMRESULT; stdcall;
Здесь:
hWaveIn – идентификатор открытого Waveform audio устройства ввода
lpWaveInHdr – адрес структуры TWaveHdr
uSize – размер WaveHdr в байтах
Что такое waveInStart(), waveInStop(), waveInClose()? Общий вид записи таков:
function waveInStart(hWaveIn: HWAVEIN): MMRESULT; stdcall;
waveInStop(), waveInClose() имеют совершенно одинаковый параметр – как и WaveInStart(), которую описывать не имеет смысла, ибо и так понятно, что она начинает считывать данные с устройства ввода, а вот waveInClose() закрывает устройство для записи, и его снова придется открывать с помощью WaveInOpen(), но об этом ниже… А вот waveInStop(), ставит запись как бы на паузу, и нам не надо повторно использовать WaveInOpen().
Что такое waveInUnprepareHeader? Функция аналогичная waveInPrepareHeader(), однако она возвращает выделенную память на буфер, т.е. как бы «уничтожая» его.
Как узнать, что уже можно передавать данные?
Мы разобрали некоторые функции WIN API, относящиеся к вводу данных. Не устали? Нет? Тогда двигаемся дальше! Создадим собственную процедуру для определения завершения передачи данных в блок памяти посредством WaveInAddBuffer(). А выглядит она так:
procedure TForm1.OnWaveMessage(var msg:TMessage);
begin
waveInPrepareHeader(waveIn,@WaveHdr,sizeof(Twavehdr));
waveInAddBuffer(wavein,@WaveHdr,sizeof(TwaveHdr));
// отправляем буфер на сервер, где WaveHdr.lpData^ - это ссылка на память, где
// хранятся считанные с микрофона данные, уже преобразованные в
// последовательность нулей и единиц, WaveHdr.dwBufferLength – длина буфера данных
idUDPClient1.Sendbuffer(WaveHdr.lpData^,WaveHdr.dwBufferLength);
// В переменную заносим количество отправленных байт
Bytes:=Bytes+WaveHdr.dwBufferLength;
// Формат строки. Посмотрите в google фразу format дельфи
Caption:=Format ('%u',[Bytes]);
UpDate
end;
В этой процедуре используются уже известные вам функции, по этому второй раз описывать их не будем. Пишем её сразу после строки {$R *.dfm}. А описываем эту процедуру в разделе private класса TForm1 как:
procedure OnWaveMessage(var msg:TMessage); message MM_WIM_DATA;
Эта процедура будет выполняться каждый раз как только передача данных в буфер будет завершена и система сгенерирует сообщение WIM_DATA. Заполним обработчик события формы OnClose():
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// завершаем все действия
Action:= caFree;
// деактивируем сокет
IdUDPClient1.Active:=false;
// закрываем, теперь уже совсем, устройство записи
waveInClose(Wavein);
end;
И конечно же, заполним обработчик события формы OnCreate():
procedure TForm1.FormCreate(Sender: TObject);
begin
// with – оператор, благодаря которому можно не писать переменные, а указывать
// сразу их свойства. В данном случае WaveFormat: TWAVEFORMATEX – отвечает
// за сигнал, т.е. за все его характеристики, описан ниже.
with waveformat do begin
nChannels:=1;
wFormatTag:=WAVE_FORMAT_PCM;
nSamplesPerSec:=8000;
wBitsPerSample:=8;
nBlockAlign:=1;
nAvgBytesPerSec:=8000;
cbSize:=0;
end;
// для удобства загоняем размер буфера в переменную, которую будем вызывать
bufsize:= waveformat.nAvgBytesPerSec*2 div 16;
// размеру буфера сокета присваиваем размер буфера bufsize
IdUDPClient1.BufferSize:=bufsize;
// waveInOpen опишем чуть ниже, как и обещал, WAVE_MAPPER – система
// сама выбирает устройство
waveInOpen(@Wavein,WAVE_MAPPER,addr(waveformat),self.Handle,0,CALLBACK_WINDOW);
// выделяем память под заголовок буфера данных
WaveHdr.lpData:=Pchar(GlobalAlloc(GMEM_FIXED, bufsize));
// присваиваем длину буфера TWaveHdr’у
WaveHdr.dwBufferLength:=bufsize;
// сбрасываем флаги
WaveHdr.dwFlags:=0;
// устанавливаем порт подключения для клиента
IdUDPClient1.Port:= 10090
end;
Что же такое WaveInOpen()?
Функция waveInOpen() открывает имеющееся устройство ввода Waveform Audio для оцифровки сигнала. Типичная ее структура выглядит следующим образом:
function waveInOpen(
lphWaveIn: PHWAVEIN;
uDeviceID: UINT;
lpFormatEx: PWaveFormatEx;
dwCallback,
dwInstance,
dwFlags: DWORD
): MMRESULT; stdcall;
Здесь:
lphWaveIn – указатель на идентификатор открытого Waveform audio устройства. Идентификатор используется после того, как устройство открыто, в других функциях Waveform audio;
uDeviceID – номер открываемого устройства (см. waveInGetNumDevs). Это может быть также идентификатор уже открытого ранее устройства. Вы можете использовать значение WAVE_MAPPER для того, чтобы функция автоматически выбрала совместимое с требуемым форматом данных устройство;
lpFormatEx - указатель на структуру типа TWaveFormatEx
type TWaveFormatEx = packed record
wFormatTag: Word; { format type }
nChannels: Word; { number of channels (i.e. mono, stereo, etc.) }
nSamplesPerSec: DWORD; { sample rate }
nAvgBytesPerSec: DWORD; { for buffer estimation }
nBlockAlign: Word; { block size of data }
wBitsPerSample: Word; { number of bits per sample of mono data }
cbSize: Word; { the count in bytes of the size of }
end;
В этой структуре значения полей следующие:
wFormatTag – формат Waveform audio. Мы будем использовать значение WAVE_FORMAT_PCM
(это означает импульсно-кодовая модуляция) другие возможные значения
смотрите в заголовочном файле MMREG.H;
nChannels – количество каналов. Обычно 1 (моно) или 2(стерео);
nSamplesPerSec – частота дискретизации. Для формата PCM – в классическом смысле, т.е.
количество выборок в секунду. Согласно теореме отсчетов должна вдвое
превышать частоту оцифровываемого сигнала. Обычно находится в диапазоне от
8000 до 44100 выборок в секунду;
nAvgBytesPerSec – средняя скорость передачи данных. Для PCM равна nSamplesPerSec*nBlockAlign;
nBlockAlign – для PCM равен (nChannels*wBitsPerSample)/8;
wBitsPerSample – количество бит в одной выборке. Для PCM равно 8 или 16;
cbSize – равно 0. Подробности в Microsoft Multimedia Programmer’s Reference;
dwCallback – адрес callback-функции, идентификатор окна или потока, вызываемого при
наступлении события;
dwInstance – пользовательский параметр в callback-механизме. Сам по себе не используется
dwFlags – флаги для открываемого устройства:CALLBACK_EVENT dwCallback-параметр –
код сообщения (an event handle);
CALLBACK_FUNCTION dwCallback – параметр – адрес процедуры-обработчика
CALLBACK_NULL dwCallback – параметр не используется
CALLBACK_THREAD dwCallback – параметр – идентификатор потока команд;
CALLBACK_WINDOW dwCallback – параметр – идентификатор окна
WAVE_FORMAT_DIRECT если указан этот флаг, ACM-драйвер не выполняет преобразование данных
WAVE_FORMAT_QUERY функция запрашивает устройство для определения
поддерживает ли оно указанный формат, но не открывает его
Мы использовали callback функцию в OnWaveMessage(). В последнюю очередь я опишу переменные, которые использовались:
type
TForm1 = class(TForm)
IdUDPClient1: TIdUDPClient;
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
procedure OnWaveMessage(var msg:TMessage); message MM_WIM_DATA;
{ Private declarations }
public
{ Public declarations }
Wavein:HWAVEIN;
WaveHdr:TWaveHdr;
bufsize:Cardinal;
end;
var
Form1: TForm1;
WaveDataLength:integer;
bytes:integer;
device:word;
waveformat: TWAVEFORMATEX;
a:integer;
Так же для работы программы необходимо добавить модуль MMSystem в раздел uses. Клиент готов! Как видите, не так страшен черт, как его малюют! Перед тем как перейти к написанию сервера, я бы вам настоятельно рекомендовал бы покопаться в генофонде всех выше описанных функций и самостоятельно глубже разобраться в том, как они устроены. Так для более углубленного изучения, советую переворошить содержимое таких компонентов из серии ACM как AcmIn, AcmOut. Только самообучением можно чего-нибудь добиться.
А что же сервер?
С чистой перед клиентом совестью, можем приступить к написанию сервера! Возможно, эта процедура покажется вам более сложной, но, разобравшись в ней, вы поймете, что это не так. Единственное, что работать мы будем не с одним буфером, а с восьмью, для удобства воспроизведения звука. В один записываем, воспроизводим, очищаем, готовим, записываем и т.д. по очереди каждый из восьми. Так же будет рассмотрена работа с флагами (dwflags) и приема потока данных (TMemoryStream) на сервер. Приступим, нетерпеливые мои!
Как обычно, создадим новый проект и вынесем на форму компонент TMemo (name=memo1) (опять же-таки я использовал его в целях определения получения потока данных, перегоняя его в шестнадцатиричный формат), кнопку TButton и IdUDPServerSocket (см. рисунок 2):
Пожалуй, начнем с простого. Напишем ниже приведенный код в обработчике события OnClose() формы:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// завершаем действия
Action:= caFree;
// выключаем сервер
IdUDPServer1.Active:= False
end;
Далее займемся обработчиком события OnClick() кнопки TButton1 (см. код):
procedure TForm1.Button1Click(Sender: TObject);
begin
If button1.Caption='Включить сервер' then Begin
// активируем сокет сервера
IdUDPServer1.Active:= true;
button1.Caption:='Выключить сервер'
end else begin
// деактивируем сокет сервера
IdUDPServer1.Active:= false;
button1.Caption:= 'Включить сервер'
end
end;
Теперь напишем процедуру, которую мы будем использовать для воспроизведения принятого звука:
procedure TForm1.playsound(s:Tstream); // получаемый поток
Var // переменная типа сообщения
msg:Tmessage;
begin
// пока а не равно нашему количеству буферов выполняем следующее
While a<>CWaveBufferCount do Begin
// проверку пользовательской установки на то, что буфер готов к записи
If FHeaders[a].dwUser=0 then begin
// записываем в буфер данные из потока, пришедшего от клиента
s.Read(Fheaders[a].lpdata^,bufsize);
// процедура waveOutPrepareHeader аналогична процедуре waveInPrepareHeader
waveOutPrepareHeader(WaveOut,@FHeaders[a],sizeof(FHeaders));
// Процедура waveOutWrite аналогична процедуре waveInAddBuffer, только она
// осуществляет воспроизведение данных из буфера
waveOutWrite(WaveOut,@FHeaders[a],sizeof(FHeaders));
memo1.Lines.Add('...Двоичный код потока...');
// обнуляем флаги буфера/ов в цикле
FHeaders[a].dwFlags:= 0;
// уже знакомая нам структура
With FHeaders[a] do begin
dwBufferLength:= bufsize;
dwBytesRecorded:= 0;
dwUser := 0;
dwLoops:= 1;
// А вот здесь мы присваиваем флагу только что воспроизведенного буфера
// значение, которое отвечает за то что буфер установлен в очередь, т.е. мы как бы
// циклично используем эти 8 буферов
dwFlags:= WHDR_INQUEUE
end;
// Увеличиваем индекс, чтобы перейти к следующему буферу
inc(a);
// соответственно после воспроизведения и подготовки нам больше не нужен цикл и
// мы выходим из него
exit;
end
end
end;
Процедура разобрана, осталось ей воспользоваться… Как это осуществить? Все просто, достаточно в обработчике события OnUDPRead() idUDPServerSocket-a написать следующий код:
procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
Begin
// если мы воспроизвели последний буфер то, начинаем всё сначала (с первого)
If a = CWaveBufferCount then
a:= 0;
//вызываем нашу процедуру, в скобках пишем наш поток, пришедший на сервер,
//смотрите процедуру сокета.
playsound(Adata);
// определяем сколько байт мы приняли
Bytes:=Bytes + aData.Size;
// показываем это в названии формы
Caption:= 'Принятых байт' + Format('%u', [Bytes]);
// обновляем форму
UpDate
end;
И не забыть при создании формы проинициализировать наши аудиоустройства. Для этого в обработчике OnCreate() формы запишем:
procedure TForm1.FormCreate(Sender: TObject);
begin
bytes:= 0;
WaveOut:= 0;
With WaveFormatOut do begin
nChannels:= 1;
wFormatTag:= WAVE_FORMAT_PCM;
nSamplesPerSec:= 8000;
wBitsPerSample:= 8;
nBlockAlign:= 1;
nAvgBytesPerSec:= 8000;
cbSize:= 0
end;
bufsize:= WaveFormatOut.nAvgBytesPerSec*2 div 16;
For a:= 0 to CWaveBufferCount-1 do
With FHeaders[a] do begin
dwFlags:= WHDR_INQUEUE;
dwBufferLength:= bufsize;
dwBytesRecorded:= 0;
dwUser:= 0;
dwLoops:= 1;
GetMem(Fheaders[a].lpData, bufsize);
end;
IdUDPServer1.BufferSize:= bufsize;
IdUDPServer1.DefaultPort:= 10090;
waveOutOpen(@WaveOut, WAVE_MAPPER, @WaveFormatOut, self.Handle, 0, CALLBACK_WINDOW);
end;
Уважаемые читатели, здесь я пишу без комментариев только для того, что дать вам возможность самим додуматься, что здесь к чему, это не так сложно, тем более, что вы это уже все знаете (мы с вами выше подробно разбирали эти аналогичные функции ввода и вывода и работы с сокетами).
Далее осталось описать переменные и константы:
Const
CwaveBufferCount = 8;
type
TForm1 = class(TForm)
IdUDPServer1: TIdUDPServer;
Button1: TButton;
Memo1: TMemo;
procedure IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
procedure playsound(s:Tstream);
private
hdr: PwaveHdr;
{ Private declarations }
public
{ Public declarations }
WaveOut:HWAVEOUT;
WaveHdrOut,WaveHdrOut2:TWaveHdr;
WaveFormatOut:tWAVEFORMATEX;
bufsize:word;
FBuffer:Pointer;
FSndBuffer:Pointer;
FHeaders:array[0..CWaveBufferCount-1] of TWAVEHDR;
FBufSize:Cardinal;
end;
var
Form1: TForm1;
bytes:Cardinal;
WaveOut: HWAVEOUT;
WaveHdrOut,WaveHdrOut2: TWaveHdr;
WaveFormatOut: tWAVEFORMATEX;
bufsize:word;
a:integer;
Я не стал описывать процедуру перегонки потока в HEX-формат, так как писал ради передачи данных в TMемо. В конце концов, вы сами запросто можете убрать ненужные строки, относящиеся к ней.
Заключение
Хочу заметить, что размеры буферов сокетов на сервере и клиенте должны быть равны размерам буферов структуры TWaveHdr, иначе вы не получите никаких звуков на выходе, кроме шипения с прерываниями, равными по длительности размеру вашего воспроизводимого буфера. Также для более быстрой реакции на события приема звука используйте меньшие размеры буферов, но и соответственно увеличьте их количество (8-ми вполне хватит). При желании, лучше использовать динамический.
Статья была написана специально для форума Клуба ПРОграммистов www.programmersforum.ru. Исходники тестового проекта (клиента и сервера) прилагаются в виде ресурсов в теме «Журнал клуба программистов. Третий выпуск» или непосредственно в архиве с журналом [5].
Выражаю огромную благодарность человеку, чей ник на вышеуказанном форуме raxp, который активно помогал мне в изучении этого материала кодами и советами.
Ресурсы
. Азбука WIN API http://letitbit.net/download/1868.1502ee9dae8ee96cec9816babb/Azbuka_WIN_API.rar.html
. Описание звуковых функций http://www.delphikingdom.com/asp/viewitem.asp?catalogid=213
. Репозитарий Indy 9: https://svn.atozed.com:444/svn/Indy9 (имя пользователя: Indy-Public-RO)
. Репозитарий Indy 10: https://svn.atozed.com:444/svn/Indy10 (имя пользователя: Indy-Public-RO)
. Модули и проекты, использованные в статье http://programmersclub.ru/pro/pro3.zip
. Обсуждение на форуме разработки прототипа VoIP телефона
http://www.programmersforum.ru/showthread.php?t=91506
Это статья из третьего номера журнала “ПРОграммист”.
Скачать его можно по ссылке.
Ознакомиться со всеми номерами журнала.
Обсудить на форуме — Передача звука по сети. Прототип VoIP телефона
20th
Май
Взаимодействие с сетевыми интерфейсами в Linux
Linux программирование. Начинающим
Этой статьей я бы хотел открыть цикл публикаций, связанных с самым интересным и захватывающим в Linux – программировании. Я не собираюсь рассказывать о том, в чем писать программы, как их компилировать и запускать, информацию по данным вопросам найти очень легко и я думаю, что любой с этим справится. Языки программирования, которые будут использоваться в статьях: C, C++, bash script.
Олег Кутков http://www.programmersforum.ru/member.php?u=2583
Oleg Kutkov by elenbert@gmail.com
В последнее время программисты и простые пользователи начинают проявлять все больший интерес к Unix- подобным операционным системам, в частности к Linux. К сожалению, новичкам, не всегда просто разобраться в новой среде, даже не смотря на то, что Linux считается одной из самых хорошо документированных ОС. Информация, как правило, разбросана по форумам, множеству отдельных статей и блогов. Основное содержимое данных материалов касается администрирования и настройки дистрибутивов, программистам же, как правило, приходится довольствоваться man-документацией ил
и автоматической doxygen-документацией (документация, сгенерированная автоматически, на основе комментариев в исходном коде). К тому же, как это часто бывает – наиболее интересный материал на английском языке. Безусловно, данную ситуацию следует исправлять.
Начало. Общие сведения
Программировать в Linux очень просто и легко. Для программистов созданы практически идеальные условия: множество мощных инструментов, открытые исходные коды, сама организация системы, множество фреймворков. Работа с файлами, строками, массивами, классами, контейнерами,
в Unix- среде, практически ничем не отличается от таковой в Windows, это касается и множества других стандартных функций и библиотек. Различия начинаются на более низком уровне. Предлагаю разобраться, как работать с сетевыми интерфейсами.
Как известно, сетевые интерфейсы Linux обозначаются короткими строковыми именами – eth0, wlan0, lo и т.д. Интерфейсу можно присвоить любое удобное имя, но лучше руководствоваться общепринятыми правилами именования. Думаю, что ни для кого не секрет, что все устройства в Linux представлены в виде особых файлов в каталоге /dev, это справедливо для всех устройств, кроме сетевых адаптеров. Но так было не всегда, в прошлых версиях Linux ядра были доступны устройства /dev/eth0, /dev/tap0 и т.д., в более же новых ядрах эти устройства упразднили, и сетевые интерфейсы были перенесены в п
амять в так называемое пространство сокетов
Примечание.
* Следует сказать, что в другой популярной Unix – подобной ОС – FreeBSD по-прежнему, сохранен старый способ. Поэтому, если вы захотите переносить приложения с Linux на FreeBSD – следует учитывать это и другие мелкие различия. Но это не значит, что работа с этими устройствами каким-либо образом осложнилась, все очень и очень просто.
Далее я бы хотел рассмотреть особую и очень важную функцию, обеспечивающую обмен управляющими сообщениями между устройством и пользовательским приложениями.
Интерфейсы управления
Для взаимодействия с устройствами, а на самом деле с драйверами устройств, в Unix имеется особый вызов – ioctl, означающий Input-Output Control.
Справедливости ради, следует сказать, что Windows имеется подобный интерфейс – DeviceIoControl. Для использования данного вызова следует включить заголовочный файл <sys/ioctl.h>. Существует также возможность определять свои собственные ioctl вызовы, этим пользуются разработчики драйверов. Рассмотрим вызов ioctl детально…
int ioctl(int d, int request, …);
d – это открытый файловый дескриптор устройства.
request – это тип запроса, для различных устройств запросы различные, соответствующую информацию обычно легко найти в справочных материалов.
Примечание.
В этой статье я рассмотрю все типы запросов для сетевых устройств.
третий аргумент – это указатель на void, т.е. там могут оказаться какие угодно данные, в зависимости от того, что требует конкретный тип запроса. В случае успеха вызовом возвращается ноль. В случае ошибки возвращается «-1» и значение глобальной переменной errno устанавливается соответствующим образом. Чтобы было понятнее, пример использования:
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
int main()
{
int fd, serial, res; // дескриптор, параметр, результат
fd = open("/dev/ttyS0", O_RDONLY); // открываем устройство
if (fd < 0) { // проверяем дескриптор и в случае ошибки - выводим ее пользователю
printf("Открытие /dev/ttyS0 завершилось с ошибкой: %s\n", strerror(errno));
return 1;
}
res = ioctl(fd, TIOCMGET, &serial); // выполняем вызов ioctl с запросом TIOCMGET
if (res < 0) { // проверяем результат и в случае ошибки выводим
printf("Вызов ioctl завершился с ошибкой: %s\n", strerror(errno));
return 1;
}
if (serial & TIOCM_DTR) // проверяем результат
printf("Последовательный порт не готов\n");
else
printf("Последовательный порт готов\n");
close(fd); // закрываем дескриптор
return 0;
}
Сначала мы подключаем необходимые заголовочные файлы, в которых объявлены используемые нами функции, а так же ioctl запросы. В данном примере идет работа с последовательным портом компьютера, а точнее проверяется готовность приема данных. Вызов ioctl на открытом файловом дескрипторе, передает драйверу открытого устройства команду TIOCMGET, сохраняет и возвращает результат в переменную
Serial.
Аналогично происходит процесс передачи информации для драйвера из переменной параметра. Как уже говорил, тип переменной параметра зависит от типа запроса. Как правило, это специальная структура. Таков общий принцип работы ioctl, как видим – ничего сложного. Теперь перейдем непосредственно к теме обсуждения – сетевым интерфейсам…
Работа с сетевыми интерфейсами
Выше был рассмотрен пример использования вызова ioctl для получения данных из открытого дескриптора последовательного порта. Для сетевых интерфейсов работа выполняется аналогичная. Внимательный читатель мог обратить внимание, как выше я говорил о том, что для сетевых устройств не существует таких специальных файлов, соответственно eth0 нельзя открыть также, как и последовательный порт. Это так сетевые устройства перенесены в пространство сокетов и для доступа к этим устройствам следует использовать именно дескрипторы сокетов. Открытие сокета для упр
авления сетевым устройством выполняется так:
int sock = socket(AF_INET, SOCK_DGRAM, 0);
Результат sock и есть дескриптор сокета, перед использованием, его, как и все прочие дескрипторы, следует проверять на отрицательные значения, на случай возможных ошибок. Также, для ioctl вызовов на дескрипторе сокета применяется особая структура параметра (serail, в примере выше) – struct ifreq. Это очень важная структура, используемая во всех случаях работы с сетевыми устройствами. Разберем ее подробнее:
struct ifreq {
char ifr_name[IFNAMSIZ];
union {
struct sockaddr ifr_addr;
struct sockaddr ifr_dstaddr;
struct sockaddr ifr_broadaddr;
struct sockaddr ifr_netmask;
struct sockaddr ifr_hwaddr;
short ifr_flags;
int ifr_ifindex;
int ifr_metric;
int ifr_mtu;
struct ifmap ifr_map;
char ifr_slave[IFNAMSIZ];
char ifr_newname[IFNAMSIZ];
char * ifr_data;
};
};
Структура состоит из двух полей: имени интерфейса и объединения, каждое возможное поле, которого выражает конкретный параметр сетевого интерфейса. Данная структура позволяет, как получать параметр интерфейса, так и задавать его. Так как используется объединение – можно получать и задавать только
один параметр за раз.
Интересуемые и используемые поля:
ifr_addr – IP адрес интерфейса
ifr_dstaddr – адрес сервера (для Point-to-Point соединения)
ifr_broadaddr – широковещательный адрес интерфейса
ifr_netmask – маска подсети
ifr_hwaddr – mac адрес
ifr_ifindex – индекс интерфейса (внутри ядра сетевые интерфейсы имеют уникальные индексы, для упращения работы сетевой подсистемы)
ifr_flags – различные флаги (интерфейс поднят или опущен, интерфейс активен или неактивен и др.)
ifr_metric – метрика интерфейса
ifr_mtu – mtu интерфейса
ifr_map – структура, содержащая в себе техническую информацию (номер прерывания, память устройства и т.д.)
ifr_slave – подчиненное устройство
ifr_newname – новое имя интерфейса (для переименования)
Перед любым использованием структуры следует ее обязательно обнулять с помощью memset, а затем задавать имя интересуемого интерфейса ifr_name. Теперь перейдем от теории к действию – получим IP адрес интерфейса eth0! Соответствующий пример приведен ниже:
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <string.h>
#include <net/if.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int sock; // дескриптор сокета
struct sockaddr_in *in_addr; // структура интернет-адреса (поля)
struct ifreq ifdata; // структура - параметр
char *ifname = "eth0"; // имя интерфейса
sock = socket(AF_INET, SOCK_DGRAM, 0); // открываем дескриптор сокета
if (sock < 0) {
printf("Не удалось открыть сокет, ошибка: %s\n", strerror(errno));
return 1;
}
memset(&ifdata, 0, sizeof(ifdata)); // очищаем структуру
strncpy(ifdata.ifr_name, ifname, sizeof(ifname)); // задаем имя интерфейса
//получаем айпи адрес с помощью SIOCGIFADDR, одновременно проверяя результат
if (ioctl(sock, SIOCGIFADDR, &ifdata) < 0) {
printf("Не получить IP адрес для %s, ошибка: %s\n", ifname, strerror(errno));
close(sock);
return 1;
}
in_addr = (struct sockaddr_in *) &ifdata.ifr_addr; // преобразовываем из массива байт
// в структуру sockaddr_in
printf("Интерфейс %s IP адрес: %s\n", ifname, inet_ntoa(in_addr->sin_addr));
close(sock);
return 0;
}
В этом коде я ввел одну новую структуру и одну новую функцию. Начну со структуры:
struct sockaddr_in {
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Даная структура предназначена для хранения базовых данных об адресе сетевого узла. Назначение ее полей:
sin_family - семейство адресов, может иметь два значения: AF_INET для IPv4 и AF_INET6 для IPv6
sin_port - порт узла
sin_addr - структура адреса (о ней ниже)
sin_zero - этот массив можно использовать по своему усмотрению
struct in_addr {
unsigned long s_addr; // load with inet_pton()
};
Эта структура состоит всего из одного поля – числа, представляющего собой собственно IP адрес. Например, «192.168.8.98», в таком формате, имеет вид 1644734656. Функция inet_ntoa предназначена для преобразования такого числового значения в привычный цифро-точечный формат. В качестве аргумента, функция принимает struct in_addr, а возвращает указатель на строку.
Теперь скажу о преобразовании из массива байт в структуру sockaddr_in. Как было показано выше, поле ifr_addr, в структуре ifreq, имеет тип struct sockaddr. Эта структура является своего рода, «упрощенной» структурой sockaddr_in.
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
У нее всего два поля: семейство адресов и массив 14 байт, содержащий собственно адрес. Структуры sockaddr и sockaddr_in хорошо и естественно приводятся к типу друг друга, чем мы и воспользовались. Обратная операция – задание адреса выполняется примерно так же. Отличия только два: перед вызовом ioctl, полю ifr_addr нужно задать новое значение адреса, а тип запроса будет SIOCSIFADDR. Как видно, оба запроса SIOCSIFADDR и SIOCGIFADDR отличаются на одну букву, которая означает Set и Get, соответственно.
Я не буду приводить пример, показывающий, как задавать новое значение адреса, так как предоставленных сведений уже достаточного для того, что бы читатель разобрался сам. Дам лишь небольшую подсказку: для преобразования строкового значения адреса, например «192.168.8.98», в тип struct in_addr следует применять функцию inet_aton:
inet_aton(const char *saddr, struct in_addr *iaddr);
saddr – указатель на строку с адресом
iaddr – указатель на struct in_addr
Для получения (или задания) всех остальных параметров используется аналогичный способ, отличие лишь в типе запроса и использовании соответствующего поля в объединении структуры ifreq. Список всех возможных типов запроса для получения или задания параметров сетевого интерфейса:
SIOCGIFNAME – получить имя сетевого интерфейса
SIOCGIFINDEX – получить индекс сетевого интерфейса
SIOCGIFFLAGS, SIOCSIFFLAGS – получить/задать флаг интерфейса (о флагах ниже)
SIOCGIFMETRIC, SIOCSIFMETRIC – получить/задать метрику интерфейса
SIOCGIFMTU, SIOCSIFMTU – получить/задать mtu интерфейса
SIOCGIFHWADDR, SIOCSIFHWADDR – получить/задать mac адрес
SIOCGIFMAP, SIOCSIFMAP – получить/задать аппаратные параметры (struct ifmap)
Наиболее интересные флаги интерфейса:
IFF_UP – интерфейс запущен
IFF_BROADCAST – интерфейс является широковещательным
IFF_LOOPBACK – интерфейс является петлевым
IFF_POINTOPOINT – point-to-point интерфейс
IFF_RUNNING – интерфейс активен
IFF_MULTICAST – интерфейс поддерживает многоадресность
Небольшой пример использования флагов, получение информации о том, запущен ли интерфейс:
ioctl(sock, SIOCGIFFLAGS, &ifdata);
if (ifdata.ifr_flags && IFF_UP) {
printf("Сетевой интерфейс %s запущен\n", ifdata.ifr_name);
}
else {
printf("Сетевой интерфейс %s не запущен\n", ifdata.ifr_name);
}
На данном этапе предлагаю читателю самостоятельно написать небольшое приложение, получающее, в качестве аргумента командной строки, имя интерфейса и выводящее всю информацию о нем. Но как быть, когда заранее неизвестны имена интерфейсов, как получить просто список доступных сетевых интерфейсов? Очень просто. В помощь приходит замечательный вызов if_nameindex().
Небольшой пример, показывающий как получить список всех сетевых интерфейсов и их индексов.
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <string.h>
#include <net/if.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int sock; // дескриптор сокета
struct sockaddr_in *in_addr; // структура интернет адреса (поля)
struct ifreq ifdata; // структура - параметр
struct if_nameindex* ifNameIndex; // структура интерфейсов и их индексов
sock = socket(AF_INET, SOCK_DGRAM, 0); // открываем дескриптор сокета
if (sock < 0) {
printf("Не удалось открыть сокет, ошибка: %s\n", strerror(errno));
return 1;
}
ifNameIndex = if_nameindex();
if (ifNameIndex) { // если удалось получить данные
while (ifNameIndex->if_index) { // пока имеются данные
memset(&ifdata, 0, sizeof(ifdata)); // очищаем структуру
strncpy(ifdata.ifr_name, ifNameIndex->if_name, IFNAMSIZ); // получаем имя следующего интерфейса
// получаем IP адрес с помощью SIOCGIFADDR, одновременно проверяя результат
if (ioctl(sock, SIOCGIFADDR, &ifdata) < 0) {
printf("Не получить IP адрес для %s, ошибка: %s\n", ifdata.ifr_name, strerror(errno));
close(sock);
return 1;
}
// преобразовываем из массива байт в структуру sockaddr_in
in_addr = (struct sockaddr_in *) &ifdata.ifr_addr;
printf("Интерфейс %s индекес %i IP адрес: %s\n", ifdata.ifr_name, ifNameIndex->if_index, inet_ntoa(in_addr->sin_addr));
++ifNameIndex; // переходим к следующему интерфейсу
}
}
close(sock);
return 0;
}
Данный пример является доработанной версией предыдущего примера, выводя все доступные интерфейсы, их индексы и IP адреса. На рисунке ниже изображен процесс компиляции и запуска приложения с выводом всей информации. Видно, так же, сообщение об ошибке, для виртуального интерфейса, с которого не удалось получить информацию:
Заключение
В данной статье я постарался раскрыть основные аспекты взаимодействия с сетевыми интерфейсами в OC Linux, многое из этого будет справедливо и для других Unix подобных операционных систем. Надеюсь, что статья получилась интересной и полезной.
Напоследок не могу не сказать, что в Linux, работа с сетью через ioctl является устаревшим способом, на смену которого приходит Netlink. Netlink – это модуль, состоящий из двух частей, одна находится в ядре и взаимодействует непосредственно с соответствующими подсистемами, вторая – библиотека на уровне пользователя. Обе части могут обмениваться информацией между собой. Это очень мощный и удобный способ управлениями всеми параметрами сетевой подсистемы Linux, такие издания, как «Linux journal» рекомендуют пользоваться только Netlink. К сожалению, документации по данному API н
е так много, как хотелось, и приходится собирать по крупицам. Но в будущих статьях я постараюсь рассмотреть некоторые аспекты использования Netlink, так как уже имею опыт данной сфере. До встречи на страницах журнала!
Литература
. Исходные коды пакета утилит net-tools
. Doxygen документация
. Christian Benvenuti. Understanding Linux network internals. – O’reilly Media, 2005
http://www.linbai.info/computers-it/understanding-linux-network-internals.html
. Интернет ресурсы: www.linuxjournal.com, www.stackoverflow.com, различные форумы
Это статья с третьего выпуска журнала “ПРОграммист”.
Скачать этот выпуск можно по ссылке.
Ознакомиться со всеми номерами журнала.
Обсудить на форуме — Взаимодействие с сетевыми интерфейсами в Linux
21st
Апр
Мониторинг Counter Strike
Есть локальный сервер, вопрос: как достать с него информацию о текущей игре:
— карту
— игроки
— боты
и т.д.?
Эти и другие вопросы решают и обсуждают на форуме.
Облако меток
css реестр ассемблер timer SaveToFile ShellExecute программы массив советы word MySQL SQL ListView pos random компоненты дата LoadFromFile form база данных сеть html php RichEdit indy строки Win Api tstringlist Image мысли макросы Edit ListBox office C/C++ memo графика StringGrid canvas поиск файл Pascal форма Файлы интернет Microsoft Office Excel excel winapi журнал ПРОграммист DelphiКупить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)