Предисловие
В Интернете есть много примеров использования arduion и Raspberry Pi для подключения к yeelink. Методы реализации аппаратного и программного обеспечения очень просты. Изучая эти примеры, я мотивировал меня изучать встроенные сети. Хотя использование arduion для подключения yeelink является простым, удобным, стабильным и надежным, оно все равно, что использовать встроенный стек протоколов Ethernet для подключения к yeelink.Например, MCU использует STM32, чип сетевой карты использует ENC28J60, а стек протоколов Ethernet использует LwIP. Хотя аппаратное и программное обеспечение намного сложнее, здесь также много «веселья». Все должно быть постепенно. Я решил тщательно изучить программирование сокетов и использовать платформу ПК для обмена данными с Yeelink.
SOCK_STREAM vs SOCK_DGRAM¶
См.также
- UDP
- TCP
Потоковый (SOCK_STREAM) | Дейтаграммный (SOCK_DGRAM) |
---|---|
Устанавливает соединение | Нет |
Гарантирует доставку данных | Нет в случае UDP |
Гарантирует порядок доставки пакетов | Нет в случае UDP |
Гарантирует целостность пакетов | Тоже |
Разбивает сообщение на пакеты | Нет |
Контролирует поток данных | Нет |
TCP гарантирует доставку пакетов, их очередность, автоматически разбивает
данные на пакеты и контролирует их передачу, в отличии от UDP.
Но при этом TCP работает медленнее за счет повторной передачи потерянных
пакетов и большему количеству выполняемых операций над пакетами. Поэтому
там где требуется гарантированная доставка (Веб-браузер, telnet, почтовый клиент) используется TCP, если же требуется передавать данные в реальном
времени (многопользовательские игры, видео, звук) используют UDP.
Добавление функциональности к вашей консоли I2P
I2P поставляется с базовым набором инструментов, который большинство темных веб-пользователей ищут прямо в комплекте. На главной странице маршрутизатора I2P есть инструменты для IRC, передачи файлов, электронной почты и веб-серверов, а также многих других. Эта функциональность может быть расширена с помощью инфраструктуры плагинов I2P, хотя очень мало плагинов доступно.
Чтобы управлять плагинами, перейдите на страницу configplugins http://127.0.0.1:7657/configplugins, и вы увидите варианты установки плагинов двумя способами. Если у вас есть плагин в файлах xpi2p и su3, вы можете загрузить его на свой роутер. Другой способ — предоставить URL-адрес одному из этих типов файлов..
Хранилище подключаемых модулей I2P находится на веб-сайте I2P (веб-сайт). Весьма вероятно, что злоумышленник или наблюдатель получат много пользы от обмана пользователя I2P при установке плагина наблюдения, поэтому не забудьте получить плагины из этого официального репозитория, а не из ненадежных сторонних источников..
Использование I2P для отправки и получения электронной почты
В настоящее время существует два распространенных способа отправки и получения электронной почты между I2P и обычными адресами электронной почты в Интернете. SusiMail поставляется в комплекте с вашим маршрутизатором I2P, а также имеется плагин под названием I2P Bote. Оба могут быть использованы для отправки электронной почты в I2P, но в настоящее время только SusiMail можно использовать для отправки электронной почты на обычные интернет-адреса и с них..
Чтобы начать работу с SusiMail, щелкните значок «Электронная почта» на странице вашего маршрутизатора I2P..
SusiMail предложит вам создать учетную запись на сайте Почтальона I2P, прежде чем вы сможете ее использовать. Оттуда вы узнаете довольно стандартный интерфейс веб-почты и сможете отправлять и получать электронную почту как на адреса I2P, так и на обычные адреса электронной почты в Интернете..
Примечание. Письма I2P могут буквально часами приходить на обычные интернет-адреса. Это на самом деле является преимуществом, поскольку затрудняет наблюдения за временной корреляцией Если известный пользователь I2P отправляет электронное письмо, а затем наблюдается выход из ее дома, и через несколько секунд приходит электронное письмо с адреса электронной почты I2P, что может поставить под угрозу ее анонимность. Наличие электронных писем гораздо позже устраняет эту возможность корреляции.
В отличие от SusiMail, I2P Bote рассматривает создание учетной записи как вариант и не требует учетной записи для отправки анонимной электронной почты. Если вы хотите получать электронную почту, вам необходимо пройти процесс создания учетной записи..
Проект I2P Bote работает над внедрением Интернета <-> Почтовый шлюз I2P, но в настоящее время он не работает.
Настройка I2P Bote требует немного больше работы. Шаги установки можно найти на странице установки I2PBote (eepsite).
- Перейдите к форме установки плагина в консоли маршрутизатора: http://127.0.0.1:7657/configclients#plugin
- Вставьте в URL http: //bote.i2p/i2pbote.su3
- Нажмите Установить плагин.
- После установки щелкните SecureMail на боковой панели или домашней странице routerconsole или перейдите по адресу http://127.0.0.1:7657/i2pbote/.
В настоящей темной моде нет необходимости создавать какие-либо учетные записи или идентификаторы для отправки электронной почты с помощью Bote. Вы можете нажать значок + в правом нижнем углу экрана, ввести электронное письмо и отправить его с полной анонимностью. Однако вы не сможете получать электронную почту, поскольку вы полностью анонимны. Если вы хотите получать электронную почту, вам нужно будет создать личность.
Небольшое упражнение
Теперь для того, чтобы сделать что-нибудь более полезное, чем то, что мы делали ранее, поместите в соответствующую часть программы-сервера следующий код:
if(read(cfd, &ch, 1)<0) perror("read"); while( ch != EOF) { if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')) ch^=0x20; /* EXORing 6th bit will result in change in case */ if(write(cfd, &ch, 1)<0) perror("write"); if(read(cfd, &ch, 1)<0) perror("read"); }
Да, вы должны сделать это правильно: добавить код после вызова функции accept(). Теперь, если вы засыпаете точно также, как и я, просто обратитесь к серверу с помощью telnet; либо двигайтесь дальше и напишите собственную клиентскую программу.
Да, прежде, чем закрывать соединение, давайте взглянем на данные, выданные в сессии telnet. Запустите в вашей командной оболочке telnet ::1 1205 и начните набирать текст. На рисунке 2 приведен пример выдаваемых данных.
Рис.2: Используем telnet
При нажатии клавиш Ctrl+D сервер закроет соединение и будет ждать новое соединение. А для тех лентяев, кому неохота писать свою собственную клиентскую программу, нужно просто поместить в программу следующий код, а не только вызовы функций write() и read(); результат показан на рис.3:
while(1) { ch=getchar(); if(write(cfd, &ch, 1)<0) perror("write"); if(read(cfd, &ch, 1)<0) perror("read"); printf("%c", ch); }
Рис.3: Работа клиентской программы
Скомпилируйте и запустите вашу клиентскую программу.
Чтобы закрыть клиентскую программу, используйте сочетание клавиш Ctrl+D, которая пошлет символ EOF на сервер, а сервер закроет соединение с клиентом. Не нужно закрывать сервер с помощью нажатия клавиш Ctrl-C. Либо вы также можете закрывать соединение из клиентской программы; мы решим эту проблему позже с помощью обработчики сигналов. Теперь, я думаю, что нужно немного отдохнуть. Спокойной ночи и FOSS — это круто!
Что означает ошибка нахождения IP-адрес сервера
Обычно указанное сообщение появляется в ситуации, когда пользовательский браузер не может открыть страницу нужного веб-сайта. При вводе адреса веб-ресурса в адресной строке навигатора и нажатии на Enter вместо нужной страницы навигатор выводит сообщение о том, что невозможно определить адрес.
Can’t find IP
Причинами появления данного сообщения может быть следующее:
- Адрес сайта был введён неверно;
- Нужный сервер работает нестабильно, или на нём ведутся какие-либо работы;
- На PC пользователя имеются проблемы с настройками сетевого подключения (в частности, некорректно работает роутер);
- Антивирус или брандмауэр PC блокирует доступ к нужному веб-ресурсу;
- Некорректно работают ДНС-сервера или служба ДНС на пользовательском PC;
- На компьютере пользователя действует вирусный зловред, изменивший сетевое подключение под себя;
- На браузере установлены сетевые плагины (расширения), влияющие на его работу;
- Имеются какие-либо неполадки у интернет-провайдера.
При идентификации причин очень важно удостовериться, имеется ли данная проблема на других ПК и у других пользователей. Попросите кого-либо из ваших друзей или знакомых перейти на проблемную интернет-страницу
Если ошибка с IP-адресом возникает и у них, тогда, скорее всего, это проблема на стороне сервера (сайта), и вашей вины в этом нет. Если же у них нужная страница открывается без проблем, тогда это проблема именно пользовательского PC.
Давайте разберёмся, как устранить ошибку «Не удалось найти IP-адрес сервера» на вашем PC.
Формат адреса
udptcpsin_port
struct sockaddr_in { sa_family_t sin_family; /* семейство адресов: AF_INET */ in_port_t sin_port; /* порт сокета в сетевом порядке байт */ struct in_addr sin_addr; /* Интернет-адрес */ }; /* Интернет-адрес */ struct in_addr { uint32_t s_addr; /* адрес в сетевом порядке байт */ };
Значение sin_family всегда устанавливается в AF_INET. Это обязательно;
в Linux 2.2 большая часть сетевых функций возвращает код ошибки EINVAL,
если это условие не выполняется. В sin_port указывается номер порта в
сетевом порядке байт. Порты, номера которых меньше 1024, называются
привилегированными портами (или, иногда, зарезервированными
портами). Только привилегированные процессы (т.е., имеющие мандат
CAP_NET_BIND_SERVICE) могут быть связаны с этими сокетами с помощью
bind(2). Заметим, что у неструктурированного протокола IPv4, нет понятия
портов как таковых, они реализуются только протоколами более высокого
уровня, типа tcp(7) и udp(7).
В sin_addr указывается IP-адрес узла. В поле s_addr структуры struct
in_addr содержится адрес интерфейса узла в сетевом порядке байт. Значение
in_addr должно быть одним из INADDR_* (например, INADDR_ANY) или
устанавливаться с помощью библиотечных функций inet_aton(3),
inet_addr(3), inet_makeaddr(3) или напрямую с помощью преобразователя
имён (смотрите gethostbyname(3)).
Адреса IPv4 делятся на однозначные (unicast), широковещательные (broadcast)
и многоадресные (multicast). Однозначный адрес указывает на один интерфейс
узла, широковещательный адрес указывает на все узлы в сети, а многоадресный
указывает на все узлы многоадресной (multicast group). Дейтаграммы могут
посылаться или приниматься по широковещательным адресам только, если для
сокета установлен флаг SO_BROADCAST. В текущей реализации сокетам,
ориентированным на соединения, разрешено иметь только однозначные адреса.
Заметим, что значение адреса и порта всегда хранится в сетевом порядке
байт. В частности, это означает, что требуется вызывать htons(3) для
числа, обозначающего порт. Все функции из стандартной библиотеки,
используемые для работы с адресами/портами, используют сетевой порядок байт.
listen()¶
См.также
- http://unixhelp.ed.ac.uk/CGI/man-cgi?listen+2
Подготавливает привязываемый сокет к принятию входящих соединений. Данная функция применима только к типам сокетов SOCK_STREAM и SOCK_SEQPACKET. Принимает два аргумента:
- sockfd — корректный дескриптор сокета.
- backlog — целое число, означающее число установленных соединений, которые могут быть обработаны в любой момент времени. Операционная система обычно ставит его равным максимальному значению.
Примечание
После принятия соединения оно выводится из очереди. В случае успеха возвращается 0, в случае возникновения ошибки возвращается −1.
Пример на Си
#include <sys/socket.h> int listen(int sockfd, int backlog);
Пример на Python
Использование I2P для доступа к веб-сайтам
Если вы не используете метод FoxyProxy, вы заметите, что после активации настроек прокси вы потеряете соединение с чистой сетью. Это связано с тем, что I2P по умолчанию не предоставляет прокси-серверы. На языке I2P outproxy — это узел I2P, который подключается к чистой сети и пропускает внутренний трафик I2P в обычный Интернет. На языке Tor это называется выходным узлом. I2P является гораздо более замкнутой сетью и обычно не используется в качестве прокси для чистой сети. В I2P FAQ есть что сказать по поводу прокси:
Как получить доступ к IRC, BitTorrent или другим службам в обычном Интернете?
Если для службы, к которой вы хотите подключиться, не настроен прокси-сервер, это невозможно. На данный момент работает только три типа прокси: HTTP, HTTPS и электронная почта
Обратите внимание, что в настоящее время нет общедоступных прокси-серверов SOCKS. Если этот тип обслуживания требуется, попробуйте Tor
В настоящее время в репозитории подключаемых модулей IP2 перечислены три подключаемых модуля прокси-сервера (eepsite). Два больше не существуют, а оставшийся один указывает на форум разработчиков I2P, содержащий инструкции о том, как взломать тестовую установку с 2016 года. Мне не удалось заставить это работать, так что, похоже, не существует общедоступного способа использования I2P в качестве анонимного интернет-прокси в это время.
При просмотре темных веб-сайтов I2P иногда вы не можете загрузить сайт, и вместо этого вам предоставляется страница, подобная этой.
Это означает, что ваш I2P-маршрутизатор не знает, где находится этот сайт, но это не обязательно означает, что сайт не существует; другие маршрутизаторы могут знать, где это. Чтобы узнать, нажмите одну или все ссылки перехода, представленные. Эти ссылки будут использовать хорошо информированные маршрутизаторы I2P, чтобы узнать, можно ли найти сайт. Во многих случаях это работает.
accept()¶
См.также
- http://unixhelp.ed.ac.uk/CGI/man-cgi?accept+2
Используется для принятия запроса на установление соединения от удаленного хоста. Принимает следующие аргументы:
- sockfd — дескриптор слушающего сокета на принятие соединения.
- cliaddr — указатель на структуру sockaddr, для принятия информации об адресе клиента.
- addrlen — указатель на socklen_t, определяющее размер структуры, содержащей клиентский адрес и переданной в accept(). Когда accept() возвращает некоторое значение, socklen_t указывает сколько байт структуры cliaddr использовано в данный момент.
Примечание
Функция возвращает дескриптор сокета, связанный с принятым соединением, или −1 в случае возникновения ошибки.
Пример на Си
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
Пример на Python
Принципы сокетов¶
Каждый процесс может создать слушающий сокет (серверный сокет) и привязать его
к какому-нибудь порту операционной системы (в UNIX непривилегированные
процессы не могут использовать порты меньше 1024). Слушающий процесс обычно
находится в цикле ожидания, то есть просыпается при появлении нового
соединения. При этом сохраняется возможность проверить наличие соединений на
данный момент, установить тайм-аут для операции и т.д.
Каждый сокет имеет свой адрес. ОС семейства UNIX могут поддерживать много
типов адресов, но обязательными являются INET-адрес и UNIX-адрес. Если
привязать сокет к UNIX-адресу, то будет создан специальный файл (файл сокета)
по заданному пути, через который смогут сообщаться любые локальные процессы
путём чтения/записи из него (см. Доменный сокет Unix). Сокеты типа INET
доступны из сети и требуют выделения номера порта.
Версия сервера для IPv6
Ниже показана версия сервера, созданного нами в предыдущей статье, для протокола IPv6. Существенных изменений нет, за исключением появления в коде цифры «6». Давайте назовем этот файл serverin6.c:
#include <stdio.h%gt; #include <unistd.h%gt; #include <sys/types.h%gt; #include <sys/socket.h%gt; #include <netinet/in.h%gt; int main() { int sfd, cfd; char ch; socklen_t len; struct sockaddr_in6 saddr, caddr; sfd= socket(AF_INET6, SOCK_STREAM, 0); saddr.sin6_family=AF_INET6; saddr.sin6_addr=in6addr_any; saddr.sin6_port=htons(1205); bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr)); listen(sfd, 5); while(1) { printf("Waiting...n"); len=sizeof(cfd); cfd=accept(sfd, (struct sockaddr *)&caddr, &len); if(read(cfd, &ch, 1)<0) perror("read"); ch++; if(write(cfd, &ch, 1)<0) perror("write"); close(cfd); } }
Теперь давайте рассмотрим различия. Первое находится в строке 6 (sockaddr_in6) и оно понятно; для адресов IPv6 нам нужно в этой адресной структуре хранить адрес, порт и тип адресов так, как мы это делали в строках 13, 14 и 15. В строке 14 находится универсальный символ in6addr_any, используемый для всех адресов IPv6, такой же, как и INADDR_ANY для IPv4. Есть также изменения и в accept(), с которыми можно легко разобраться. И вот наш сервер работает с версией протокола IPv6. Вы можете с правами пользователя root настроить протокол IPv6 с помощью следующей команды:
ip -f inet6 addr add face:1f::ea54:a dev eth0
Здесь -f указывает семейство протоколов (inet6), а addr предназначен для хранения адреса — мы добавили face:1f::ea54:a (когда мы записываем адреса IPv6, лидирующие нули можно опустить, указанный выше адрес, в действительности, является адресом face:001f::ea54:000a). Вы можете задать любой адрес, и чтобы он ни с чем не совпадал, вы можете использовать свой MAC-адрес. В параметре dev указывается устройство, для которого мы устанавливаем адрес, в данном случае — eth0. Вы можете проверить результаты с помощью команды ifconfig.
Скомпилируйте и запустите сервер следующим образом:
cc serverin6.c -o serverin6 ./serverin6
Прежде, чем писать клиентскую программу, вы можете увидеть, что сервер работает даже с нашим клиентом для версии IPv4, а адрес IPv4, также указывает на ту же самую машину. Мы можем для того, чтобы проверить, работает ли сервер так, как ожидалось, подключаться к каждому из серверов с помощью telnet, например:
telnet localhost 1205
Введите символ, который вы хотите отправить на сервер, и нажмите клавишу Enter, и сервер ответит следующим символом ASCII, а затем закроет соединение. Помните, что для версии IPv4 localhost будет равен 127.0.0.1, а для IPv6 — будет ::1.
connect()¶
См.также
- http://unixhelp.ed.ac.uk/CGI/man-cgi?connect+2
Устанавливает соединение с сервером.
Некоторые типы сокетов работают без установления соединения, это в основном касается UDP-сокетов. Для них соединение приобретает особое значение: цель по умолчанию для посылки и получения данных присваивается переданному адресу, позволяя использовать такие функции как send() и recv() на сокетах без установления соединения.
Загруженный сервер может отвергнуть попытку соединения, поэтому в некоторых видах программ необходимо предусмотреть повторные попытки соединения.
Примечание
Возвращает целое число, представляющее код ошибки: 0 означает успешное выполнение, а −1 свидетельствует об ошибке.
Пример на Си
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
Пример на Python
Обработка разрывов соединения
Если соединение разрывается, оно автоматически восстанавливается браузером. Сервер может отправить таймаут для повторного завершения или закрытия соединения. В таком случае браузер попытается подключиться после завершения таймаута или не будет ничего делать, если соединение завершено.
Реализация образца сервера
Если клиент такой простой, возможно, сложной окажется реализация сервера? Обработчик сервера для SSE может выглядеть следующим образом:
function handler(response) { // настраиваем заголовки для ответа с целью получить постоянное HTTP-соединение response.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // составляем сообщение response.write('id: UniqueIDn'); response.write("data: " + data + 'nn'); // каждый раз, когда мы вводим два символа новой строки, сообщение отправляется автоматически }
Определяем функцию, которая будет обрабатывать ответ:
- Устанавливать заголовки;
- Создавать сообщение;
- Отправлять.
Обратите внимание, что здесь нет вызов метода send() или метода push(). Стандарт определяет: сообщение будет отправлено, как только в него будет добавлено два символа n n, как например: response.write(«data: » + data + ‘nn’);
В результате сообщение будет немедленно отправлено клиенту.
Составление сообщений
Сообщение может содержать несколько свойств:
1. ID
Если значение этого поля не содержит U + 0000 NULL, устанавливаем для буфера последнего идентификатора события значение поля. Иначе игнорируем поле.
2. Data
Добавляем значение поля в буфер, затем добавляем в буфер один символ U + 000A LINE FEED (LF).
3. Event
Устанавливаем для буфера тип события и значение поля. Это приводит к тому, что для event.type задается пользовательское имя события.
4. Retry
Если значение поля состоит только из цифр ASCII, тогда интерпретируем значение поля как целое число в десятичной системе исчисления. А также устанавливаем для времени повторного соединения потока событий это целое число. В противном случае игнорируем поле.
Все остальное будет проигнорировано. Мы не можем вводить собственные поля.
Пример с добавленным event:
response.write('id: UniqueIDn'); response.write('event: addn'); response.write('retry: 10000n'); response.write("data: " + data + 'nn');
В клиенте это обрабатывается с помощью addEventListener следующим образом:
source.addEventListener("add", function(event) { // выполняем действия с данными event.data; });
Вы можете отправлять несколько сообщений, разделенных символом новой строки, а также использовав для них разные идентификаторы.
... id: 54 event: add data: "" id: 55 event: remove data: JSON.stringify(some_data) id: 56 event: remove data: { data: "msg" : "JSON data"n data: "field": "value"n data: "field2": "value2"n data: }nn
Это значительно упрощает то, что мы можем сделать с нашими данными.
Взаимное преобразование между форматами
IP-адрес в формате ASCII можно сопоставить с IP-адресом в целочисленном формате. Inet_addr и inet_ntoa могут использоваться на платформе Windows.
3.1 inet_addr
Прототип функции и входные и выходные параметры
Функция роль
Преобразуйте IP-адрес формата ASCII (формат AAA.BBB.CCC.DDD) в 32-разрядное целое число без знака.
3.2inet_ntoa
Прототип функции и входные и выходные параметры
Функция роль
Преобразуйте IP-адрес типа in_addr в IP-адрес в формате ASCII (форма AAA.BBB.CCC.DDD). inet_ntoa
nМожно понимать как сеть,
aМожно понимать как ASCII. Эта функция обычно используется для печати IP-адреса.