DNK_C_C++_Go_Rust
45 subscribers
14 photos
45 links
DNK - дневник кодера С и С++
Download Telegram
🚀Это отличный ресурс для программистов, работающих с C++. Можно найти подробную документацию по стандартной библиотеке, STL, различным версиям стандарта C++, а также примеры кода и объяснения по ключевым аспектам языка.

Справочник по C++
C++11, C++14, C++17, C++20, C++23, C++26 │ Поддержка компиляторами C++11, C++14, C++17, C++20, C++23, C++26

Справочник по языку C
C89, C95, C99, C11, C17, C23 │ Поддержка компиляторами C99, C23


https://ru.cppreference.com/w/

#cpp #programming

👉 @cpp_lib
#449_ALG_C_Cpp_TP

Структура данных интрузивный список реализация на Си и С++.

Интрузивный список (intrusive list) — структура данных, где каждый элемент содержит указатели на следующий и предыдущий элементы, и эти указатели хранятся непосредственно внутри самих элементов, а не в отдельной структуре узла списка.
Это позволяет избежать дополнительного уровня косвенности и улучшает производительность.


Реализация на C.
#include <stdio.h>
#include <stdlib.h>

// Определение структуры элемента списка
struct IntrusiveElement {
int data;
struct IntrusiveElement *next;
struct IntrusiveElement *prev;
};

// Функция для создания нового элемента
struct IntrusiveElement* create_element(int value) {
struct IntrusiveElement *new_element = (struct IntrusiveElement *)malloc(sizeof(struct IntrusiveElement));
if (new_element == NULL) {
printf("Ошибка выделения памяти\n");
exit(1);
}
new_element->data = value;
new_element->next = NULL;
new_element->prev = NULL;
return new_element;
}

// Функция для вставки элемента после заданного элемента
void insert_after(struct IntrusiveElement *element, int value) {
struct IntrusiveElement *new_element = create_element(value);
if (element->next != NULL) {
element->next->prev = new_element;
}
new_element->next = element->next;
new_element->prev = element;
element->next = new_element;
}

// Функция для удаления элемента
void remove_element(struct IntrusiveElement *element) {
if (element->prev != NULL) {
element->prev->next = element->next;
}
if (element->next != NULL) {
element->next->prev = element->prev;
}
free(element);
}

// Функция для печати содержимого списка
void print_list(struct IntrusiveElement *head) {
struct IntrusiveElement *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}

int main() {
struct IntrusiveElement *head = create_element(0); // Создание головы списка

// Вставка элементов в список
insert_after(head, 1);
insert_after(head->next, 2);
insert_after(head->next->next, 3);

// Печать списка
print_list(head);

// Удаление второго элемента
remove_element(head->next->next);

// Печать обновленного списка
print_list(head);

return 0;
}



.
Реализация на C++.
#include <iostream>
using namespace std;

// Определение класса элемента списка
class IntrusiveElement {
public:
int data;
IntrusiveElement *next;
IntrusiveElement *prev;
};

// Класс для управления списком
class IntrusiveList {
private:
IntrusiveElement *head;
public:
IntrusiveList() { head = nullptr; } // Конструктор инициализирует пустой список

// Метод для вставки элемента после заданного элемента
void insertAfter(IntrusiveElement *element, int value) {
IntrusiveElement *new_element = new IntrusiveElement();
new_element->data = value;
if (element->next != nullptr) {
element->next->prev = new_element;
}
new_element->next = element->next;
new_element->prev = element;
element->next = new_element;
}

// Метод для удаления элемента
void removeElement(IntrusiveElement *element) {
if (element->prev != nullptr) {
element->prev->next = element->next;
}
if (element->next != nullptr) {
element->next->prev = element->prev;
}
delete element;
}

// Метод для печати содержимого списка
void printList() {
IntrusiveElement *current = head;
while (current != nullptr) {
cout << current->data << " -> ";
current = current->next;
}
cout << "NULL" << endl;
}
};

int main() {
IntrusiveList list;
IntrusiveElement *head = new IntrusiveElement(); // Создание головы списка
head->data = 0;
list.head = head;
// Вставка элементов в список
list.insertAfter(list.head, 1);
list.insertAfter(list.head->next, 2);
list.insertAfter(list.head->next->next, 3);

// Печать списка
list.printList();

// Удаление второго элемента
list.removeElement(list.head->next->next);

// Печать обновленного списка
list.printList();

return 0;
}



Эти реализации показывают базовую функциональность интрузивного списка: вставка элементов, удаление элементов и печать содержимого списка.
#448_TLS_Vim

Как в Vim переместиться к определению функции?

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

Основные способы:

1. Используя встроенные возможности поиска тегов Vim поддерживает систему тегов (ctags), которая позволяет создавать индексы функций, классов и других элементов программы.
Чтобы воспользоваться этой системой, нужно сначала создать файл с индексами, используя ctags, а затем использовать команды Vim для навигации.

Для установки ctags (если еще не установлен):
sudo apt-get install exuberant-ctags  # Для Ubuntu/Debian


Создадим файл тегов для вашего проекта:
ctags -R .


Это создаст файл tags в текущей директории, который будет содержать индексы всех функций и классов в проекте.
Теперь в Vim можно перемещаться к определенному элементу следующим образом:
— если курсор находится над именем функции, нажмимаем <Ctrl-]>;
— используя команду: :tag <имя_функции>.


2. Использование плагинов — плагины расширяют функциональность Vim и делают работу с кодом удобнее.
Например, можно установить такие плагины, как:
YouCompleteMe — плагин для автодополнения и навигации по коду.
Coc.nvim — аналогичен YouCompleteMe, но использует LSP (Language Server Protocol).
Tagbar — отображение структуры файла в виде боковой панели.


Пример настройки с использованием Coc.nvim:
Установите Coc.nvim согласно инструкции на GitHub.
Настройте плагин для работы с нужным ЯП
(Python, C++ и др.).
После установки и настройки, находясь внутри редактора, можно перемещаться к определениям функций с помощью команд:
<Ctrl+>] — перемещение к определению функции.
<Ctrl+t> — возврат назад после перехода.


3. Интеграция с IDE-функциональностью через Language Server Protocol (LSP) — современные редакторы и плагины используют протокол LSP для интеграции с языковой поддержкой.
Например, coc.nvim или nvim-lspconfig позволяют легко подключать серверы поддержки для разных языков.

Пример для Python:
Установите сервер поддержки для Python, например, Pyright или Jedi.
Подключите его к
Vim с помощью плагина LSP (например, через coc.nvim).
Используйте команды перемещения, аналогичные тем, что описаны выше для
Coc.nvim.



На практике наиболее удобным вариантом является использование плагинов вроде YouCompleteMe или Coc.nvim, поскольку они предоставляют мощный функционал для навигации по коду и автоматического завершения.
#450_ALG_C_Cpp

Структура данных "ЛЕС".

ЛЕС — структура данных, представляющая собой набор деревьев (деревьев поиска).

В контексте компьютерных наук чаще всего используется термин «лес» в двух основных значениях:
Ориентированный ациклический граф (DAG) — структура данных, где каждый узел имеет одного родителя, кроме корневого узла, который родителей не имеет. Такой DAG называют лесом, потому что он состоит из нескольких деревьев, каждое из которых представляет собой ориентированное дерево (с одним корнем).
Лес в контексте файловых систем — также иногда употребляется этот термин для обозначения набора каталогов (директорий), где каждая директория может содержать другие директории или файлы.
Это похоже на древовидную структуру файлового хранилища, но здесь важно понимать, что файловые системы могут иметь циклы через символические ссылки, поэтому строго говоря, это не совсем лес в смысле графа.


Пример леса (DAG):
Представим себе набор элементов, где у каждого элемента есть один родитель, но сами элементы могут формировать несколько отдельных деревьев.
Например, такие деревья могут выглядеть следующим образом:
     A       B
/ | \ \
C D E F

Здесь два дерева: одно с корнем A, другое с корнем B.

Этот набор деревьев образует лес. Каждый элемент может иметь максимум одного родителя, и между элементами не должно быть циклов.


Основные операции над лесом:
Добавление нового узла — можно добавить новый узел как корень (создать новое дерево) либо как потомок уже существующего узла.
Удаление узла — удаление узла возможно вместе с его потомками или только самого узла, передавая его детей его родителю.
Поиск пути от корня до листа — можно использовать алгоритмы обхода (например, глубину или ширину) для нахождения путей между узлами.
Обход леса — аналогично дереву, можно применять алгоритмы обхода, начиная с каждого корня отдельного дерева.


Применение лесов:
Графовые структуры данных — леса используются для представления иерархии объектов, когда объекты имеют одного родителя, но самих объектов много, и они образуют несколько независимых структур.
Файловые системы — в ОС леса часто применяются для описания каталога (корень дерева), содержащего другие подкаталоги и файлы.
Компьютерная графика — лес может использоваться для моделирования сложных сцен, состоящих из множества объектов, связанных друг с другом иерархически.


Связь с другими структурами данных:
Лес можно рассматривать как обобщенную версию бинарного дерева, где количество дочерних узлов у каждого узла не ограничено.
В случае, если в лесу существует только одно дерево, оно превращается в обычное дерево (или корневое дерево).


Таким образом, лес является удобной структурой для работы с наборами взаимосвязанных объектов, организованных в виде нескольких деревьев.
Примеры реализации леса на С и С++.

Реализация леса на ЯП С и С++ предполагает создание структуры данных, представляющей собой набор деревьев.

Реализация на С:
#include <stdio.h>
#include <stdlib.h>

// Структура узла дерева
typedef struct Node {
int data;
struct Node *parent; // Указатель на родителя
struct Node *leftChild; // Левый ребенок
struct Node *rightSibling; // Правый брат/сестра
} Node;

// Функция для создания нового узла
Node* createNode(int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Ошибка выделения памяти\n");
exit(1);
}
newNode->data = value;
newNode->parent = NULL;
newNode->leftChild = NULL;
newNode->rightSibling = NULL;
return newNode;
}

// Функция для добавления ребенка к узлу
void addChild(Node* parent, Node* child) {
if (parent->leftChild == NULL) {
parent->leftChild = child;
} else {
Node* sibling = parent->leftChild;
while (sibling->rightSibling != NULL) {
sibling = sibling->rightSibling;
}
sibling->rightSibling = child;
}
child->parent = parent;
}

// Функция для печати леса
void printForest(Node* root) {
if (root == NULL) return;

Node* current = root;
while (current != NULL) {
printf("%d ", current->data);
if (current->leftChild != NULL) {
printForest(current->leftChild);
}
current = current->rightSibling;
}
}

int main() {
// Создание леса
Node* forestRootA = createNode(10); // Первое дерево
Node* forestRootB = createNode(20); // Второе дерево

Node* child1 = createNode(11);
Node* child2 = createNode(12);
Node* child3 = createNode(13);

addChild(forestRootA, child1);
addChild(forestRootA, child2);
addChild(forestRootA, child3);

Node* child21 = createNode(21);
Node* child22 = createNode(22);

addChild(forestRootB, child21);
addChild(forestRootB, child22);

// Печать леса
printForest(forestRootA);
printForest(forestRootB);

return 0;
}



Реализация на С++:
#include <iostream>
using namespace std;

// Класс узла дерева
class Node {
public:
int data;
Node* parent; // Указатель на родителя
Node* leftChild; // Левый ребенок
Node* rightSibling; // Правый брат/сестра

// Конструктор класса
Node(int value) : data(value), parent(nullptr), leftChild(nullptr), rightSibling(nullptr) {}
};

// Функция для добавления ребенка к узлу
void addChild(Node* parent, Node* child) {
if (parent->leftChild == nullptr) {
parent->leftChild = child;
} else {
Node* sibling = parent->leftChild;
while (sibling->rightSibling != nullptr) {
sibling = sibling->rightSibling;
}
sibling->rightSibling = child;
}
child->parent = parent;
}

// Функция для печати леса
void printForest(Node* root) {
if (root == nullptr) return;

Node* current = root;
while (current != nullptr) {
cout << current->data << " ";
if (current->leftChild != nullptr) {
printForest(current->leftChild);
}
current = current->rightSibling;
}
}

int main() {
// Создание леса
Node* forestRootA = new Node(10); // Первое дерево
Node* forestRootB = new Node(20); // Второе дерево

Node* child1 = new Node(11);
Node* child2 = new Node(12);
Node* child3 = new Node(13);

addChild(forestRootA, child1);
addChild(forestRootA, child2);
addChild(forestRootA, child3);

Node* child21 = new Node(21);
Node* child22 = new Node(22);

addChild(forestRootB, child21);
addChild(forestRootB, child22);

// Печать леса
printForest(forestRootA);
printForest(forestRootB);

return 0;
}

.
Структура узла (struct Node в С и class Node в С++):
Поле data хранит значение узла.
Поле parent указывает на родителя узла.
Поля leftChild и rightSibling указывают соответственно на левого ребенка и правого брата/сестру.

Функция createNode/конструктор Node
— cоздает новый узел с заданным значением и инициализирует указатели на родителя, левого ребенка и правого брата/сестры в NULL.

Функция addChild — добавляет новый узел как ребенка к существующему узлу. Если у узла еще нет детей, новый узел становится левым ребенком. Иначе новый узел добавляется справа от последнего существующего ребенка.

Функция printForest — обходит лес и выводит значения всех узлов в порядке обхода в ширину.

Пример использования:
Создадим два дерева с корнями 10 и 20, добавим к ним детей и выведем результат.


Эти примеры демонстрируют базовую реализацию леса на С и С++, которую можно расширить для решения различных задач.
#451_ALG_C_PRGM

Реализация "частотного словаря" (Frequency Dictionary) на С.

Программа производит подсчет того, сколько раз в тексте встречается каждое слово.
При подсчете игнорируются все символы, которые не является буквами английского алфавита. Так же не имеет значения какой регистр имеют буквы в слове (слова "Text" и "TEXT", подсчитываются как употребление слова "text" два раза).
Результатом работы программы является список содержащий слова и частоту их упоминания в тексте, которые в расположены в порядке убывания по частоте упоминания.

В программе используется структура данных BST (бинарное дерево поиска), а сортировка результата производится путем создания массива указателей на узлы дерева и сортировки этого массива в порядке убывания.

Программу можно расширить добавив возможность сортировки по возрастанию или в алфавитном порядке, так же легко получить количество уникальных слов. (функция для подсчета количества узлов BST уже реализована).
.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE 1024

typedef struct Node {
char *word;
size_t wcount;
struct Node *left;
struct Node *right;
} TNode;

typedef TNode *Tree;

void out_error_message(const char* msg);
Tree add_tnode(Tree root, const char* word);
char* scan_word(char* eof);
void tree_destroy(Tree root);
char* str_realloc(char* src, size_t* size);
void get_array_pnodes(const Tree root, Tree array_pnodes[SIZE], size_t* index);
size_t get_tsize(const Tree root);
void insert_sort(Tree array[SIZE], const size_t tsize,
int (*cmp)(const Tree node1, const Tree node2));
int compare_wcount(const Tree node1, const Tree node2);

int main(void) {
Tree root = NULL;
char *word = NULL;
char eof = '\0';

do {
word = scan_word(&eof);
if (strlen(word)) {
root = add_tnode(root, word);
}
free(word);
word = NULL;
} while (eof != EOF);

size_t tsize = get_tsize(root);
Tree array_pnodes[SIZE] = {NULL};
size_t index = 0;

get_array_pnodes(root, array_pnodes, &index);
insert_sort(array_pnodes, tsize, compare_wcount);

for (size_t i = 0; i != tsize; ++i) {
printf("%s %d\n", array_pnodes[i]->word, array_pnodes[i]->wcount);
}

tree_destroy(root);
return 0;
}

char* str_realloc(char* src, size_t* size) {
*size *= 2;
char* newstr = (char*)realloc(src, *size * sizeof(char));
if (newstr) {
src = newstr;
}
return src;
}

char* scan_word(char* eof) {
char ch;
size_t size = 16;
int idx = 0;
char* word = (char*)calloc(size, sizeof(char));
if (word == NULL) {
out_error_message("Memory allocation error");
}
while (isspace(ch = getchar()));
do {
if (isalpha(ch)) {
if (idx == size) {
word = str_realloc(word, &size);
}
word[idx++] = tolower(ch);
}
} while (EOF != (ch = getchar()) && !isspace(ch));
if (ch == EOF) {
*eof = ch;
}
return word;
}

Tree add_tnode(Tree root, const char* word) {
if (!root) {
root = (Tree)malloc(sizeof(TNode));
if (root) {
size_t wlen = strlen(word);
root->left = root->right = NULL;
root->word = (char*)calloc(wlen + 1, sizeof(char));
if (root->word) {
strncpy(root->word, word, wlen);
root->wcount = 1;
}
} else if (!root->word) {
out_error_message("Memory allocation error");
}
return root;
}

int cmp = strcmp(word, root->word);
if (!cmp) {
++(root->wcount);
} else if (0 < cmp) {
root->right = add_tnode(root->right, word);
} else {
root->left = add_tnode(root->left, word);
}
return root;
}

size_t get_tsize(const Tree root) {
if (!root) {
return 0;
}
return 1 + get_tsize(root->left) + get_tsize(root->right);
}

void get_array_pnodes(const Tree root, Tree array_pnodes[SIZE], size_t *index) {
if (!root) {
return;
}
get_array_pnodes(root->left, array_pnodes, index);
array_pnodes[(*index)++] = root;
get_array_pnodes(root->right, array_pnodes, index);
}

int compare_wcount(const Tree node1, const Tree node2) {
return node1->wcount > node2->wcount;
}

void insert_sort(Tree array[SIZE], const size_t tsize,
int (*cmp)(const Tree node1, const Tree node2)) {
for (size_t i = 0, j = 1; j != tsize; ++i, j = i + 1) {
while (j >= 1 && cmp(array[j], array[j - 1])) {
Tree tmp = array[j - 1];
array[j - 1] = array[j];
array[j--] = tmp;
}
}
}

void tree_destroy(Tree root) {
if (!root) {
return;
}
free(root->word);
tree_destroy(root->left);
tree_destroy(root->right);
free(root);
}

void out_error_message(const char *msg) {
perror(msg);
abort();
}

.
#452_C_LIB

Функции bzero() и memset() в С.

В ЯП C функция bzero использовалась для заполнения блока памяти нулями.
Однако эта функция устарела и больше не рекомендуется к использованию в современных стандартах языка C. Вместо неё лучше применять стандартные функции, такие как memset.

Вот пример использования устаревшей функции bzero, хотя её лучше избегать (хотя в книге UNIX._Razrabotka_setevyh_prilojeniy_Master-klass_3-e_izdanie_2007_PITER_U.R.Stivens_B.Fenner_E.M.Rudoff_1040_pg автор на странице 39 пишет что в первом издании книги сделал десять ошибок поменяв местами аргументы функции memset (аналог функции bzero). Дело в том, что компилятор С не распознает это как ошибку, так как оба аргумента, которые автор поменял местами принадлежали одному типу. Поэтому нужно быть внимательным при использовании аналогов функции bzero таких как memset):
// заголовочный файл для bzero
#include <strings.h>

char buffer[100];
// Заполняем массив buffer нулями
bzero(buffer, sizeof(buffer));


Рекомендуемый способ — использование стандартной библиотеки <string.h>:
Правильнее и современнее использовать стандартную библиотеку <string.h> и функцию memset.
Вот эквивалентная замена для примера выше:
// Заголовочный файл для memset
#include <string.h>

char buffer[100];
// Устанавливаем весь буфер в нули
memset(buffer, 0, sizeof(buffer));



Преимущества использования memset перед bzero:
memset является частью стандарта ISO C и поддерживается всеми компиляторами.
Код становится переносимым между разными платформами и системами.
Функциональность ясна и хорошо документирована.



Таким образом, предпочтительно заменить устаревшую функцию bzero на современную и стандартизированную memset (но будьте внимательны не путайте аргументы).
#453_C_LIB_NTWK

Функции inet_pton и inet_addr в С.

Функция inet_pton() используется в C для преобразования строкового представления IP-адреса в двоичное представление формата сети (network byte order), которое удобно хранить и передавать по сетям.

Прототип функции выглядит следующим образом:
#include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);


Параметры:
afcемейство адресов (например, AF_INET для IPv4 или AF_INET6 для IPv6).
srccтроковое представление IP-адреса (например, "192.168.1.1" или "2001:db8::1").
dst — буфер, куда будет записан преобразованный IP-адрес в бинарном виде.

Возвращаемое значение:
1 — преобразование успешно выполнено.
0 переданный адрес недействителен для указанного семейства адресов.
-1 — произошла ошибка (см. переменную errno).


Пример использования:
Допустим, необходимо преобразовать строку IP-адреса ("192.168.1.1") в бинарное представление и сохранить его в структуре sockaddr_in:
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

int main() {
struct in_addr ip_bin;
int result = inet_pton(AF_INET, "192.168.1.1", &ip_bin.s_addr);

if(result > 0) {
printf("Преобразование прошло успешно.\n");
} else if(result == 0) {
fprintf(stderr, "Адрес неверен.\n");
} else {
perror("Ошибка:");
}

return EXIT_SUCCESS;
}


Пояснения:
Используется семейство адресов AF_INET, так как работаем с IPv4.
Адрес представлен строкой "192.168.1.1".
Результат сохраняется в структуру in_addr, где поле .s_addr хранит сам IP-адрес в нужном формате.

Таким образом, inet_pton() позволяет легко и эффективно преобразовывать строки в форматы, удобные для сетевых операций.
Функция inet_addr() в C предназначена для преобразования строкового представления IP-адреса версии 4 (IPv4), заданного в десятичном виде с точками (например, "192.168.1.1"), в числовое представление типа unsigned long, подходящее для передачи по сети.

Эта функция широко применяется в сетевом программировании на C для обработки IP-адресов.
Прототип функции:
#include <arpa/inet.h>

unsigned long inet_addr(const char *cp);


Аргументы:
cp — строка, содержащая IP-адрес в формате вида "xxx.xxx.xxx.xxx".

Возвращаемые значения:
Если IP-адрес корректен, возвращается соответствующее число типа unsigned long, представляющее этот IP-адрес в формате big-endian (формат, подходящий для сетей).
Если строка некорректна, возвращает специальное значение INADDR_NONE (обычно равное (unsigned)-1).

Пример использования:
Простая программа, демонстрирующая работу функции inet_addr():
#include <stdio.h>
#include <arpa/inet.h>

int main() {
unsigned long addr = inet_addr("192.168.1.1"); // Конвертируем строку в числовой IP-адрес

if(addr != INADDR_NONE) { // Проверяем успешность конвертации
printf("IP-адрес: %lu\n", addr); // Выведем полученный IP-адрес
} else {
printf("Некорректный IP-адрес!\n");
}

return 0;
}


Важные моменты:
Функция inet_addr() работает исключительно с IPv4-адресами.
Она вернёт ошибку, если введён некорректный адрес (например, неправильный формат или недопустимые символы).
Значение, возвращённое функцией, представлено в порядке байтов от старшего к младшему (big endian), что важно учитывать при работе с различными архитектурами процессоров.


Альтернативы:
Для IPv6-адресов и более универсального подхода лучше использовать функцию inet_pton(), которая поддерживает оба протокола (IPv4 и IPv6).
#454_NTWK_TP

Описание основных портов TCP/IP.

Что такое TCP/IP-порты?
TCP/IP-порты представляют собой виртуальные каналы связи, используемые приложениями для взаимодействия друг с другом через сеть. Каждый порт имеет уникальный номер от 0 до 65535, разделённый на три диапазона:

Стандартные порты (0–1023) — зарезервированы IANA (Internet Assigned Numbers Authority) для широко используемых сервисов и приложений.
Зарегистрированные порты (1024–49151) — используются зарегистрированными организациями и компаниями для определённых целей.
Динамические/частные порты (49152–65535) — назначаются динамически операционными системами для временных соединений.


Основные стандартные порты TCP/IP:

HTTP (порт 80)
Протокол: TCP
Назначение: Веб-серверы передают веб-контент (HTML, CSS, JavaScript и др.) клиентам браузеров.
Пример использования: доступ к сайтам через браузер (http://example.com).

HTTPS (порт 443)
Протокол: TCP
Назначение: Безопасная версия HTTP, использующая шифрование SSL/TLS для защиты данных.
Пример использования: защищённые веб-сайты (https://example.com).

FTP (порты 20 и 21)
Протокол: TCP
Назначение:
Передача файлов между клиентом и сервером.
Порты:
Порт 20: канал данных.
Порт 21: управляющий канал.
Пример использования: загрузка файлов на удалённый сервер.

SMTP (порт 25)
Протокол: TCP
Назначение:
Отправка электронной почты между почтовыми серверами.
Пример использования: отправка писем с почтового клиента на сервер.

POP3 (порт 110)
Протокол: TCP
Назначение:
Получение электронной почты с почтового сервера.
Пример использования: получение писем с сервера в почтовые клиенты.

IMAP (порт 143)
Протокол: TCP
Назначение:
Управление почтой на сервере (просмотр, удаление, организация папок).
Пример использования: работа с почтовым ящиком через клиентские приложения.

SSH (порт 22)
Протокол: TCP
Назначение:
Удалённое управление устройствами и выполнение команд через зашифрованное соединение.
Пример использования: администрирование серверов и рабочих станций.

DNS (порт 53)
Протокол: TCP/UDP
Назначение:
Преобразование доменных имен в IP-адреса и обратно.
Пример использования: преобразование имени сайта в IP-адрес для доступа к ресурсу.

Telnet (порт 23)
Протокол: TCP
Назначение: Простой способ удалённого управления терминалами и устройствами
(без шифрования).
Пример использования: настройка оборудования или тестирование соединения.
.
🤗1
DHCP (порт 67 и 68)
Протокол: UDP
Назначение:
Автоматическое назначение IP-адресов устройствам в сети.
Порты:
Порт 67:
сервер.
Порт 68: клиент.
Пример использования: автоматическая настройка сетевых настроек компьютера при подключении к локальной сети.

SNMP (порт 161)
Протокол: UDP
Назначение: Мониторинг состояния и производительности сетевого оборудования.

Пример использования: сбор статистики и диагностика проблем в сети.

LDAP (порт 389)
Протокол: TCP
Назначение:
Каталоговая служба для хранения и предоставления информации о пользователях и объектах сети.
Пример использования: аутентификация пользователей в корпоративных сетях.

Как работают порты?
Каждый сервис на компьютере привязывается к определённому порту, что позволяет другим компьютерам направлять запросы именно этому сервису.
Например, когда вводите адрес сайта в браузере, компьютер посылает запрос на сервер по порту
80 (или 443 для HTTPS), и сервер возвращает данные, используя тот же порт.


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


Знание назначения стандартных портов помогает лучше понимать работу интернета и сетей, оптимизировать настройки безопасности и устранять проблемы с соединением.
Правильная настройка и защита портов являются важными аспектами кибербезопасности и стабильной работы компьютерных систем.
🤗1
#455_NTWK_TP

Для чего используется 13 порт?

TCP/IP-порт номер 13 используется сетевыми службами для передачи времени в формате ASCII (Daytime Protocol).
Это один из старых протоколов, разработанный ещё в эпоху ранних сетей ARPANET. Протокол Daytime предназначен исключительно для тестирования сети и проверки доступности серверов.


Основные характеристики порта 13:
Назначение: передача текущего времени сервера.
Формат передаваемых данных: строка ASCII символов с указанием текущего времени (например, Wed Feb 8 14:32:00 UTC 2023).
Тип протокола: TCP или UDP.
Использование: преимущественно устаревшее и редко применяемое на современных серверах и устройствах.


Сегодня этот протокол практически не используется, поскольку большинство устройств используют более современные методы синхронизации времени, такие как NTP (Network Time Protocol), работающий на порте 123.


Тем не менее, некоторые старые системы или специализированные устройства всё ещё могут поддерживать этот порт для совместимости с исторически сложившимися конфигурациями.


Порт 13 использовался для простого обмена временем, но в настоящее время является пережитком прошлого и почти не применяется в повседневной практике.
#456_C_NTWK_LIB

Функция htons() в С.

Функция htons используется в C для преобразования числа из формата представления хоста (host byte order) в сетевой порядок байтов (network byte order).
Это особенно важно при работе с сетевыми протоколами, такими как TCP/IP, поскольку разные архитектуры компьютеров используют разный порядок байтов («big-endian» vs «little-endian»).


Пример использования:
Если необходимо отправить число 12345 (десятичное значение) по сети. Для этого сначала преобразуем его в сетевой порядок байтов с помощью функции htons.
#include <stdio.h>
#include <arpa/inet.h> // здесь объявлена htons()

int main() {
unsigned short host_number = 12345;

// Преобразование числа в сетевой порядок байтов
unsigned short network_number = htons(host_number);

printf("Хостовое представление: %hu\n", host_number); // Выведет исходное число
printf("Сетевое представление: %hu\n", network_number); // Выведет число в сетевом порядке байтов

return 0;
}


Пояснения:
Host Byte Orderпорядок байтов на конкретной архитектуре компьютера (например, x86 — little endian).
Network Byte Order всегда big-endian (старший байт идет первым), стандартизированный порядок байтов для сетей IP.


Зачем нужен этот переход?
Разные компьютеры хранят многобайтовые значения по-разному.
Например, на процессорах Intel данные хранятся младшими байтами вперед ("little-endian"), тогда как большинство сетевых протоколов требуют именно старший байт впереди ("big-endian"). Функция htons() автоматически решает проблему различия порядков байтов между хостом и сетью.


Использование htons() гарантирует совместимость ваших приложений при обмене данными по сети независимо от архитектуры компьютера.
#457_C_Cpp_NTWK_LIB

Средства стандартной библиотеки для работы с сетью в С и С++.

Стандартная библиотека языка C/C++ включает ряд функций и заголовочных файлов, предназначенных для работы с сетями и сокетами. Эти средства позволяют создавать приложения, взаимодействующие по различным сетевым протоколам, таким как TCP/IP.


Основные инструменты для работы с сетью в C и C++.

Заголовочные файлы:

<sys/socket.h> (C) — этот файл содержит основные определения для работы с сокетами.
Он определяет типы данных, константы и базовые функции, необходимые для открытия, закрытия и управления сокетами.

Основные функции:
socket: создание нового сокета.
bind: привязка адреса к сокету.
listen: установка режима прослушивания порта.
accept: принятие входящих соединений.
connect: установление соединения с удалённым сервером.
send, recv: отправка и получение данных.

Пример использования:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

// Создание сокета
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
perror("Ошибка при создании сокета");


<netinet/in.h> (C) — файл содержит структуры и макросы для представления IPv4 адресов и портов.
Здесь определены такие важные структуры, как sockaddr_in, используемые для хранения IP-адресов и номеров портов.

Структура sockaddr_in выглядит примерно так:
struct sockaddr_in {
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* номер порта */
struct in_addr sin_addr;/* IP-адрес */
};


<arpa/inet.h> (C)предоставляет функции для конвертации строковых представлений IP-адресов и портов в двоичные формы, удобные для передачи в сеть.

Ключевые функции:
inet_pton: преобразует строку в структуру типа in_addr или in6_addr.
inet_ntop: обратное преобразование бинарного представления адреса обратно в строку.

Примеры:
#include <arpa/inet.h>

char ip_str[] = "192.168.1.1";
struct in_addr addr;

if (inet_pton(AF_INET, ip_str, &addr)) {
printf("IP успешно преобразован!\n");
}


<unistd.h> (C)этот файл также важен для работы с сокетами, так как содержит определение функций ввода-вывода и базовых операций UNIX, таких как закрытие сокетов (close) и чтение-запись данных (read, write).

Примеры основных операций с сетью:

— Открытие сокета.
— Создание клиентского сокета
.
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
perror("Ошибка при открытии сокета");
exit(EXIT_FAILURE);
}


— Cоздание сервера:
int server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server_fd < 0) {
perror("Ошибка при создании сокета");
exit(EXIT_FAILURE);
}

struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT_NUMBER);

if (bind(server_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
perror("Ошибка при привязке адреса");
close(server_fd);
exit(EXIT_FAILURE);
}

if (listen(server_fd, MAX_QUEUE_SIZE)) {
perror("Ошибка при установке слушающего состояния");
close(server_fd);
exit(EXIT_FAILURE);
}


Отправка и приём данных.
— Отправка данных клиенту:
ssize_t bytes_sent = send(sockfd, buffer, len, flags);
if (bytes_sent != len) {
perror("Ошибка отправки данных");
}


Приём данных от клиента:
ssize_t bytes_received = recv(sockfd, buffer, BUFLEN, 0);
if (bytes_received <= 0) {
if (bytes_received == 0) {
fprintf(stderr, "Клиент отключился.\n");
} else {
perror("Ошибка приёма данных");
}
}



Средства для работы с сетью в C++.

Сокеты в C++.
В чистом стандарте C++ отсутствуют встроенные возможности для работы с сетью. Однако программисты часто пользуются библиотеками и решениями, разработанными поверх стандартных возможностей POSIX и Windows API.


Boost.Asio (библиотека стороннего разработчика)является популярной кросс-платформенной библиотекой для асинхронного ввода-вывода, включая работу с сетью.
Она позволяет легко разрабатывать высокоуровневые сетевые приложения на C++, предоставляя объектно-ориентированный интерфейс.
Пример простого эхо-сервера на Boost.Asio:
#include <boost/asio.hpp>
#include <iostream>

using boost::asio::ip::tcp;

void handle_connection(boost::asio::io_context& io_ctx, tcp::socket&& sock) {
try {
char data[1024];
size_t len = sock.read_some(boost::asio::buffer(data));
std::cout.write(data, len);
sock.write_some(boost::asio::buffer(data, len));
} catch(std::exception& e) {
std::cerr << "Исключение: " << e.what() << "\n";
}
}

int main() {
try {
boost::asio::io_context io_ctx;
tcp::acceptor acceptor(io_ctx, tcp::endpoint(tcp::v4(), PORT_NUMBER));

for (;;) {
tcp::socket sock(io_ctx);
acceptor.accept(sock);
std::thread(handle_connection, std::ref(io_ctx), std::move(sock)).detach();
}
} catch(const std::exception& ex) {
std::cerr << "Ошибка: " << ex.what() << '\n';
}

return 0;
}


Boost.Asio поддерживает как синхронные, так и асинхронные операции, позволяя эффективно управлять потоками и ресурсами.


Работа с сетью в C и C++ требует знания низкоуровневых инструментов, предоставляемых системой (POSIX, WinAPI), однако они предоставляют гибкость и производительность. Если необходима работа на высоком уровне абстракции, можно воспользоваться сторонними библиотеками вроде Boost.Asio, упрощающими разработку сложных сетевых приложений.
#458_C_NTWK_LIB

Функция connect() в С.

Функция connect() в языке программирования C предназначена для установления соединения с удаленным сервером по определенному адресу и порту.
Эта функция активно применяется при разработке клиентов для различных сетевых протоколов, таких как HTTP, FTP, SSH и др., работающих поверх протокола TCP.

Прототип функции:
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);


Где:
sockfd — дескриптор ранее открытого сокета (полученный функцией socket()).
addr — структура типа sockaddr, содержащая адрес и порт удаленного узла, к которому устанавливается соединение.
addrlen — размер структуры sockaddr в байтах.


Структура sockaddr — типичная структура для хранения адреса имеет вид:
struct sockaddr_in {
sa_family_t sin_family; // Семейство адресов (AF_INET)
uint16_t sin_port; // Номер порта
struct in_addr sin_addr; // Адрес IP
};


Чтобы заполнить поля структуры, нужно задать семейство адресов, преобразовать номер порта в сетевой порядок байтов с помощью функции htons() и установить нужный IP-адрес.


Как работает connect()?
Процесс подключения осуществляется следующим образом:
Клиент создает сокет с помощью функции socket().
Заполняет структуру sockaddr_in нужными значениями (адресом и номером порта).
Вызывает функцию connect(), передавая ей созданный сокет и заполненную структуру.
Ожидает успешного завершения процесса установки соединения.


Пример использования:
Предположим, необходимо подключиться к веб-серверу по адресу example.com на стандартный порт HTTP (порт 80):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

#define SERVER_IP "74.125.239.113"
#define SERVER_PORT 80

int main(void) {
int sockfd;
struct sockaddr_in serv_addr;

// Создаем сокет
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Ошибка при создании сокета");
exit(EXIT_FAILURE);
}

memset(&serv_addr, 0, sizeof(serv_addr)); // Обнуляем структуру
serv_addr.sin_family = AF_INET; // Используем семейство IPv4
serv_addr.sin_port = htons(SERVER_PORT); // Устанавливаем порт (преобразуя в сетевой порядок)
inet_pton(AF_INET, SERVER_IP, &(serv_addr.sin_addr)); // Конвертируем IP-адрес в двоичный формат

// Подключаемся к серверу
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Ошибка подключения к серверу");
close(sockfd);
exit(EXIT_FAILURE);
}

printf("Подключение установлено!\n");

// Теперь можно отправлять запросы и получать ответы...

close(sockfd);
return 0;
}


Возвращаемые значения:
0: Соединение успешно установлено.
-1: Произошла ошибка, код ошибки доступен через переменную errno.

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


Важные моменты:
После успешного вызова connect() можно начинать передачу данных через сокет с использованием функций send() и recv().
Если сервер временно недоступен или занят, функция connect() блокирует выполнение программы до тех пор, пока соединение не установится или не произойдет таймаут.
Можно настроить неблокирующий режим сокета перед вызовом connect(), используя опцию fcntl() или флаг O_NONBLOCK, чтобы избежать блокировки потока исполнения.


Функция connect() является ключевой частью механизма взаимодействия с удалёнными серверами и лежит в основе большинства сетевых приложений на языке C.
#459_C_NTWK_LIB

Функции getaddrinfo() и tcp_connect() в С.

Функции getaddrinfo() и tcp_connect() широко применяются в программах на языке С для облегчения процедуры подключения к удалённому узлу по имени домена или IP-адресу.
Они решают ключевые задачи сетевого программирования, обеспечивая гибкое и переносимое решение для инициализации TCP-соединений.


Что делает каждая из функций?

getaddrinfo() — служит для перевода символьного имени узла (доменное имя или IP-адрес) вместе с именем сервиса (номер порта или название службы) в список структур, содержащих всю необходимую информацию для установки сетевого соединения.

Пример прототипа:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(
const char* node, // Имя узла (IP или DNS)
const char* service, // Порт или сервис
const struct addrinfo* hints, // Дополнительные опции
struct addrinfo** res // Результат (список адресов)
);


Параметры:
node: имя узла (либо IP-адрес, либо DNS-имя).
service: порт или название услуги (может быть задан как числовой порт или строка вида "http").
hints: дополнительные настройки, такие как тип сокета, протокол и желаемое семейство адресов.
res: выходная переменная, куда помещается связанный список найденных адресов.

Возвращает:
0, если операция выполнена успешно,
Код ошибки, если возникла проблема (например, неверный узел или служба).

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


tcp_connect()вспомогательная функция, реализующая подключение к серверу с помощью стандартного интерфейса BSD Socket API (такого как socket(), connect() и др.).
Обычно она помогает упростить процедуру создания сокета и установки TCP-соединения.

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

Пример реализации простой версии tcp_connect():
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int tcp_connect(const char* hostname, const char* port) {
struct addrinfo hints, *result, *rp;
int sfd, errcode;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // Разрешить оба семейства (IPv4 и IPv6)
hints.ai_socktype = SOCK_STREAM;// Тип сокета TCP
hints.ai_protocol = IPPROTO_TCP;// Протокол TCP

// Получаем список возможных адресов
errcode = getaddrinfo(hostname, port, &hints, &result);
if (errcode != 0) {
fprintf(stderr, "Ошибка getaddrinfo(): %s\n", gai_strerror(errcode));
return -1;
}

// Пробуем каждый полученный адрес
for (rp = result; rp != NULL; rp = rp->ai_next) {
// Создаем сокет
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1) continue; // Перейти к следующему адресу

// Подключаемся к выбранному адресу
if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; // Успешное подключение!

close(sfd); // Неудача с данным адресом, закрываем сокет
}

freeaddrinfo(result); // Освобождаем память

if (rp == NULL) { // Ни один адрес не подошел
fprintf(stderr, "Не удалось подключиться ни к одному адресу\n");
return -1;
}

return sfd; // Вернуть дескриптор сокета
}


Использование такой функции облегчает написание простых TCP-клиентов, скрывая рутинные шаги обработки адресов и создания сокетов.


getaddrinfo() полезна для трансляции имен узлов и сервисов в готовые сетевые адреса, облегчая доступ к удалённым ресурсам.

tcp_connect() представляет собой удобный способ создания и подключения TCP-сокета, упрощая последовательность действий и обработку ошибок.

Эти функции совместно помогают быстро и надёжно реализовать сетевую коммуникацию в приложениях на языке С.