Файл с расширением sock

Обновлено: 07.07.2024

SOCKS - это протокол проксирования (посредования) соединений между клиентом и сервером, который призван обеспечить максимальную прозрачность для приложений. SOCKS в целом разрабатывался с целью обеспечения контролируемых соединений между хостами в приватной сети, которые находятся за фаерволом, и хостами в публичной сети Интернет. В настоящее время используемая версия протокола - это SOCKS версии 5, которая была разработана в 1996 году, и спецификация которой представлена в RFC1928. SOCKS был популярен как метод выхода в “большую сеть” до нынешнего NAT. Если сравнивать SOCKS с NAT, то можно отметить следующие моменты:

SOCKS работает как “прослойка” между приложением и траспортом, и на модели OSI его принято ставить на сессионный уровень. Отсюда возникает необходимость, чтобы само приложение поддерживало SOCKS, что может быть далеко не всегда. А еще возникает проблема распространения настроек SOCKS-сервера на хосты, что часто делается просто ручным вводом в приложение. В NAT такой проблемы не возникает. У хоста есть IP, шлюз по умолчанию и DNS-сервер (которые чаще всего приходят по DHCP), и для него взаимодейсвие с сетью Интернет прозрачно. Всей работой по трансляции адресов и удержанием сессий занимается устройство на периметре внутренней сети (обычно роутер или фаервол).

Но с другой стороны, SOCKS, так сказать, никого не обманывает. Когда устанавливается SOCKS-сессия, то SOCKS-прокси сообщает клиенту внешний IP-адрес и порт, который закреплен за сессией клиента. Это открывает возможность таким протоколам как FTP или SIP, которые содержат в теле своих пакетов инфомацию о IP-адресации, подставлять правильные данные сразу на выходе от клиента, без трансформации пакета устройствами “по пути”. Это также позволяет более простую эксплуатацию p2p-приложений, и стоит упомянуть поддержку со стороны SOCKS “реверс-прокси” и протокола UDP. В NAT чаще всего для такого включаются дополнительные фичи “сверху” основных функций, которые лезут в пакеты уровня приложения и трансформируют их. В вопросе p2p-приложений в случае маленькой сети проблема решается протоколом UPnP, который может не всегда поддерживаться периметровым устройством или самим приложением. А в жизни часто требуется ручной проброс портов.

С некоторой точки зрения, SOCKS обеспечивает больший контроль за трафиком (из-за самого факта разрыва соединения), а также поддерживает аутентификацию пользователя. В случае NAT контроль обеспечивается меньший, и нет аутентификации (если без применения дополнительных методов).

Но NAT естественным образом победил - несмотря на некоторые недостатки, эксплуатировать его оказалось гораздо легче, чем SOCKS, да и работает NAT быстрее.

После распространения NAT, SOCKS используется чаще как средство сокрытия реальных IP-адресов, обхода ограничений доступа в Интернет, а также как удобный транспорт для состыкования приложений и оверлейных сетей, таких как TOR и I2P. Все браузеры и многие IM-приложения имеют в своем распоряжении SOCKS-клиент.

Какие недостатки имеет SOCKS с точки зрения проксирования? Как минимум - SOCKS, в большинстве реализаций, не шифрует проксируемый трафик. Конечно, можно сказать, что поддержка шифрования - задача самого приложения или “прослойки” уровня выше, что чаще всего TLS/SSL. Это, в целом, правильно, но тут возникает другая проблема - проблема идентификации трафика средствами DPI (как самого SOCKS, так и проксируемых приложений), что может вести к ограничению применения SOCKS в некоторых сетях. И все-таки отсутствие универсальности шифрования для трафика, в том случае, если прокси используется как метод доступа к внутренним ресурсам приватной сети (если применение VPN невозможно или неоптимально по каким-либо причинам). И более того - при использовании метода аутентификации 0x02 “логин/пароль”, эти логин/пароль улетают на сервер в виде открытого текста, что вызывает риск их перехвата. Для правоты стоит сказать, что для безопасной аутентификации и защиты трафика, SOCKS поддерживает GSSAPI. Только вот GSSAPI штука довольно старая и сложная в настройке, и я, честно говоря, пока что не видел его поддержки в каком-либо приложении при настройке SOCKS, кроме curl.

Как же можно подключаться к прокси с шифрованием? Например, использовать S… SSH!

SSH-тоннели

SSH, как известно, поддерживает проксирование TCP-трафика “внутри” текущего SSH-соединения. Таких типов проксирования три:

  1. Local Port Forwarding
  2. Remote Port Forwarding
  3. Dynamic Port Forwarding

Первые 2 рассматривать в данной статье я не буду, но остановлю внимание на типе Dynamic Port Forwarding. SSH Dynamic Port Forwarding позволяет сделать проксирование SOCKS-over-SSH. Работает это так - при соединении SSH-клиента и SSH-сервера со стороны SSH-клиента поднимается SOCKS-прокси, например, на localhost’е, на который можно указывать приложениям с поддержкой SOCKS. Само проксирование будет через SSH-сервер, с которым вы соединяетесь. В сумме - Интернет вас будет видеть от имени SSH-сервера, соединение между SSH-клиентом и SSH-сервером зашифровано, так что не видно вложенных данных приложения, а для приложения все выглядит как обращение к обычному SOCKS-прокси.

socks-over-ssh

На Linux выполняется это очень просто:

Чтобы поднять SOCKS-прокси на локалхосте, обычно делают так:

Приложению остается указать SOCKS-сервер localhost (127.0.0.1) и порт 1080, без аутентификации. Например, в Firefox вот так:

ff-socks-over-ssh


Хороший тон - это поставить проксирование DNS-запросов!

А еще лучше для большего контроля и удобства рекомендую использовать плагин FoxyProxy, использование которого описано в статье “Настройка универсального узла связи для выхода в оверлейные сети” (см. раздел по настройке I2P)

Чтобы расшарить прокси во внешнюю сеть, например устройствам в локальной сети, можно поставить адрес внешнего интерфейса:

Устройствам в локальной сети и самой локальной машине нужно будет обращаться к 192.168.1.100 в данном случае. Только не забудьте настроить локальный фаерволл, если он есть, так как он может блокировать обращения извне.

Работает эта фишка и на Windows - достаточно настроить клиент PuTTY. На Andoid поддержка есть в JuiceSSH, но только в платной версии. Хотя возможно есть аналоги.

Также стоит рассмотреть фичу SSH “PermitTunnel”, которая позволяет поднять полноценный L3-тоннель. Описана фича, например, тут. Но многие жалуются, что из-за TCP-over-TCP сильно проседает производительность, и рекомендуется как временное решение.

Какие же недостатки у SSH Dynamic Port Forwarding?

SSH будет плохо работать в условиях мобильной сети - потребуются постоянные ручные реконнекты;

SSH легко идентифицируется средствами DPI;

Видел информацию, что SSH-прокси развивает довольно маленькую скорость доступа, но сам я не проверял;

Для пользователей, плохо знакомых с командной строкой и SSH, может быть достаточно сложно;

По моему есть проблема разграничения доступа. Хотелось бы некоторым пользователям давать доступ к прокси и только к прокси, выдав некоторые простые настройки (IP, порт, пароль). В случае SSH потребуется настройка разграничений доступа по пользователям и настройка SSH-сервера.

И вот на этом моменте перейдем к рассмотру такого инструмента, как Shadowsocks.

Shadowsocks

A secure socks5 proxy, designed to protect your Internet traffic.

1) Super Fast. Bleeding edge techniques using Asynchronous I/O and Event-driven programming.

Отмечу, что в момент нагрузки ни сервер ни клиент много ресурсов не потребляют:

shsocks-cpu


Нагрузка на сервере

shsocks-cpu-client


Нагрузка на клиенте

2) Flexible Encryption. Secured with industry level encryption algorithm. Flexible to support custom algorithms.

Ну, список алгоритмов стандартной поставки действительно немаленький:

Такой список алгоритмов поддерживается с целью подбора оптимального под большее число устройств. Например, для процессоров без аппаратной поддержки AES может быть более оптимален chacha20, и наоборот. Вообще, шифрование тут больше не для сокрытия трафика, а для обхода средств DPI. Подробнее об этом в следующем пункте, а об обфускации трафика в следующем разделе.

3) Mobile Ready. Optimized for mobile device and wireless network, without any keep-alive connections.

Shadowsocks, в отличие от SSH, не обязательно удерживать текущее TCP-соединение. Вы можете спокойно переходить из одной сети в другую (из сотовой в WiFi и обратно, менять WiFi-сеть) и при этом ручного переподключения или переподключения по тайм-ауту не требуется. На каждое новое соединение shadowsocks открывает новую TCP-сессию, также как и обычный SOCKS. Первоначальная установка соединения, аутентификация, согласование параметров - этого в shadowsocks не требуется вовсе. Данные, главным образом, шифруются симметричным ключом, который указывается в конфиге на стороне сервера и клиента. Клиент “вслепую” шифрует данные ключом, указанным в конфиге и посылает серверу, без всякого согласования. И если ключ не совпадает и shadowsocks-сервер не может расшифровать данные - сервер просто сбросит соединение, ответив RST-пакетом. Да, ну и можно упомянуть, что один ключ может работать с любым количеством устройств - никакой конкуренции между устройствами нет.

4) Cross Platform. Available on most platforms, including Windows, Linux, Mac, Android, iOS, and OpenWRT.

Не брешут. Клиентов и серверов действительно большое разнообразие. Linux, Windows, Android - имеются. Есть даже на OpenWRT.

5) Open Source. Totally free and open source. A worldwide community devoted to deliver bug-free code and long-term support.

Опенсурс. Что еще добавить?

6) Easy Deployment. Easy deployment with pip, aur, freshports and many other package manager systems.

Развертка действительно очень простая. По крайней мере, я настраивал быстрее, чем danted. Покажу на примере Ubuntu Server 16.04. Для начала несколько слов о работе. Принцип примерно такой же, как и у SSH - на клиентской машине поднимается локальный SOCKS-прокси, на который ссылаются приложения, а shadowsocks обеспечивает шифрованный транспорт между клиентом и прокси-сервером.

socks-over-shsocks


Заметьте, что shadowsocks тут не “стрелочка”, а просто “труба”, потому что shadowsocks своих собственных поверхностных соединений, которые требуется удерживать, в отличие от SSH, не устанавливает.

Устанавливаем из стандартного репо shadowsocks на С - shadowsocks-libev:

Открываем конфиг файл по умолчанию /etc/shadowsocks-libev/config.json и задаем несколько параметров

По порядку:
server - внешний адрес, который будет прослушиваться сервером;
server_port - внешний порт, который будет прослушиватся сервером;
password - общий ключ-пароль между клиентом и сервером;
method - алгоритм шифрования.

И все, на этом закончили c сервером. Переходим к клиенту.

Устанавливаем на машине-клиенте тот же пакет:

Делаем конфиг-файл, например в домашней директории -

Единственное отличие - это наличие local_port . Настройка указывает, какой порт на локалхосте будет использовать локальный SOCKS-сервер.

Теперь можем увидеть, что на локалхосте на указанном порту поднялся наш локальный SOCKS-сервер:

Приложениям, проксируемым через SOCKS, указываем SOCKS-прокси с адресом 127.0.0.1 и портом из local_port - 1080 . Для FF например нужно задать настройки как на скриншоте выше. ss-local при желании можно например загнать в cron или в штатный для системы автостарт, чтобы он сразу стартовал при запуске машины. Быстро проверить работу прокси можно например вот так:

Теперь насчет мобильных девайсов. Я использую бета-версию Shadowsocks на Android. Удобные фичи - несколько режимов работы (прокси, VPN, transproxy), проксирование отдельных приложений, чтение конфига сервера из QR-кода. По последнему - насколько я знаю, генерировать QR можно в GUI-версиях Shadowsocks, а можно и такой простой командой:

Только подставьте свои алгоритм, пароль, IP и порт сервера.

SOCKS можно расшарить во внешнюю сеть, просто привязав SOCKS-сервер на клиентской машине к адресу внешнего интерфейса. Удобно, если вы запускаете shadowsocks на сервере в своей локальной сети.

Обфускация трафика

В первую очередь на обеих сторонах нужно установить плагин:

Второй момент - чтобы разрешить прослушивать серверу ss-server порты <1024 без запуска из-под root, нужно выполнить следующую команду:

После перезапускаем сервер:

Теперь конфиг со стороны клиента, например создаем файл

Запускаем клиента, проверяем работу прокси:

На стороне клиента меняется следующее (сделаем например еще один файл

Запускаем клиент, проверяем работу прокси:

shsocks-tls-proxy

Если залезть в дамп трафика, то вот например “Client Hello” обфусцированного трафика:

Теперь насчет мобильного клиента. На Android плагин simple-obfs к Shadowsocks называется как Simple Obfuscation. Устанавливаете и задаете в нем такие же параметры, как и в случае конфигов выше. А точнее там настраивается два параметра - метод обфускации и obfs-host .

Чтобы несколько вариантов настроек shadowsocks на сервере работали параллельно, можно например создать под каждый вариант юнит в systemd.

01b24c42909bceefb43c03ddc40e5191.jpg

Сразу отмечу главное: никакое централизованное решение высокий уровень анонимности обеспечить не может, так как необходимо доверять центральному узлу.
Мы не будем рассуждать об организационных, политических и бюрократических сложностях на пути раскрытия анонимности.
Возможно, VPN-сервер в Панаме действительно более безопасен, чем такой же сервер в Испании. А возможно — нет.
Также как и не будем говорить про цепочки узлов, так как их надежность с трудом поддаётся оценке. С одной стороны, в виду организационных сложностей, риск раскрытия ниже, а с другой — мы должны быть достаточно уверены в каждом узле.
Перейдём к конкретике.

  • X-Forwarded-For: client1, proxy1, proxy2 …
  • X-Forwarded-For: 169.78.138.66, 169.78.64.103.
  • SOCKS4. Такие серверы требуют от клиента, например, веб-браузера, только ip-адрес ресурса, к которому он обращается (адресата). Следовательно, клиенту надо как-то этот ip-адрес узнать, а узнать его клиент может только прямым DNS-запросом в обход прокси. Это может привести к деанонимизации, так как интернет-провайдер может видеть DNS-запросы в открытом виде, данная уязвимость называется DNS-leaks, она описана далее, во второй части статьи.
  • SOCKS4a. Является расширением SOCKS4. Главное отличие состоит в том, что SOCKS4a-сервер принимает от клиента только DNS-имя адресата, а не его ip-адрес. Это бывает необходимо, когда клиент не может самостоятельно определить ip-адрес адресата по DNS-имени.
  • SOCKS5. Также является расширением SOCKS4. Сервер SOCKS5 поддерживает UDP, IPv6, авторизацию и пр. И хотя SOCKS5-прокси могут принимать от клиента как ip-адрес, так и DNS-имя целевого ресурса, некоторые приложения, поддерживающие SOCKS5, могут сами получать ip-адрес адресата до того, как обратиться к SOCKS5-прокси, что также может привести к утечке DNS-запросов.

SSH туннель — это туннель, создаваемый посредством SSH-соединения и используемый для шифрования передаваемых данных. Как гласит одноимённая статья в Википедии: «SSH (англ. Secure SHell — «безопасная оболочка») — сетевой протокол прикладного уровня, позволяющий производить удалённое управление операционной системой и туннелирование TCP-соединений (например, для передачи файлов)».
При использовании SSH-туннеля открытый траффик какого-либо протокола шифруется на одном конце SSH-соединения, клиенте, и расшифровывается на другом, SSH-сервере.
Схема работы SSH-туннеля показана на рисунке:

635b6bc6cc4be3a0e96f4b90ccb95499.jpg

  • VPN призван обеспечить защищённый удалённый доступ к ресурсам корпоративной сети. Как только компьютер подключается к VPN-серверу, он становится частью «локальной» сети, а, следовательно, может получать все её сервисы: общие ресурсы, локальный сервис VoIP, также становятся возможными NetBios-, UDP-, и широковещательные запросы, единые VPN-политики и т.д. Через VPN в большинстве случаев отправляется траффик всей операционной системы и приложений.
  • SSH изначально предназначался для защищенного удаленного управления устройствами. SSH-соединение — это соединение с «конкретным устройством», а не с «сетью». Хотя мастера SSH могут делать с помощью него много крутых вещей.

Удобство
Удобство — понятие условное и субъективное, оно зависит от ваших целей и опыта.

69d7ff095b988d3c626be33fec126060.jpg

К VPN-серверу легко подключиться, но для новичков может быть непросто его настроить.
Тогда как SSH-сервер более прост в настройке, но, например, вручную настраивать SSH-туннель для каждого приложения кому-то может показаться не совсем удобным.

  • network — 96.5 Mbps.
  • network/SSH — 94.2 Mbps.
  • network/VPN — 32.4 Mbps.

Полезный совет

Иногда бывает ситуация, когда VPN-соединение по каким-либо причинам может разрываться. Если в случае с прокси-сервером, сетевое взаимодействие прекращается, то в случае с VPN траффик продолжит идти напрямую. Наиболее надёжным вариантом для недопущения этого является использование таблицы маршрутизации, где в качестве основного шлюза по умолчанию указан только шлюз VPN-сервера.
Делается это просто:
1. Удаляем любые маршруты по умолчанию:

19a8a2ff6538873fed0a998a7e6291bd.jpg


2. Разрешаем доступ в интернет только к адресу VPN-сервера:

ab17dad1f2f5317944b3e3d53ecc1e87.jpg


3. Добавляем маршрут по умолчанию со шлюзом – VPN-сервером:

e3d7ff5e7d6c1b1b55edeeee0956defb.jpg

Где: 192.168.0.1 — шлюз интернета, 55.55.55.55 — VPN-шлюз.
Еще одним способом является установка в свойствах открытого интернет-соединения несуществующих DNS-серверов, например, 127.0.0.1. В таком случае веб-сёрфинг и другие подобные задачи становятся невозможными без подключения к VPN-серверу.
Также существуют специальные программы, например, VPN-watcher, которые для заданных приложений проверяет VPN-соединение несколько раз в секунду и приостанавливает их работу, если VPN-соединение обрывается.
Спасибо за еще один способ Pongo: "Еще один способ обезопасить себя от разрыва vpn — это настройка файрвола. Подойдет в том числе и стандартный windows firewall. Есть инструкция с картинками. Причем блокирующие правила можно не создавать, а ограничиться 10-м пунктом. Для отдельных программ (например для openvpn) можно отдельно создать разрешающие правила, чтобы эти программы работали даже если впн не подключен."
Спасибо за еще один способ amarao: "Я думаю, если строить защищённую конструкцию, то следует просто выделять две сессии — защищённую и не защищённую. Лидера сессии положить в cgroups, откуда не-vpn интерфейс просто не доступен для использования — в этом случае информация будет отправляться только через этот интерфейс."

Деанонимизирующие данные и возможные уязвимости

Посмотрим, какую идентификационную информацию о себе мы можем передать в интернет. Я не буду рассматривать уязвимости (в том числе и 0day) в программах, эксплуатация которых может привести вообще к полному контролю за компьютером.

9251965e9e6274593b87c65b99d6bae5.jpg

IP-адрес. Самый «популярный» идентификатор в сети. Его ценность может быть разной в различных ситуациях, но как правило именно раскрытием ip-адреса принято пугать сетевых «анонимусов».
Решение: со скрытием ip-адреса справляются средства, описанные в первой статье: "Методы анонимности в сети. Просто о сложном"

Профилирование возникает, когда большая часть траффика долго выходит в интернет через один узел, например, Тоr. Тогда появляется возможность отнести увиденную активность к одному псевдониму. Выходной узел может и не знать ваш ip-адрес, но будет знать, что вы делаете.
Решение: не использовать постоянные цепочки Tor, регулярно менять выходные узлы (VPN-серверы, прокси-серверы), либо, забегая вперёд, использовать дистрибутив Whonix.

MitM-атаки направлены на прослушивание и модификацию траффика на выходном узле, например Tor или любом прокси-сервере. Интересным вариантом является модификация выходным узлом цифровых подписей, GPG- или SSL-отпечатков, хеш-сумм скачиваемых файлов.
Решение: быть внимательным при появлении предупреждений о валидности сертификатов и ключей.

Деанонимизирующая активность в анонимном сеансе. Например, когда клиент из анонимного сеанса заходит на свою страницу в соцети, то его интернет-провайдер об этом не узнает. Но соцсеть, несмотря на то, что не видит реальный ip-адрес клиента, точно знает, кто зашёл.
Решение: не допускать никакой левой активности в анонимном сеансе.

Одновременное подключение по анонимному и открытому каналу. В таком случае, например, при обрыве интернет-соединения, оборвутся оба соединения клиента с одним ресурсом. По данному факту серверу будет нетрудно вычислить и сопоставить два одновременно завершенных соединения и вычислить реальный адрес.
Решение: не допускать одновременного подключения к ресурсу по анонимному и открытому каналу.

Определение авторства текста. Подробнее здесь. Приложение может сравнить текст написанный анонимно и другой открытый текст, точно принадлежащий автору, и определить с высокой степень вероятности совпадение авторства.
Решение: шутки-шутками, но эта тема пока не достаточно изучена. Можно посоветовать прятать текст, который можно однозначно связать с вами. Тогда не с чем будет сравнивать и анонимный текст.

MAC-адрес сетевого интерфейса становится известен wi-fi точке доступа при подключении к ней клиента.
Решение: если переживаете за то, что точка доступа запомнит MAC-адрес вашего интерфейса, просто поменяйте его до подключения.

5f4aae1fe43b6fc85b695ab26b1bf931.jpg

Что могут рассказать Браузеры?

Скрипты Javascript, исполняемые на стороне клиента, могут собрать для сервера еще больше информации, в том числе и явно его идентифицирующей. Более того, если посещаемый нами сайт подвержен XSS, то включенные на нём скрипты Javascript помогут злоумышленнику провести успешную атаку со всеми вытекающими последствиями.

Web Bugs — это невидимые детали веб-страниц, используемые для мониторинга посещений сайта, способны дополнительно отсылать серверу разные данные о клиенте. Web Bugs от Гугла широко распространены по всему интернету.

1bbd4aecd697b43b3fbc9e4507b29b8f.jpg

Спасибо за внимание! Буду рад конструктивным комментариям и уточнениям.
UPDATE: В следующей статье я расскажу про схему "при которой и пользоваться интернетом не напряжно и никаких следов такого рода не остаётся". А именно: разберу настройки веб-браузера, касающиеся анонимности, на примере Firefox.

По мере того, как программы становятся больше (и используют больше файлов), становится всё более утомительным давать предварительные объявления каждой функции, которую вы хотите использовать, и которая определена в другом файле. Было бы неплохо, если бы вы могли поместить все свои предварительные объявления в одно место, а затем импортировать их, когда они вам понадобятся?

Исходные файлы кода C++ (с расширением .cpp ) – это не единственные файлы, которые обычно встречаются в программах на C++. Другой тип файлов – это заголовочный файл (иногда просто заголовок). Заголовочные файлы обычно имеют расширение .h , но иногда вы можете встретить их с расширением .hpp или вообще без расширения. Основная цель заголовочного файла – распространять объявления в исходные файлы кода.

Ключевой момент

Заголовочные файлы позволяют нам размещать объявления в одном месте, а затем импортировать их туда, где они нам нужны. Это может сэкономить много времени при наборе текста в проектах из нескольких файлов.

Использование заголовочных файлов стандартной библиотеки

Рассмотрим следующую программу:

Эта программа печатает « Hello, world! » в консоль с помощью std::cout . Однако эта программа никогда не предоставляла определение или объявление для std::cout , поэтому как компилятор узнает, что такое std::cout ?

Ключевой момент

Когда дело доходит до функций и переменных, стоит помнить, что заголовочные файлы обычно содержат только объявления функций и переменных, а не их определения (в противном случае может произойти нарушение правила одного определения). std::cout объявлен в заголовке iostream, но определен как часть стандартной библиотеки C++, которая автоматически подключается к вашей программе на этапе линкера.

Рисунок 1 Диаграмма процесса сборки

Рисунок 1 – Диаграмма процесса сборки

Лучшая практика

Заголовочные файлы обычно не должны содержать определений функций и переменных, чтобы не нарушать правило одного определения. Исключение сделано для символьных констант (которые мы рассмотрим в уроке «4.14 – const, constexpr и символьные константы»).

Написание собственных заголовочных файлов

А теперь вернемся к примеру, который мы обсуждали в предыдущем уроке. Когда мы закончили, у нас было два файла, add.cpp и main.cpp , которые выглядели так:

(Если вы воссоздаете этот пример с нуля, не забудьте добавить add.cpp в свой проект, чтобы он компилировался).

В этом примере мы использовали предварительное объявление, чтобы при компиляции main.cpp компилятор знал, что такое идентификатор add . Как упоминалось ранее, добавление предварительных объявлений для каждой функции, которую вы хотите использовать, и которая находится в другом файле, вручную может быстро стать утомительным.

Давайте напишем заголовочный файл, чтобы избавиться от этого бремени. Написать заголовочный файл на удивление легко, поскольку файлы заголовков состоят только из двух частей:

  1. защита заголовка, о которой мы поговорим более подробно в следующем уроке («2.11 – Защита заголовков»);
  2. фактическое содержимое файла заголовка, которое должно быть предварительными объявлениями для всех идентификаторов, которые мы хотим, чтобы другие файлы могли видеть.

Добавление заголовочного файла в проект работает аналогично добавлению исходного файла (рассматривается в уроке «2.7 – Программы с несколькими файлами исходного кода»). Если вы используете IDE, выполните такие же действия и при появлении запроса выберите Файл заголовка (или C/C++ header) вместо Файла С++ (или C/C++ source). Если вы используете командную строку, просто создайте новый файл в своем любимом редакторе.

Лучшая практика

При именовании файлов заголовков используйте расширение .h .

Заголовочные файлы часто идут в паре с файлами исходного кода, при этом заголовочный файл предоставляет предварительные объявления для соответствующего исходного файла. Поскольку наш заголовочный файл будет содержать предварительное объявление для функций, определенных в add.cpp , мы назовем наш новый заголовочный файл add.h .

Лучшая практика

Если заголовочный файл идет в паре с файлом исходного кода (например, add.h с add.cpp ), они оба должны иметь одинаковое базовое имя ( add ).

Вот наш завершенный заголовочный файл:

Следовательно, наша программа будет правильно компилироваться и компоноваться.

Рисунок 2 Диаграмма процесса сборки

Рисунок 2 – Диаграмма процесса сборки

Включение заголовочного файла в соответствующий исходный файл

Позже вы увидите, что большинство исходных файлов включают свой соответствующий заголовочный файл, даже если он им не нужен. Зачем?

Включение заголовочного файла в исходный файл увеличивает прямую совместимость. Очень вероятно, что в будущем вы добавите больше функций или измените существующие таким образом, что им нужно будет знать о существовании друг друга.

Когда мы углубимся в изучение стандартной библиотеки, вы будете включать множество заголовочных файлов библиотек. Если вам потребовалось включение в заголовочном файле, оно, вероятно, понадобилось вам для объявления функции. Это означает, что вам также потребуется такое же включение в исходный файл. Это приведет к тому, что в исходном файле у вас будет копия включений заголовочного файла. Включив заголовочный файл в исходный файл, исходный файл получит доступ ко всему, к чему имел доступ заголовочный файл.

При разработке библиотеки включение заголовочного файла в исходный файл может даже помочь в раннем обнаружении ошибок.

Лучшая практика

При написании исходного файла включите в него соответствующий заголовочный файл (если он существует), даже если он вам пока не нужен.

Поиск и устранение проблем

Если вы получаете ошибку компилятора, указывающую, что add.h не найден, убедитесь, что файл действительно называется add.h . В зависимости от того, как вы его создали и назвали, возможно, файл может иметь имя вроде add (без расширения), add.h.txt или add.hpp . Также убедитесь, что он находится в том же каталоге, что и остальные исходные файлы.

Угловые скобки и двойные кавычки

Вам, наверное, интересно, почему мы используем угловые скобки для iostream и двойные кавычки для add.h . Возможно, что заголовочные файлы с таким же именем могут существовать в нескольких каталогах. Использование угловых скобок и двойных кавычек помогает компилятору понять, где ему следует искать заголовочные файлы.

Когда мы используем угловые скобки, мы сообщаем препроцессору, что это заголовочный файл, который мы не писали сами. Компилятор будет искать заголовок только в каталогах, указанных в каталогах включаемых файлов (include directories). Каталоги включаемых файлов настраиваются как часть вашего проекта / настроек IDE / настроек компилятора и обычно по умолчанию используются для каталогов, содержащих заголовочные файлы, которые поставляются с вашим компилятором и/или ОС. Компилятор не будет искать заголовочный файл в каталоге исходного кода вашего проекта.

Когда мы используем двойные кавычки, мы сообщаем препроцессору, что это заголовочный файл, который написали мы. Компилятор сначала будет искать этот заголовочный файл в текущем каталоге. Если он не сможет найти там подходящий заголовочный файл, он будет искать его в каталогах включаемых файлов.

Правило

Используйте двойные кавычки, чтобы включать заголовочные файлы, которые написали вы или которые, как ожидается, будут найдены в текущем каталоге. Угловые скобки используйте, чтобы включать заголовочные файлы, которые поставляются с вашим компилятором, ОС или сторонними библиотеками, которые вы установили в другом месте своей системы.

Почему у iostream нет расширения .h ?

Другой часто задаваемый вопрос: «Почему iostream (или любой другой заголовочный файл стандартной библиотеки) не имеет расширения .h ?». Ответ заключается в том, что iostream.h – это другой заголовочный файл, отличающийся от iostream ! Для объяснения требуется небольшой урок истории.

Когда C++ был только создан, все файлы в стандартной библиотеке оканчивались расширением .h . Жизнь была последовательной, и это было хорошо. Исходные версии cout и cin были объявлены в iostream.h . Когда комитет ANSI стандартизировал язык, они решили переместить все функции стандартной библиотеки в пространство имен std , чтобы избежать конфликтов имен с пользовательскими идентификаторами. Однако это представляло проблему: если бы они переместили всю функциональность в пространство имен std , ни одна из старых программ (включая iostream.h ) больше не работала бы!

Кроме того, многие библиотеки, унаследованные от C, которые всё еще используются в C++, получили префикс c (например, stdlib.h стал cstdlib ). Функциональные возможности этих библиотек также были перенесены в пространство имен std , чтобы избежать конфликтов имен.

Лучшая практика

При включении заголовочного файла из стандартной библиотеки используйте версию без расширения (без .h ), если она существует. Пользовательские заголовочные файлы по-прежнему должны использовать расширение .h .

Включение заголовочных файлов из других каталогов

Другой распространенный вопрос связан с тем, как включать заголовочные файлы из других каталогов.

Хотя это будет компилироваться (при условии, что файлы существуют в этих относительных каталогах), обратная сторона этого подхода состоит в том, что он требует от вас отражения структуры каталогов в вашем коде. Если вы когда-нибудь обновите структуру каталогов, ваш код больше не будет работать.

Лучший способ – сообщить вашему компилятору или IDE, что у вас есть куча заголовочных файлов в каком-то другом месте, чтобы он смотрел туда, когда не может найти их в текущем каталоге. Обычно это можно сделать, установив путь включения (include path) или каталог поиска (search directory) в настройках проекта в IDE.

Для пользователей Visual Studio

Кликните правой кнопкой мыши на своем проекте в обозревателе решений и выберите Свойства (Properties), затем вкладку Каталоги VC++.(VC++ Directories). Здесь вы увидите строку с названием «Включаемые каталоги» (Include Directories). Добавьте каталоги, в которых компилятор должен искать дополнительные заголовочные файлы.

Для пользователей Code::Blocks

В Code:: Blocks перейдите в меню Project (Проект) и выберите Build Options (Параметры сборки), затем вкладку Search directories (Каталоги поиска). Добавьте каталоги, в которых компилятор должен искать дополнительные заголовочные файлы.

Для пользователей GCC/G++

Используя g++, вы можете использовать параметр -I , чтобы указать альтернативный каталог для включения.

Хороший момент в этом подходе заключается в том, что если вы когда-нибудь измените структуру каталогов, вам нужно будет изменить только одну настройку компилятора или IDE, а не каждый файл кода.

Заголовочные файлы могут включать другие заголовочные файлы

Содержимое этих транзитивных включений доступно для использования в вашем файле исходного кода. Однако не следует полагаться на содержимое заголовков, которые включены транзитивно. Реализация заголовочных файлов может со временем меняться или отличаться в разных системах. Если это произойдет, ваш код может компилироваться только на определенных системах или может компилироваться сейчас, но перестать в будущем. Этого легко избежать, явно включив все заголовочные файлы, необходимые для содержимого вашего файла исходного кода.

Лучшая практика

К сожалению, нет простого способа определить, полагается ли ваш файл кода случайно на содержимое заголовочного файла, который был включен другим заголовочным файлом.

Вопрос: Я не включил <someheader.h> , и моя программа всё равно работала! Почему?

Это один из наиболее часто задаваемых вопросов. Ответ: скорее всего, он работает, потому что вы включили какой-то другой заголовок (например, <iostream> ), который сам включает <someheader.h> . Несмотря на то, что ваша программа будет компилироваться, в соответствии с приведенными выше рекомендациями вам не следует полагаться на это. То, что компилируется у вас, может не компилироваться на машине друга.

Лучшая практика

Рекомендации по использованию заголовочных файлов

Вот еще несколько рекомендаций по созданию и использованию заголовочных файлов.

Файл с расширением .SO является файлом общей библиотеки. Они содержат информацию, которая может использоваться одной или несколькими программами для разгрузки ресурсов, так что приложения, вызывающие файл SO, не должны фактически предоставлять файл SO

Например, один файл SO может содержать информацию и функции о том, как быстро выполнить поиск по всему компьютеру. Несколько программ могут затем вызывать этот SO-файл для использования этой функции в своих соответствующих программах.

Однако вместо того, чтобы компилировать его в собственном двоичном коде программы, SO-файл служит расширением, которое программа просто должна вызвать, чтобы использовать ее утилиты. Файл SO даже может быть обновлен/заменен позже без необходимости внесения каких-либо изменений в собственный код.

Файлы общей библиотеки аналогичны файлам динамической библиотеки (DLL), используемым в Windows, и файлам динамической библиотеки Mach-O (DYLIB) в macOS, за исключением того, что файлы SO находятся в системах на базе Linux и в ОС Android.

SO не просто ссылается на файл общей библиотеки. Это также аббревиатура для параметров сервера , объект службы , перегрузка системы , только отправка , сбой системы , последовательный вывод и застрял в открытом состоянии . Однако не путайте это с ОС, аббревиатурой от операционной системы.

Как открыть файл SO

SO файлы могут быть технически открыты с помощью GNU Compiler Collection, но эти типы файлов не предназначены для просмотра или использования так же, как файлы другого типа. Вместо этого они просто помещаются в соответствующую папку и автоматически используются другими программами через загрузчик динамических ссылок Linux.

Однако вы можете прочитать SO-файл как текстовый файл, открыв его в текстовом редакторе, таком как Leafpad, gedit, KWrite или Geany, если вы работаете в Linux, или Notepad ++ в Windows. Маловероятно, однако, что текст будет в удобочитаемом формате.

Как конвертировать SO файлы

Нам неизвестно о каких-либо программах, которые могут конвертировать SO в DLL для использования в Windows, и, учитывая, что эти файлы делают, они вряд ли есть. Это также не простая задача конвертировать SO в другие форматы файлов, такие как JAR или A (файл Stat Library).

Возможно, вы сможете «конвертировать» SO-файлы в JAR-файлы, просто заархивировав их в формат архивного файла, например .ZIP, а затем переименовав его в .JAR.

Дополнительная информация о файлах SO

Вот лишь несколько примеров: libdaemon.SO.14 , libchromeXvMC.SO.0 , libecal-1.2.SO.100 , libgdata.SO.2 и libgnome-bluetooth.SO.4.0.1 .

Число в конце позволяет иметь несколько версий одного и того же файла, не вызывая проблем с перекрывающимися именами. Эти файлы обычно хранятся в /lib/ или /usr/lib/.

На устройстве Android SO файлы хранятся в APK в /lib //. Здесь «ABI» может быть папкой с именем armeabi , armeabi-v7a , arm64-v8a , mips , mips64 , x86 или x86_64 . Файлы SO в правильной папке, которая относится к устройству, используются для установки приложений через файл APK.

Файлы общей библиотеки иногда называют динамически связанными библиотеками общих объектов , общими объектами , общими библиотеками и библиотеками общих объектов .

Все еще не можете открыть файл?

Очевидная причина, по которой вы, возможно, не сможете открыть SO-файл, заключается в том, что это не совсем SO-файл. Вы можете просто иметь файл, который имеет несколько общих букв в качестве этого расширения файла. Подобные расширения звучащих файлов не обязательно означают, что форматы файлов похожи или что они могут работать с одними и теми же программами.

Например, формат файла ISO является популярным форматом, который во многом похож на «.SO» в конце файла, но оба они не связаны и не могут открываться с помощью одних и тех же программ.

Другой пример можно увидеть с файлами SOL, которые являются файлами локальных общих объектов Flash. Они используются с Adobe Flash и не связаны с SO-файлами.

Читайте также: