Сокет это порт пк находящегося в сети
Тема сетевого программирования является для разработчиков одной из важнейших в современном цифровом мире. Правда, надо признать, что большая часть сетевого программирования сосредоточена в области написания скриптов исполнения для web-серверов на языках PHP, Python и им подобных. Как следствие - по тематике взаимодействия клиент-сервер при работе с web-серверами написаны терабайты текстов в Интернете. Однако когда я решил посмотреть, что же имеется в Интернете по вопросу программирования сетевых приложений с использованием голых сокетов, то обнаружил интересную вещь: да, такие примеры конечно же есть, но подавляющее большинство написано под *nix-системы с использованием стандартных библиотек (что понятно – в области сетевого программирования Microsoft играет роль сильно отстающего и менее надежного «собрата» *nix-ов). Другими словами все эти примеры просто не будут работать под Windows. При определенных танцах с бубнами код сетевого приложения под Linux можно запустить и под Windows, однако это еще более запутает начинающего программиста, на которого и нацелены большинство статей в Интернете с примерами использования сокетов.
вообще не заработает, т.к. полю Service.sin_addr.s_addr невозможно присвоить значение целого типа, которое возвращает функция inet_addr (возвращает unsigned long). То есть это ни много, ни мало - ошибка! Можно себе представить, сколько пытливых бойцов полегло на этом месте кода.
Сразу оговорюсь, что статья рассчитана на начинающих программистов, которые только входят в сетевое программирование под Windows. Необходимые навыки – базовое знание С++, а также теоретическая подготовка по теме сетевых сокетов и стека технологии TCP/IP.
Этап 2: Создание сокета и его инициализация
Сокет в С++ – это структура данных (не класс) типа SOCKET. Её инициализация проводится через вызов функции socket() , которая привязывает созданный сокет к заданной параметрами транспортной инфраструктуре сети. Выглядит прототип данной функции следующим образом:
Семейство адресов: сокеты могут работать с большим семейством адресов. Наиболее частое семейство – IPv4. Указывается как AF_INET .
Тип сокета: обычно задается тип транспортного протокола TCP ( SOCK_STREAM ) или UDP ( SOCK_DGRAM ). Но бывают и так называемые "сырые" сокеты, функционал которых сам программист определяет в процессе использования. Тип обозначается SOCK_RAW
Тип протокола: необязательный параметр, если тип сокета указан как TCP или UDP – можно передать значение 0. Тут более детально останавливаться не будем, т.к. в 95% случаев используются типы сокетов TCP/UDP.
При необходимости подробно почитать про функцию socket() можно здесь.
Код Этапа 2 будет выглядеть так:
Установление связи
error = connect(s, serveraddr, serveraddrlen);
которая инициирует установление связи на сокете, используя дескриптор сокета s и информацию из структуры serveraddr, имеющей тип sockaddr_in, которая содержит адрес сервера и номер порта на который надо установить связь. Если сокет не был связан с адресом, connect автоматически вызовет системную функцию bind.
Connect возвращает 0, если вызов прошел успешно. Возвращенная величина -1 указывает на то, что в процессе установления связи произошла некая ошибка. В случае успешного вызова функции процесс может работать с дескриптором сокета, используя функции read и write, и закрывать канал используя функцию close.
Со стороны сервера процесс установления связи сложнее. Когда сервер желает предложить один из своих сервисов, он связывает сокет с общеизвестным адресом, ассоциирующимся с данным сервисом, и пассивно слушает этот сокет. Для этих целей используется системный вызов listen:
error = listen(s, qlength);
где s это дескриптор сокета, а qlength это максимальное количество запросов на установление связи, которые могут стоять в очереди, ожидая обработки сервером; это количество может быть ограничено особенностями системы.
Когда сервер получает запрос от клиента и принимает решение об установлении связи, он создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'. Для Internet домена это означает тот же самый номер порта. Для этой цели используется системный вызов accept:
newsock = accept(s, clientaddr, clientaddrlen);
Сокет, ассоциированный клиентом, и сокет, который был возвращен функцией accept, используются для установления связи между сервером и клиентом.
Процесс установления связи показан на рисунке 1.
Рис. 1: Взаимодействие клиента и сервера
Этап 4 (для Клиента). Организация подключения к серверу
Код для Клиента до текущего этапа выглядит даже проще: необходимо исполнение Этапов 0, 1 и 2. Привязка сокета к конкретному процессу ( bind() ) не требуется, т.к. сокет будет привязан к серверному Адресу и Порту через вызов функции connect() (по сути аналог bind() для Клиента). Собственно, после создания и инициализации сокета на клиентской стороне, нужно вызвать указанную функцию connect() . Её прототип:
Функция возвращает 0 в случае успешного подключения и код ошибки в ином случае.
Процедура по добавлению данных в структуру sockaddr аналогична тому, как это делалось на Этапе 3 для Сервера при вызове функции bind() . Принципиально важный момент – в эту структуру для клиента должна заноситься информация о сервере, т.е. IPv4-адрес сервера и номер «слушающего» порта на сервере.
Разница между сокетом и портом
Основные условия
Интерфейс, IP-адрес, сокет, порт, номер порта
использование
В то время как сокет работает как интерфейс для отправки и получения данных через определенный порт, порт помогает идентифицировать конкретное приложение или процесс.
Привязка к локальным именам
В Internet домене связывание сокета и имени может быть весьма сложным, но, к счастью, обычно нет необходимости специально привязывать адрес и номер порта к сокету, так как функции connect и send автоматически свяжут данный сокет с подходящим адресом, если это не было сделано до их вызова.
Для связывания сокета с адресом и номером порта используют системный вызов bind:
bind(s, name, namelen);
Привязываемое имя (name) это строка байт переменной длины, которая интерпретируется поддерживаемым протоколом. Интерпретация может различаться в различных коммуникационных доменах.
Закрывание сокетов
Когда взаимодействующие модули решают прекратить передачу данных и закрыть сеанс связи, они обмениваются трехсторонним рукопожатием с сегментами, содержащими установленный бит "От отправителя больше нет данных" (этот бит еще называется FIN бит).
Если сокет больше не используется, процесс может закрыть его с помощью функции close, вызвав ее с соответствующим дескриптором сокета:
Если данные были ассоциированы с сокетом, обещающим доставку (сокет типа stream), система будет пытаться осуществить передачу этих данных. Тем не менее, по истечении довольно таки длительного промежутка времени, если данные все еще не доставлены, они будут отброшены. Если пользовательский процесс желает прекратить любую передачу данных, он может сделать это с помощью вызова shutdown на данном сокете для его закрытия. Вызов shutdown вызывает "моментальное" отбрасывание всех стоящих в очереди данных. Формат вызова следующий:
главное отличие между сокетом и портом в том, что сокет - это интерфейс отправки и получения данных через определенный порт, а порт - числовое значение, назначенное конкретному процессу или приложению на устройстве.
Заключение
Сокет и порт - это два термина, используемые в компьютерных сетях. Разница между сокетом и портом заключается в том, что сокет является интерфейсом отправки и получения данных на конкретном порту, а порт - это числовое значение, назначенное конкретному процессу или приложению на устройстве.
Ссылка:
1. Концепция сокетов на примере реальной жизни, HowTo, 25 декабря 2014 г.,
Видео: Разница между сокетом и портом | Сравните разницу между похожими терминами
Передача данных
write(s, buf, sizeof(buf)); read(s, buf, sizeof(buf));
Вызовы send и recv практически идентичны read и write, за исключением того, что добавляется аргумент флагов.
send(s, buf, sizeof(buf), flags); recv(s, buf, sizeof(buf), flags);
- MSG_OOB - Посылать/получать данные, характерные для сокетов типа stream.
- MSG_PEEK - Просматривать данные без чтения. когда указывается в recv, любые присутствующие данные возвращаются пользователю, но сами данные остаются как "непрочитанные". Следующий read или recv вызванный на данном сокете вернет прочитанные в прошлый раз данные.
- MSG_DONTROUTE - посылать данные без маршрутизации пакетов. (Используется только процессами, управляющими таблицами маршрутизации.)
Теория сокетов за 30 секунд для "dummies"
Начну всё-таки немного с теории в стиле «for dummies». В любой современной операционной системе, все процессы инкапсулируются, т.е. скрываются друг от друга, и не имеют доступа к ресурсам друг друга. Однако существуют специальные разрешенные способы взаимодействия процессов между собой. Все эти способы взаимодействия процессов можно разделить на 3 группы: (1) сигнальные, (2) канальные и (3) разделяемая память.
Когда мы говорим про работу сетевого приложения, то всегда подразумеваем взаимодействие процессов: процесс 1 (клиент) пытается что-то послать или получить от Процесса 2 (сервер). Наиболее простым и понятным способом организации сетевого взаимодействия процессов является построение канала между этими процессами. Именно таким путём и пошли разработчики первых сетевых протоколов. Получившийся способ взаимодействия сетевых процессов в итоге оказался многоуровневым: основной программный уровень - стек сетевой технологии TCP/IP, который позволяет организовать эффективную доставку пакетов информации между различными машинами в сети, а уже на прикладном уровне тот самый «сокет» позволяет разобраться какой пакет какому процессу доставить на конкретной машине.
Иными словами «сокет» - это «розетка» конкретного процесса, в которую надо подключиться, чтобы этому процессу передать какую-либо информацию. Договорились, что эта «розетка» в Сети описывается двумя параметрами – IP-адресом (для нахождения машины в сети) и Портом подключения (для нахождения процесса-адресата на конкретной машине).
Для того, чтобы сокеты заработали под Windows, необходимо при написании программы пройти следующие Этапы:
Инициализация сокетных интерфейсов Win32API.
Инициализация сокета, т.е. создание специальной структуры данных и её инициализация вызовом функции.
«Привязка» созданного сокета к конкретной паре IP-адрес/Порт – с этого момента данный сокет (его имя) будет ассоциироваться с конкретным процессом, который «висит» по указанному адресу и порту.
Для серверной части приложения: запуск процедуры «прослушки» подключений на привязанный сокет.
Для клиентской части приложения: запуск процедуры подключения к серверному сокету (должны знать его IP-адрес/Порт).
Акцепт / Подтверждение подключения (обычно на стороне сервера).
Обмен данными между процессами через установленное сокетное соединение.
Закрытие сокетного соединения.
Итак, попытаемся реализовать последовательность Этапов, указанных выше, для организации простейшего чата между клиентом и сервером. Запускаем Visual Studio, выбираем создание консольного проекта на С++ и поехали.
Этап 5 (только для Сервера). Подтверждение подключения
После начала прослушивания (вызов функции listen() ) следующей функцией должна идти функция accept() , которую будет искать программа после того, как установится соединение с Клиентом. Прототип функции accept() :
Если подключение подтверждено, то вся информация по текущему соединению передаётся на новый сокет, который будет отвечать со стороны Сервера за конкретное соединение с конкретным Клиентом. Перед вызовом accept() нам надо создать пустую структуру типа sockaddr_in , куда запишутся данные подключившегося Клиента после вызова accept() . Пример кода:
Всё, соединение между Клиентом и Сервером установлено! Самое время попробовать передать информацию от Клиента к Серверу и обратно. Как мы в начале и договорились, мы будет реализовывать простейший чат между ними.
Этап 4 (для сервера): «Прослушивание» привязанного порта для идентификации подключений
Серверная часть готова к прослушке подключающихся «Клиентов». Для того, чтобы реализовать данный этап, нужно вызвать функцию listen() , прототип которой:
Второй аргумент: максимально возможное число подключений устанавливается через передачу параметр SOMAXCONN (рекомендуется). Если нужно установить ограничения на количество подключений – нужно указать SOMAXCONN_HINT(N) , где N – кол-во подключений. Если будет подключаться больше пользователей, то они будут сброшены.
После вызова данной функции исполнение программы приостанавливается до тех пор, пока не будет соединения с Клиентом, либо пока не будет возвращена ошибка прослушивания порта. Код Этапа 4 для Сервера:
Содержание:
Разъем против порта
В контексте компьютерных сетей сокет - это конечная точка двунаправленной связи, которая происходит в сети, основанной на интернет-протоколе. Сокеты будут распространять пакеты данных, поступающие по каналу связи, в нужное приложение. Это делается с использованием такой информации, как IP-адрес и номер порта. В общем случае (программный) порт - это логическое соединение для передачи данных, которое может использоваться для обмена данными. В Интернете порты TCP и UDP используются для обмена данными между компьютерами, и это наиболее широко используемые порты.
Что такое розетка?
Сокет - это конечная точка двунаправленной связи, которая происходит в компьютерной сети, основанной на интернет-протоколе. Сокеты будут распространять пакеты данных, поступающие по каналу связи, в нужное приложение. Операционная система сопоставляет каждый сокет с процессом или потоком, который обменивается данными. Есть два типа сокетов, которые называются активными и пассивными. Активный сокет - это сокет, который подключен к другому активному сокету через открытое соединение для передачи данных. Активные сокеты на обоих концах канала связи будут уничтожены при закрытии соединения. Пассивный сокет не участвует в соединении, но сокет, который ожидает входящего соединения. Когда подключен пассивный сокет, он генерирует новый активный сокет. Интернет-сокет идентифицируется адресом локального сокета (локальный IP-адрес и номер порта), адресом удаленного сокета и транспортным протоколом (например, TCP, UDP).
Что такое порт?
Порт - это логическое соединение для передачи данных, которое можно использовать для обмена данными без использования временного файла или хранилища. В Интернете порты TCP и UDP используются для обмена данными между компьютерами, и это наиболее широко используемые порты. Порт идентифицируется с помощью номера, связанного с портом, называемого номером порта, IP-адресом, связанным с портом, и транспортным протоколом. Набор номеров портов обычно зарезервирован на главном компьютере для определенных типов служб. Сканирование портов - это процесс попытки подключиться к набору портов, которые находятся в последовательности. Как правило, сканирование портов рассматривается как попытка злоумышленника. Системные администраторы проводят его для проверки наличия уязвимостей в системе.
В чем разница между сокетом и портом?
Сокет - это конечная точка двунаправленной связи, которая происходит в компьютерной сети, основанной на Интернет-протоколе, тогда как порт - это логическое соединение для передачи данных, которое можно использовать для обмена данными без использования временного файла или хранилища. Сокет связан с портом, и с ним может быть связано несколько сокетов. С портом, ожидающим входящих подключений, может быть один пассивный сокет. Кроме того, может быть несколько активных сокетов, которые соответствуют соединениям, открытым в этом порту.
Определение
Сокет - это внутренняя конечная точка для отправки и получения данных в узле компьютерной сети. Порт - это числовое значение, которое назначается приложению в конечной точке связи.
Этап 6: Передача данных между Клиентом и Сервером
Принимать информацию на любой стороне можно с помощью функции recv() , которая при своём вызове блокирует исполнение кода программы до того момента, пока она не получит информацию от другой стороны, либо пока не произойдет ошибка в передаче или соединении.
Отправлять информацию с любой стороны можно с помощью функции send() . При вызове данной функции обычно никакого ожидания и блокировки не происходит, а переданные в неё данные сразу же отправляются другой стороне.
Рассмотрим прототипы функций recv() и send() :
Флаги в большинстве случаев игнорируются – передается значение 0.
Функции возвращают количество переданных/полученных по факту байт.
Как видно из прототипов, по своей структуре и параметрам эти функции совершенно одинаковые. Что важно знать:
и та, и другая функции не гарантируют целостности отправленной/полученной информации. Это значит, что при реализации прикладных задач по взаимодействию Клиента и Сервера с их использованием требуется принимать дополнительные меры для контроля того, что все посланные байты действительно посланы и, что еще более важно, получены в том же объеме на другой стороне
предельно внимательно надо относиться к параметру "размер буфера". Он должен в точности равняться реальному количеству передаваемых байт. Если он будет отличаться, то есть риск потери части информации или «замусориванию» отправляемой порции данных, что ведет к автоматической поломке данных в процессе отправки/приёма. И совсем замечательно будет, если размер буфера по итогу работы функции равен возвращаемому значению функции – размеру принятых/отправленных байт.
В качестве буфера рекомендую использовать не классические массивы в С-стиле, а стандартный класс С++ типа char, т.к. он показал себя как более надежный и гибкий механизм при передаче данных, в особенности при передаче текстовых строк, где важен терминальный символ и «чистота» передаваемого массива.
Сама по себе упаковка и отправка данных делается элементарным использованием функций чтения всей строки до нажатия кнопки Ввода - fgets() с последующим вызовом функции send() , а на другой стороне - приёмом информации через recv() и выводом буфера на экран через cout
Процесс непрерывного перехода от send() к recv() и обратно реализуется через бесконечный цикл, из которого совершается выход по вводу особой комбинации клавиш. Пример блока кода для Серверной части:
Пришло время показать итоговый рабочий код для Сервера и Клиента. Чтобы не загромождать и так большой текст дополнительным кодом, даю ссылки на код на GitHub:
Несколько важных финальных замечаний:
В итоговом коде я не использую проверку на точное получение отосланной информации, т.к. при единичной (не циклической) отсылке небольшого пакета информации накладные расходы на проверку его получения и отправку ответа будут выше, чем выгоды от такой проверки. Иными словами – такие пакеты теряются редко, а проверять их целостность и факт доставки очень долго.
В последующих статьях я покажу реализацию полноценного чата между двумя сторонами (поможет разобраться в понятии «нити процесса»), а также покажу полноценную реализацию прикладного протокола по копированию файлов с Сервера на Клиент.
После того, как мы разобрались с основными протоколами и IP-адресами, не плохо было бы понять, как все это вместе стыкуется и работает. В заголовках протоколов нет наименований протоколов, а есть только номера. Кроме того, данные каждому приложению также доставляются с использованием номеров, которые называются портами. Пара - протокол и порт - позволяет стеку протоколов TCP/IP доставить данные нуждающемуся в них приложению. Увидеть номера протоколов можно в файле /etc/protocols или прочитать в RFC Assigned Numbers.
Содержание файла /etc/protocols:
Как видно из содержания файла protocols, практически всем основным протоколам присвоены номера. Другой группой магических цифр являются номера портов, которые закреплены за информационными сервисами Internet.
Информационный сервис - это прикладная программа, которая осуществляет обслуживание на определенном порте TCP или UDP. Собственно WKS - это и есть совокупность этих сервисов Internet. К сервисам относятся: доступ в режиме удаленного терминала, доступ к файловым архивам FTP, доступ к серверам World Wide Web и т. п. Распределение сервисов по портам можно найти в файле /etc/services. Сам файл сервисов очень большой, поэтому приведем только небольшой его фрагмент с наиболее интересными и популярными сервисами.
Фрагмент содержания файла /etc/services:
Среди сервисов, определенных в этом фрагменте, есть не только информационные сервисы типа World Wide Web или Gopher, но и протокол маршрутизации RIP, и протокол удаленной загрузки bootp, и сервис доменных имен BIND, и другие протоколы, которые нацелены на повышение работы сети и чрезвычайно полезны при администрировании сети.
Рис. 2.19. Использование номеров портов и номеров протоколов для передачи данных
Однако, этим механизм взаимодействия приложений в рамках TCP/IP не исчерпывается. Дело в том, что кроме статически назначенных WKS существуют еще динамически назначаемые сервисы.
Рис. 2.20. Динамическое назначение портов. Образование сокетов
Описывая взаимодействие в рамках TCP/IP обмена, мы до сих пор обходили стороной вопросы связанные с тем, как пакет реально транспортируется по сети. Все наши примеры ограничивались работой в рамках локальной сети. Теперь пора поговорить о том, как осуществляется передача пакетов по сети, или в терминах сетевого обмена - маршрутизация.
2.6. Основные принципы IP-маршрутизации
Как уже было сказано раньше, протокол IP не является протоколом ориентированным на соединение. Следовательно, решение о направлении IP-пакета на тот или иной сетевой интерфейс принимается шлюзом в момент прохождения через него пакета. Данное решение принимается на основании таблицы маршрутов, имеющаяся на каждом компьютере, который поддерживает стек протоколов TCP/IP.
Прежде чем перейти к описанию самой процедуры, введем пример сети, на которой будем рассматривать маршрутизацию пакетов (рисунок 2.21).
Рис. 2.21. Пример фрагмента локальной сети
На рисунке 2.21 изображены два фрагмента подсетей (144.206.160.0 и 144.206.128.0) сети класса B (144.206.0.0). Машина с интерфейсами, которые имеют адреса 144.206.160.32 и 144.206.130.137 - это шлюз между двумя подсетями, а машина с адресом сетевого интерфейса 144.206.130.3 - это шлюз сети с другой сетью, которая подключена к Internet.
Теперь отправим пакет машине из другой подсети 144.206.130.138. В этом случае на широковещательный запрос мы ответа не получим. Но пакет отправлять как-то надо. Для этой цели в описании маршрутов пакетов всегда есть IP-адрес, на который следует отправлять пакеты по умолчанию, если нет другого способа их рассылки. Естественно, что это адрес шлюза. Для машины 144.206.160.40 таким адресом является адрес 144.206.160.32. Физический адрес этого интерфейса получают точно также, как мы до этого получили физический адрес интерфейса 144.206.160.33. Принципиальное различие здесь заключается в том, что в первом случае адреса получателя и отправителя во фрейме физического протокола совпадали с адресами, которые указаны для IP-адресов из IP-пакета в таблице ARP. В случае шлюза здесь можно обнаружить несоответствие. Во фрейме в качестве получателя будет указан адрес интерфеса 144.206.160.32, в то время как в IP-пакете будет указан IP-адрес 144.206.130.138. Но ведь этому IP-адресу соответствует совсем другой физический адрес. Если быть более точным, то другой адрес будет соответствовать в сети Ethernet, если же речь идет о FrameRelay, то там может применяться относительная система адресации, что может приводить к равенству физических адресов.
Получив таким образом пакет модуль IP машины шлюза определяет, что это не его адрес указан в IP-пакете. После такого открытия IP-модуль шлюза принимает решение о дальнейшей отправке пакета. Отправлять пакет в тот же интерфейс, из которого он получен - бессмысленно, поэтому модуль IP никогда не отправляет пакеты назад. Следовательно, происходит поиск нужного интерфейса и через него снова рассылается широковещательный запрос ARP. В нашем случае такой запрос вернет для IP-адреса 144.206.130.138 физический адрес машины и пакет будет отправлен по этому адресу.
Если пакет отправляется в Internet, то шлюз не найдет физического адреса машины, и будет вынужден воспользоваться адресом рассылки по умолчанию. Таким образом, пакет попадет на шлюз через 144.206.130.3 и там будет решаться его судьба.
При рассмотрении стека протоколов TCP/IP на компьютере с одним сетевым интерфейсом было не очень понятно, зачем IP дублирует физический адрес, например, Ethernet, ведь каждому адресу Ethernet ставился в соответствие один IP-адрес. При рассмотрении архитектуры стека протоколов шлюза этот вопрос становится более понятным.
Из рисунка 2.22 видно, что таблица ARP создается для каждого интерфейса. Для получения таких таблиц можно использовать команду arp, где в качестве аргумента надо указать имя интерфейса. Модуль IP для шлюза общий и таблица маршрутов, а именно, она и используется модулем для перенаправления пакетов на интерфейсы, также общая.
Прежде чем перейти к описанию таблицы маршрутов и способам управления ею, обратим свое внимание на способы настройки сетевых интерфейсов, т.к. без них никакой стек протоколов работать с сетью не будет.
Рис. 2.22. Архитектура шлюза 144.206.130.137-144.206.160.32
2.7. Настройка операционной системы и сетевые интерфейсы
Для того, чтобы система работала со стеком протоколов TCP/IP необходимо соответствующим образом настроить ядро системы. В системах клона BSD конфигурация определяется в фале типа /usr/sys/conf, Например, во FreeBSD 2.x, - это файл /sys/i386/conf/GE-NERIC, если речь идет о только что установленной системе.
Обычно, на основе этого файла делают копию, которую называют local, и в этой копии проводят изменения настроек. В этом файле следует определить, чаще всего раскомментировать, строки, которые определяют сетевые интерфейсы, например, ed0 или ed1, если речь идет о Ethernet. Кроме этого, если машина будет работать шлюзом, нужно указать опцию GATEWAY, если нужно использовать распределенную файловую систему, то нужно заказать также соответствующие опции. Для таких протоколов, как SLIP и PPP, нужно определить еще и псевдоустройства. Ниже приведен пример такого файла конфигурации, точнее фрагмент.
Файл конфигурации ядра FreeBSD 2.x.
Из этого фрагмента видно, что данная машина позволяет работать с распределенной файловой системой, является шлюзом, имеет два сетевых интерфейса Ethernet: ed0 и ed1, а также еще два интерфейса на последовательных портах: sl0 и sl1, что соответствует портам sio0 и sio1.
Для того, чтобы данная конфигурация была реализована, надо пересобрать ядро. Для этого выполняется процедура генерации make-файлов, и после этого выполняется программа make. Как только новое ядро собрано его можно копировать в корень на место существующего ядра, но при этом надо не забыть сохранить старое ядро. Обычно Unix позволяет указать при загрузке имя файла ядра, что позволит загрузиться на машине, новое ядро которой оказалось неработоспособным.
При настройках Windows NT систему можно сконфигурировать для работы с сетями TCP/IP как при установке, так и потом, по мере необходимости. Правда отложенная конфигурация приведет к перезагрузке системы. Настраивается сеть из меню network в меню control panel. Там также определяется тип интерфейса и копируется с дистрибутива драйвер для данного интерфейса. Потом для интерфейса определяется семейство протоколов, где среди протоколов Microsoft можно найти и TCP/IP. Далее интерфейсу назначается IP-адрес, определяется шлюз и сервер доменных имен. Другие параметры, типа сервиса WINS (Windows Internet Name System), мы рассматривать не будем, т.к. это касается только Windows, а не Internet вообще.
При установке стека TCP/IP на машины с операционной системой MS-DOS следует иметь в виду, что здесь надо установить все выше описанное, но только в отличии от выше перечисленных операционных систем все программное обеспечение является прикладными задачами, а не частью операционной системы, поэтому никакой перенастройки ядра не происходит. Общая схема машины с OC MS-DOS, поддерживающей стек TCP/IP будет выглядеть так, как показано на рисунке 2.23.
Рис. 2.23. Структура стека программного обеспечения для поддержки стека протоколов TCP/IP на персональном компьютере
Естественно, что все эти модули, которые представлены на рисунке 2.23 должны быть указаны в файлах config.sys и autoexec.bat. Примеры содержания файлов autoexec.bat и config.sys приведены ниже и основаны они на настройке персонального компьютера notebook, который работает с сетью либо через сетевой PCMCI-адаптер Ethernet, либо через PCMCI-модем, что и отражено в вариантной загрузке.
Пример файла autoexec.bat
При работе через сетевой адаптер все интерфейсы устанавливаются только в config.sys, а в autoexec.bat подправляется только переменная окружения PATH.
Содержание файла config.sys
Впрочем, для различных систем и для различных пакетов, настройки могут довольно сильно отличаться, но структура всегда будет одной и той же.
Таким образом, после сборки нового ядра в системах Unix и Windows NT, или после выполнения настройки MS-DOS в системе появляются сетевые интерфейсы, стек протоколов TCP/IP и возможность совместной настройки интерфейсов и стека протоколов.
2.8. Настройка сетевых интерфейсов
Настройкой сетевых интерфейсов называют определение параметров обмена данными через сетевой интерфейс и присвоение ему IP-адреса. Сначала разберем, как это делается для интерфесов Ethernet, а потом уже для последовательных портов.
2.8.1. Настройка Ethernet-интерфейса
В данном случае никаких параметров настраивать не надо. Следует только назначить IP-адрес. Делается это по команде ifconfig:
В данном случае интерфесу ed0 назначается адрес 144.206.130.138, при этом на сети установлена маска 255.255.224.0. В общем случае команда ifconfig имеет следующий формат:
В этой команде параметры означают следующее:
interface - имя сетевого интерфейса;
address_famaly - тип адреса. В нашем случае - это inet, т.е. адрес Internet. Данное значение задается по умолчанию;
address - IP-адрес источника. Обычно в этом поле указывают IP-адрес который назначается сетевому интерфейсу. Если речь идет об интерфейсе Ethernet то этого адреса достаточно для его настройки.
dest_address - IP-адрес получателя. Данный адрес указывается для интерфейсов типа "точка-точка", например, для SLIP (sl0) или PPP (ppp0). В таких соединениях к концам линии связи подключены только два интерфейса и надо задать адрес обоих. В предыдущем поле (address) задают адрес своего интерфейса, а в данном поле интерфейса, установленного на другом конце линии.
В качестве параметров можно указать маску сети, как это сделано в примере. Этот параметр - обязательный. Существуют и другие параметры, например адрес широковещания, но их используют редко и обычно в данном случае их значения назначаются по умолчанию, так, например, адрес широковещания по умолчанию ограничен локальной сетью, и это правильно, т.к. нет нужды "светиться" за пределами своего шлюза.
Команда ifconfig может быть использована и для получения информации об интерфейсе. Для этого в ней надо указать только имя:
В данном случае была запрошена информация об интерфейсе ed1. Из отчета видно, какие флаги установлены для данного интерфейса и какой адрес ему назначен. С точки зрения администратора важно, что этот интерфейс в данный момент работоспособен (флаг UP) и не используется для сканирования пакетов в сети. Сканированию пакетов мы уделим достаточно внимания в разделе, посвященном вопросам безопасности сети.
Однако более исчерпывающую статистику можно получить при помощи команды netstat:
Кроме этого, следует обратить внимание интерфейс lo0, который закреплен за петлей 127.0.0.1. Для этого интерфейса также нужна команда ifconfig.
В данном случае имя localhost будет заменено на 127.0.01.
Но Ethernet - это только один из интерфейсов, которые могут быть использованы при подключении к сети. Кроме него довольно популярны интерфейсы подключения через последовательные порты.
2.8.2. Настройка SLIP
С последовательным портом работают через псевдоустройство sl0, которое и является интерфейсом последовательного порта. Для сцепления sl0 c устройством служит команда slattach:
Однако, формат slattach от одной системы к другой существенно изменяется. Так в ряде случаев сначала выдается команда slattach, которая присоединяет интерфейс к порту, а затем на нее выдается команда ifconfig для интерфейса sl.
В данном случае определен интерфейс для обмена данными по протоколу SLIP с компрессией, со скоростью на порту 38400, через порт /dev/sio01.
Как показывает практика, доступ по протоколу SLIP обычно организуют для удаленных пользователей, которые через данный шлюз хотят работать с Internet. Однако, не всегда можно "зацепиться" за модем надежно. Кроме того, на одном и том же порту могут обслуживаться как терминальные пользователи, так и пользователи Internet, а кроме того, туда же можно подключать и пользователей UUCP. В этом случае гораздо разумнее вместо slattach воспользоваться командой sliplogin. Данная команда также имеется не во всех операционных системах. Но обычно во всех есть ее аналог.
Суть sliplogin заключается в том, что пользователь, который дозвонился и работает в режиме удаленного терминала, имеет возможность самостоятельно запустить из этого режима присоединение sl интерфейса и его настройку на IP-адреса и параметры сессии. Это же дает возможность провайдерам, устанавливая стеки TCP/IP на персональных компьютерах, включать в их настройки скрипты, которые автоматически производят аутентификацию в удаленной машине и конфигурирование интерфейса для работы по SLIP.
- сначала дозваниваются до удаленной машины;
- затем вводят идентификатор и пароль;
- после входа в режим командной строки запускается команда sliplogin;
- после этого следует перейти в режим работы по SLIP.
Из этого режима обычно самостоятельно не выходят: либо обрывается связь, либо происходит какое-нибудь другое чрезвычайное событие. Поэтому систему настраивают таким образом, чтобы она сама завершала задачу и клала трубку на модеме.
Sliplogin управляется тремя файлами slip.hosts, slip.login и slip.logout. В файле slipl.host перечисляются пользователи, которым разрешено запускать sliplogin, и какие адреса при этом назначаются, а также тип протокола SLIP (с компрессией или без нее). Ниже приведен пример такого файла:
Однако, этого мало. Фактически, sliplogin выполняет slattach на порт, через который работает пользователь, но после этого этот порт надо сконфигурировать. Для этой цели служит скрипт slip.login:
После того, как соединение разорвалось, следует убрать все процессы которые им были вызваны. Для этой цели служит скрипт sliplogout.
Смысл написанного в этом файле заключается в том, что бы найти все остатки от sliplogin и по команде kill прервать их выполнение.
Однако для выделенных телефонных каналов обычно применяется другой протокол, а именно PPP.
2.8.3. Настройка интерфейса PPP
В различных системах настройка интерфейсов PPP производится по-разному. Поэтому мы снова будем основываться на примере BSDI и FreeBSD. Для работы через PPP в этих системах используется либо демон pppd, либо прикладная программа ppp. Обычно демон используется для выделенных линий и для приема звонков на выделенном под PPP порте. Программа ppp используется для запуска из командной строки.
Для того, чтобы использовать демона в файле конфигурации ядра, необходимо определить псевдоустройство ppp(0-1). Демона помещают в файл начальной загрузки. Настройки демона производятся при помощи файла настроек:
В данном примере мы используем файл /etc/ppp/options. В нем определяется порт, через который настраивается интерфейс, скорость на порте, адрес интерфейса и адрес ответного интерфейса провайдера, маска, установленная на сети провайдера, команда passive, которая заставляет оставлять данный интерфейс постоянно в таблице маршрутов, определение его как шлюза по умолчанию, и определяет его управление с локальной машины. Кроме этого, в данном файле есть еще и закомментированные опции, которые использовались автором во время отладки соединения. Включение этих двух опций приводит к полному дампированию пакетов PPP, что позволяет выяснить причины отсутствия соединения или плохого соединения.
В данном случае мы отлаживали соединение с relarn, где на конце relarn пакеты принимал маршрутизатор CISCO.
Если надо устанавливать соединение с удаленной машины со шлюзом, то вместо SLIP можно также использовать PPP. Но только в этом случае лучше всего использовать программу ppp. Она также настраивается через свой файл конфигурации, пример которого приведен ниже:
Надеюсь, что значение параметров в этом файле понятно и без лишних комментариев.
Главной особенностью программы ppp является то, что ее можно запустить в интерактивном режиме, по мере ее работы менять тип информации, который подлежит отладке.
При использовании и ppp, и pppd команды ifconfig на интерфейсы выдавать не надо, т.к. эти команды сами производят их настройку.
В заключении разговора о настройке интерфейсов приведем пример таблицы интерфейсов с машины, где работает сразу три разных интерфейса:
В этой таблице можно найти интерфейс Ethernet (ed1), интерфейс PPP (ppp0) и интерфейс SLIP (sl1), которые находятся в активном состоянии и принимают и отправляют пакеты.
Через интерфейс ed1 (IP-адрес: 144.226.43.1) доступна сеть 144.226.43.0, через интерфейс ppp0 (IP-адрес: 194.190.135.22) доступна сеть 144.190.135.0, которая является путем в Internet, через sl0 (IP-адрес: 194.226.43.99) работает удаленный пользователь.
Для обеспечения сетевых коммуникаций используются сокеты. Сокет это конечная точка сетевых коммуникаций. Каждый использующийся сокет имеет тип и ассоциированный с ним процесс. Сокеты существуют внутри коммуникационных доменов. Домены это абстракции, которые подразумевают конкретную структуру адресации и множество протоколов, которое определяет различные типы сокетов внутри домена. Примерами коммуникационных доменов могут быть: UNIX домен , Internet домен , и т.д.
В Internet домене сокет - это комбинация IP адреса и номера порта, которая однозначно определяет отдельный сетевой процесс во всей глобальной сети Internet. Два сокета, один для хоста-получателя, другой для хоста-отправителя, определяют соединение для протоколов, ориентированных на установление связи, таких, как TCP.
- Создание сокета
- Привязка к локальным именам
- Установление связи
- Передача данных
- Закрывание сокетов
- Пример функции, для установления WWW коннекции
Создание сокета
s = socket(domain, type, protocol);
Этот вызов основывается на информации о коммуникационном домене и типе сокета. Для использования особенностей Internet, значения параметров должны быть следующими: communication domain - AF_INET (Internet протоколы). type of the socket - SOCK_STREAM; Этот тип обеспечивает последовательный, надежный, ориентированный на установление двусторонней связи поток байтов. Выше был упомянут сокет с типом stream. Краткое описание других типов сокетов приведено ниже: Datagram socket - поддерживает двусторонний поток данных. Не гарантируется, что этот поток будет последовательным, надежным, и что данные не будут дублироваться. Важной характеристикой данного сокета является то, что границы записи данных предопределены. Raw socket - обеспечивает возможность пользовательского доступа к низлежащим коммуникационным протоколам, поддерживающим сокет-абстракции. Такие сокеты обычно являются датаграм- ориентированными. Функция socket создает конечную точку для коммуникаций и возвращает файловый дескриптор, ссылающийся на сокет, или -1 в случае ошибки. Данный дескриптор используется в дальнейшем для установления связи.
Для создания сокета типа stream с протоколом TCP , обеспечивающим коммуникационную поддержку, вызов функции socket должен быть следующим:
s = socket(AF_INET, SOCK_STREAM, 0);
Этап 1: Инициализация сокетных интерфейсов Win32API
Прежде чем непосредственно создать объект сокет, необходимо «запустить» программные интерфейсы для работы с ними. Под Windows это делается в два шага следующим образом:
Нужно определить с какой версией сокетов мы работаем (какую версию понимает наша ОС) и
Запустить программный интерфейс сокетов в Win32API. Ну либо расстроить пользователя тем, что ему не удастся поработать с сокетами до обновления системных библиотек
Первый шаг делается с помощью создания структуры типа WSADATA , в которую автоматически в момент создания загружаются данные о версии сокетов, используемых ОС, а также иная связанная системная информация: WSADATA wsData;
Второй шаг – непосредственный вызов функции запуска сокетов с помощью WSAStartup() . Упрощённый прототип данной функции выглядит так:
Первый аргумент функции – указание диапазона версий реализации сокетов, которые мы хотим использовать и которые должны быть типа WORD . Этот тип данных является внутренним типом Win32API и представляет собой двухбайтовое слово (аналог в С++: unsigned short ). Функция WSAStartup() просит вас передать ей именно WORD , а она уже разложит значение переменной внутри по следующему алгоритму: функция считает, что в старшем байте слова указана минимальная версия реализации сокетов, которую хочет использовать пользователь, а в младшем – максимальная. По состоянию на дату написания этой статьи (октябрь 2021 г.) актуальная версия реализации сокетов в Windows – 2. Соответственно, желательно передать и в старшем, и в младшем байте число 2. Для того, чтобы создать такую переменную типа WORD и передать в её старший и младший байты число 2, можно воспользоваться Win32API функцией MAKEWORD(2,2) .
Второй аргумент функции – просто указатель на структуру WSADATA , которую мы создали ранее и в которую подгрузилась информация о текущей версии реализации сокетов на данной машине.
WSAStartup() в случае успеха возвращает 0, а в случае каких-то проблем возвращает код ошибки, который можно расшифровать последующим вызовом функции WSAGetLastError() .
Важное замечание: поскольку сетевые каналы связи и протоколы в теории считаются ненадежными (это отдельный большой разговор), то критически важно для сетевого приложения анализировать все возможные ошибки, которые возникают в процессе вызовов сокетных функций. По этой причине каждый вызов таких функций мы будем анализировать на ошибки и в случае их обнаружения завершать сетевые сеансы и закрывать открытые сокеты. Используем для этого переменную erStat типа int .
Также важно после работы приложения обязательно закрыть использовавшиеся сокеты с помощью функции closesocket(SOCKET ) и деинициализировать сокеты Win32API через вызов метода WSACleanup() .
Итого код Этапа 1 следующий:
Да, кода мало, а описания много. Так обычно и бывает, когда хочешь глубоко в чем-то разобраться. Так что на лабе будешь в первых рядах.
Этап 3: Привязка сокета к паре IP-адрес/Порт
Сокет уже существует, но еще неполноценный, т.к. ему не назначен внешний адрес, по которому его будут находить транспортные протоколы по заданию подключающихся процессов, а также не назначен порт, по которому эти подключающиеся процессы будут идентифицировать процесс-получатель.
Такое назначение делается с помощью функции bind() , имеющей следующий прототип:
Функция bind() возвращает 0 , если удалось успешно привязать сокет к адресу и порту, и код ошибки в ином случае, который можно расшифровать вызовом WSAGetLastError() - см. итоговый код Этапа 3 далее.
Тут надо немножно притормозить и разобраться в том, что за такая структура типа sockaddr передается вторым аргументом в функцию bind() . Она очень важна, но достаточно запутанная.
Итак, если посмотреть в её внутренности, то выглядят они очень просто: в ней всего два поля – (1) первое поле хранит семейство адресов, с которыми мы уже встречались выше при инициализации сокета, а (2) второе поле хранит некие упакованные последовательно и упорядоченные данные в размере 14-ти байт. Бессмысленно разбираться детально как именно эти данные упакованы, достаточно лишь понимать, что в этих 14-ти байтах указан и адрес, и порт, а также дополнительная служебная информация для других системных функций Win32API .
Но как же явно указать адрес и порт для привязки сокета? Для этого нужно воспользоваться другой структурой, родственной sockaddr , которая легко приводится к этому типу - структурой типа sockaddr_in .
В ней уже более понятные пользователю поля, а именно:
Семейство адресов - опять оно ( sin_family )
Вложенная структура типа in_addr , в которой будет храниться сам сетевой адрес ( sin_addr )
Технический массив на 8 байт ( sin_zero[8] )
При приведении типа sockaddr_in к нужному нам типу sockaddr для использования в функции bind() поля Порт (2 байта), Сетевой адрес (4 байта) и Технический массив (8 байт) как раз в сумме дают нам 14 байт, помещающихся в 14 байт, находящихся во втором поле структуры sockaddr . Первые поля у указанных типов совпадают – это семейство адресов сокетов (указываем AF_INET ). Из этого видно, что структуры данных типа sockaddr и sockaddr_in тождественны, содержат одну и ту же информацию, но в разной форме для разных целей.
Соответственно, ввод данных для структуры типа sockaddr_in выглядит следующим образом:
Создание структуры типа sockaddr_in : sockaddr_in servInfo;
Заполнение полей созданной структуры servInfo
servInfo.sin_port = htons(); порт всегда указывается через вызов функции htons() , которая переупаковывает привычное цифровое значение порта типа unsigned short в побайтовый порядок понятный для протокола TCP/IP (протоколом установлен порядок указания портов от старшего к младшему байту или «big-endian»).
Далее нам надо указать сетевой адрес для сокета. Тип этого поля – структура типа in_addr , которая по своей сути представляет просто особый «удобный» системным функциям вид обычного строчного IPv4 адреса. Таким образом, чтобы указать этому полю обычный IPv4 адрес, его нужно сначала преобразовать в особый числовой вид и поместить в структуру типа in_addr .
Благо существует функция, которая переводит обычную строку типа char[] , содержащую IPv4 адрес в привычном виде с точками-разделителями в структуру типа in_addr – функция inet_pton() . Прототип функции следующий:
В случае ошибки функция возвращает значение меньше 0.
Соответственно, если мы хотим привязать сокет к локальному серверу, то наш код по преобразованию IPv4 адреса будет выглядеть так:
erStat = inet_pton(AF_INET, “127.0.0.1”, &ip_to_num);
Результат перевода IP-адреса содержится в структуре ip_to_num. И далее мы передаем уже в нашу переменную типа sockaddr_in значение преобразованного адреса:
Вся нужная информация для привязки сокета теперь у нас есть, и она хранится в структуре servInfo . Можно смело вызывать функцию bind() , не забыв при этом привести servInfo из типа sockaddr_in в требуемый функции sockaddr* . Тогда итоговый код Этапа 3 (слава богу закончили) выглядит так:
Что такое порт
Порт - это числовое значение, назначаемое каждому приложению или процессу. При связи данные передаются от отправляющего устройства к устройству назначения (приемнику). Если на целевом устройстве запущено три приложения, номер порта помогает определить приложение, для которого требуются полученные данные. Компьютер имеет в общей сложности 65536 портов. Номера портов от 0 до 1023 зарезервированы для использования системой. Порт помогает идентифицировать процесс, который требует полученных данных.
Рисунок 1: Компьютерная сеть
Ключевые области покрыты
1. Что такое порт
- определение, функциональность
2. Что такое сокет
- определение, функциональность
3. Разница между сокетом и портом
- Сравнение основных различий
Этап 0: Подключение всех необходимых библиотек Win32API для работы с сокетами
Сокеты не являются «стандартными» инструментами разработки, поэтому для их активизации необходимо подключить ряд библиотек через заголовочные файлы, а именно:
WinSock2.h – заголовочный файл, содержащий актуальные реализации функций для работы с сокетами.
WS2tcpip.h – заголовочный файл, который содержит различные программные интерфейсы, связанные с работой протокола TCP/IP (переводы различных данных в формат, понимаемый протоколом и т.д.).
Ну и в конце Этапа 0 подключаем стандартные заголовочные файлы iostream и stdio.h
Итого по завершению Этапа 0 в Серверной и Клиентской частях приложения имеем:
Обратите внимание: имя системной библиотеки ws2_32.lib именно такое, как это указано выше. В Сети есть различные варианты написания имени данной библиотеки, что, возможно, связано иным написанием в более ранних версиях ОС Windows. Если вы используете Windows 10, то данная библиотека называется именно ws2_32.lib и находится в стандартной папке ОС: C:/Windows/System32 (проверьте наличие библиотеки у себя, заменив расширение с “lib” на “dll”).
Что такое сокет
Сокет - это интерфейс для отправки и получения данных по определенному порту. Каждое устройство в сети имеет IP-адрес. Это идентификатор для распознавания устройства в сети. При отправке данных этот IP-адрес помогает передавать данные в правильный пункт назначения. Комбинация IP-адреса и порта также называется сокетом. В реальном сценарии порт похож на номер квартиры в многоквартирном доме, а розетка - на дверь этой квартиры.
Читайте также: