Telnet chat server for Linux x86_64

Тема в разделе "WASM.BEGINNERS", создана пользователем Hacker, 16 июл 2023.

  1. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    262
    [​IMG]
    --- Сообщение объединено, 18 фев 2024 ---
    Структура создается из двух атрибутов, чтобы не проверять указатели, а сразу знать посмотрев/сравнив флаг. Создается динамический массив из таких типов данных
    Так это не проблема, обработал данные, потом вызвал освобождение памяти

    Все эти телодвижения с новым типом данных применяется там где пориджи традиционно ждут что для них изобретут сборщик мусора. Мы об одном и том же говорим?

    Не совсем понятно о чем ты. Вероятно можно добавить в структуру для этого новый атрибут
     
  2. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    590
    Дававайте продолжим тему концепт-разговоров на примере вашего кодеса

    как насчет разобрать топис СОЛИД-а

    upload_2024-2-18_12-15-14.png
     

    Вложения:

  3. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.498
    Адрес:
    Россия, Нижний Новгород
    А зачем вводить дополнительный атрибут, если значение указателя (нулевой/ненулевой) само по себе служит флагом?
    Ну вот - сразу требуется выделять память под массив, плюс одна аллокация на ровном месте.
    И придётся писать код для заполнения и очистки этого массива, для удаления из него указателей, которые мы хотим вернуть наружу.
    Да, типа того. Только сборщик мусора работает где-то в фоне, а в RAII объекты удаляются сразу при выходе из области видимости.
    А оно так и работает. В процедурном стиле это можно сделать так:
    Код (C++):
    1. struct Object
    2. {
    3.     void* buffer;
    4. };
    5.  
    6. void destroy(Object* object)
    7. {
    8.     if (object->buffer)
    9.     {
    10.         free(object->buffer);
    11.         object->buffer = nullptr;
    12.     }
    13. }
    14.  
    15. void move(Object* from, Object* to)
    16. {
    17.     if (from == to)
    18.     {
    19.         return;
    20.     }
    21.  
    22.     destroy(to);
    23.     to->buffer = from->buffer;
    24.     from->buffer = nullptr;
    25. }
    26.  
    27. Object object{};
    28. object.buffer = malloc(123);
    29.  
    30. Object newOwner{};
    31. move(&object, &newOwner);
    Здесь сразу есть потенциальные проблемы:
    1. Во внешнем коде и содержимое Object'a, и данные по указателю может бесконтрольно поменять кто угодно.
    2. Можно сделать так: Object obj2 = obj - в итоге два объекта владеют одним и тем же буфером

    И то же самое в объектном:
    Код (C++):
    1. class Object
    2. {
    3. private:
    4.     void* m_buffer;
    5.  
    6. public:
    7.     Object() noexcept
    8.         : m_buffer(nullptr)
    9.     {
    10.     }
    11.  
    12.     explicit Object(void* buffer) noexcept
    13.         : m_buffer(buffer)
    14.     {
    15.     }
    16.  
    17.     // Запретили его копировать:
    18.     Object(const Object&) = delete;
    19.     Object& operator = (const Object&) = delete;
    20.  
    21.     // Научили его создаваться, принимая владение:
    22.     Object(Object&& obj) noexcept
    23.         : m_buffer(std::exchange(obj.m_buffer, nullptr))
    24.     {
    25.     }
    26.  
    27.     // Научили уже созданный объект принимать владение:
    28.     Object& operator = (Object&& obj) noexcept
    29.     {
    30.         if (&obj != this)
    31.         {
    32.             if (m_buffer)
    33.             {
    34.                 free(m_buffer);
    35.             }
    36.  
    37.             m_buffer = std::exchange(obj.m_buffer, nullptr);
    38.         }
    39.  
    40.         return *this;
    41.     }
    42.  
    43.     // Научили удаляться:
    44.     ~Object() noexcept
    45.     {
    46.         if (m_buffer)
    47.         {
    48.             free(m_buffer);
    49.         }
    50.     }
    51.  
    52.     // Геттер для мутабельного контекста:
    53.     void* buffer() noexcept
    54.     {
    55.         return m_buffer;
    56.     }
    57.  
    58.     // Геттер для константного и мутабельного контекстов:
    59.     const void* buffer() const noexcept
    60.     {
    61.         return m_buffer;
    62.     }
    63. };
    Вроде кода получилось в два раза больше, зато в использовании такие объекты нагляднее, а код, работающий с ними, короче. И нет проблем из процедурного варианта:
    Код (C++):
    1. Object obj1(malloc(...));
    2. Object obj2(obj1); // Нельзя
    3. Object obj2(std::move(obj1)); // Можно
    4. obj2 = obj1; // Нельзя
    5. obj2 = std::move(obj1); // Можно
    6. obj.m_buffer = ...; // Нельзя
    7.  
    8. void func(const Object& obj)
    9. {
    10.     memcpy(obj.buffer(), ...); // Нельзя
    11. }
    12.  
    Кроме того, если у тебя есть объект, хранящий другие объекты, у которых определены операторы перемещения, ты автоматически можешь безопасно перемещать и объект-контейнер:
    Код (C++):
    1. struct SuperObject
    2. {
    3.     Object obj1;
    4.     Object obj2;
    5.     Object obj3;
    6.  
    7.     SuperObject(SuperObject&&) = default;
    8.     SuperObject& operator = (SuperObject&&) = default;
    9. };
    10.  
    11. SuperObject superObject = ...;
    12. SuperObject newOwner = std::move(superObject);
    В общем, чем сложнее и больше проект - тем заметнее преимущества объектной модели.
    И фактически ты делаешь то же самое, что процедурным кодом, только пишешь эту рутину с освобождениями и мувами не сам, а просишь компилятор.
    А внутри разницы не будет. Один раз пишешь обёртки, а затем юзаешь их везде, и компилятор сам сгенерирует код, который тебе пришлось бы писать вручную.

    Это всё касается стилистической стороны, чтобы упростить себе жизнь.
    А другая - это всё, что касается наследования и полиморфизма. Его тоже можно сделать процедурным стилем, вкладывая структурки в структурки и юзая всякие макросы, типа CONTAINING_RECORD, но опять же - это просто неудобно.
    --- Сообщение объединено, 18 фев 2024 ---
    Если пилить по солиду - было бы неплохо декомпозировать сервер хотя бы на функции, не говоря уже о классах: у меня же всё в main'e сплошной стеной.
    Например, ввести классы для соединения, для сервера и клиентов, интерфейсы для обработки команд и их реализацию (тот же обработчик /setname), инфу о клиентах хранить в клиентах, а не в отдельной мапе - тут можно далеко зайти.
    Для технодемок, имхо, достаточно самого простого кода в лоб - чтобы чел понял принцип, как оно вообще работает (а чел не понял...), а архитектуру уже накрутить не проблема.
    --- Сообщение объединено, 18 фев 2024 ---
    О, кстати, к вопросу о безопасности. Нашёл бажок в операторе перемещения в Auto::Fd - забыл закрыть текущий открытый дескриптор.
    Код (C++):
    1. Fd& operator = (Fd&& fd) noexcept
    2. {
    3.     if (&fd == this)
    4.     {
    5.         return *this;
    6.     }
    7.  
    8.     // Вот этот if надо добавить:
    9.     if (valid())
    10.     {
    11.         ::close(m_fd);
    12.     }
    13.  
    14.     m_fd = std::exchange(fd.m_fd, k_invalid);
    15.  
    16.     return *this;
    17. }
     
    alex_dz и MaKsIm нравится это.
  4. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    262
    Почему?
    Зачем? :)

    Особенно заметно когда чужой код читаешь, где автар объектной моделью обмазался.
    Преимущества есть если надо надолго спрятать в таком коде тайны мадридского двора.
     
    Последнее редактирование: 18 фев 2024
  5. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.498
    Адрес:
    Россия, Нижний Новгород
    Потому что константность указателя на объект не означает константность данных, на которые смотрит мутабельный указатель в этом константном объекте.
    Код (C++):
    1. void func(const Object* obj)
    2. {
    3.     // Можно, т.к. ты не меняешь состояние самого obj'a, а const распространяется только на него:
    4.     memcpy(obj->buffer, ...);
    5. }
    Например, чтобы положить в коллекцию или перенести в другое место.
    Когда смотришь на структуру с указателями и интами, непонятно, безопасно ли её копировать через memcpy или нет.
    Не все указатели требуют освобождения (например, указатели на статические данные) и не все инты семантически копируемы (например, дескрипторы).
    А так как процедуры никак к объекту не привязаны, ты можешь даже не знать, что есть специальные функции для копирования/перемещения такого объекта, или просто ошибиться - ведь компилятор тебя никак не ограничит.
    --- Сообщение объединено, 18 фев 2024 ---
    Да, бывает, что перебарщивают с абстракциями. Но самый лучший пример недостатков процедурной модели - это исходники ядра Linux.
     
  6. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    262
    Это совсем наркомания, никто в здавом уме не будет такой код делать,
    ты начал уже какие-то гипотетические примеры приводить.

    Если этой логике следовать, с объектами можно такой же чуши нагенерировать

    Там слишком большой простор для полемики, я имел в виду что-то поконкретней для обсуждения,
    на примере типов данных для устранения проблем в сложных проектах
     
    Последнее редактирование: 18 фев 2024
  7. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.498
    Адрес:
    Россия, Нижний Новгород
    В здравом или не в здравом, но так сделать можно. В реальности примеры будут намного менее очевидные.
    Без инкапсуляции всегда упираешься в то, что все данные торчат наружу, а все способы их скрыть упираются в непрозрачные объекты или даже в непрозрачные массивы для хранения объектов.
    Когда у тебя в структурке не одно поле, а пятьдесят, и ты не знаешь внутреннюю кухню объекта, очень сложно понять, что из полей ты можешь использовать напрямую, для каких требуются геттеры, а какие поля вообще нужны только для внутреннего использования.
    Разница в том, что с объектами сделать это сложнее: надо специально задаться целью так написать, а в случае процедурной модели, когда всё открыто, так получается само.
     
  8. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    262
    Структура с 50 полями? Это идиотом надо быть

    Я с такими проблемами не сталкиваюсь, для меня это немного странно
    --- Сообщение объединено, 18 фев 2024 ---
    Чтобы данные торчали наружу, надо быть архитектурным астронавтом )
    --- Сообщение объединено, 18 фев 2024 ---
    --- Сообщение объединено, 18 фев 2024 ---
    Код (Text):
    1. namespace Auto
    2. {
    3. class Fd
    4. {
    5. public:
    6.     static constexpr int k_invalid = -1;
    7. private:
    8.     int m_fd;
    9. public:
    10.     Fd() noexcept
    11.         : m_fd(k_invalid)
    12.     {
    13.     }
    14.     explicit Fd(int fd) noexcept
    15.         : m_fd(fd)
    16.     {
    17.     }
    18.     Fd(const Fd&) noexcept = delete;
    19.     Fd(Fd&& fd) noexcept
    20.         : m_fd(std::exchange(fd.m_fd, k_invalid))
    21.     {
    22.     }
    23.     ~Fd() noexcept
    24.     {
    25.         if (valid())
    26.         {
    27.             ::close(m_fd);
    28.         }
    29.     }
    30.     Fd& operator = (const Fd&) noexcept = delete;
    31.     Fd& operator = (Fd&& fd) noexcept
    32.     {
    33.         if (&fd == this)
    34.         {
    35.             return *this;
    36.         }
    37.         m_fd = std::exchange(fd.m_fd, k_invalid);
    38.         return *this;
    39.     }
    40.     bool valid() const noexcept
    41.     {
    42.         return m_fd != k_invalid;
    43.     }
    44.     explicit operator bool () const noexcept
    45.     {
    46.         return valid();
    47.     }
    48.     int fd() const noexcept
    49.     {
    50.         return m_fd;
    51.     }
    52. };
    53. } // namespace Auto
    54. namespace Poll
    55. {
    56. class Entry : public pollfd
    57. {
    58. public:
    59.     Entry(int fd, short events) noexcept
    60.         : pollfd{ fd, events, 0 }
    61.     {
    62.     }
    63.     Entry(const Entry&) noexcept = delete;
    64.     Entry(Entry&& desc) noexcept
    65.         : pollfd(std::exchange(static_cast<pollfd&>(desc), pollfd{}))
    66.     {
    67.     }
    68.     Entry& operator = (const Entry&) noexcept = delete;
    69.     Entry& operator = (Entry&& desc) noexcept
    70.     {
    71.         if (&desc == this)
    72.         {
    73.             return *this;
    74.         }
    75.         static_cast<pollfd&>(*this) = std::exchange(static_cast<pollfd&>(desc), {});
    76.         return *this;
    77.     }
    78.     void reset() noexcept
    79.     {
    80.         pollfd::revents = 0;
    81.     }
    82.     bool signaled() const noexcept
    83.     {
    84.         return (pollfd::revents & pollfd::events) != 0;
    85.     }
    86. };
    87. } // namespace Poll
    ООП в этом случае идеальное решение :lol:
     
  9. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.498
    Адрес:
    Россия, Нижний Новгород
    Ну вот не придумали ничего лучше: sched.h - include/linux/sched.h - Linux source code (v6.7.5) - Bootlin
    Я про это и говорю: преимущества объектной модели становятся заметны с увеличением масштабов.
    На маленьких проектах в пару функций разницы нет - ну закроешь ты вручную хэндлы, руки не отвалятся.
    В сложных проектах есть всё - и структуры на сотню полей, и функции на 500+ строк, и перекидывание объектов между коллекциями, и времена жизни со счётчиками ссылок.
    ООП здесь решает и проблему управления ресурсами (если язык без сборщика мусора), и структурирует код, позволяя скрывать для внешнего мира ненужное, и строить иерархии объектов через наследование.

    Короче говоря, ООП - просто эволюция процедурного программирования. Было многословно и неудобно - придумали объекты с ассоциированными функциями, наследованием и скрытием внутренностей - стало попроще.
    Концепция оказалась удачной и универсальной, подходящей под любой софт - вот её и используют.
    Можно и по старинке процедурами и структурками, но зачем?
    --- Сообщение объединено, 18 фев 2024 ---
    Ну почему безальтернативно, есть же ещё функциональное программирование - тоже удобная штука, когда надо обрабатывать данные по цепочке. Все эти ваши reduce, map, filter.
    Да и можно ли навязать что-то неудобное? Начинаешь писать на си - сразу не хватает деструкторов, не хватает полиморфизма, сразу много лишнего кода и ручной работы. Нафиг оно надо, когда можно проще и быстрее.
     
  10. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    262
    В основном так и происходит

    Назвать это процедурным программированием как-то язык не поворачивается )

    Для меня до сих пор не понятно, где эта хрень с ООП полезна.
    Про "масштабные проекты" как-то не заходит

    Все эти абстрактные ООП-концепции и шаблоны очень эволюционны

    [​IMG]
     
  11. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    590
    видно олд-скульную школу! в С++ 30 летней давности так да.. но уже давно нету надобности
    https://pvs-studio.com/en/blog/posts/cpp/1100/
    --- Сообщение объединено, 18 фев 2024 ---
    Еще я б добавил чутка защитного программирования - раз оперируете указателями, может попасться nullptr на вход у будет оЙ
     
    Последнее редактирование: 18 фев 2024
  12. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.498
    Адрес:
    Россия, Нижний Новгород
    А, да, всё время забываю, что у него внутри проверка. В delete, кстати, тоже.
     
  13. Kulesh

    Kulesh Member

    Публикаций:
    0
    Регистрация:
    16 фев 2024
    Сообщения:
    30
    Какие именно сообщения вызывает poll
    Что дискриптор сокета ему нужно передать я понял
    И по протаколу самого telnet там в коде 5 сообшений, 4 прочитать 1 записать
     
  14. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.498
    Адрес:
    Россия, Нижний Новгород
    poll не вызывает сообщения.
    Ты передаёшь ему массив из нескольких структур pollfd, хранящих дескрипторы, на которых ты хочешь подождать.
    Каждый pollfd в массиве хранит дескриптор, маску событий, которые ты ожидаешь, и выходную маску revents, в которую poll запишет события, которые произошли в дескрипторе.
    Формируешь этот массив, отправляешь в poll, и, как только хотя бы в одном дескрипторе произойдут указанные в events события, poll вернёт управление.
    После этого тебе нужно пробежаться по всему массиву дескрипторов и найти просигналившие, проверив у них поле revents.
    В poll нужно передавать серверный и все клиентские сокеты.
    На первой итерации, когда клиентов ещё нет, передаёшь только серверный.
    Если серверный просигналил - значит, пришёл новый клиент.
    Принимаешь этого клиента функцией accept и добавляешь этот клиентский сокет в массив дескрипторов для poll’a, и снова вызываешь poll.
    Теперь мы ждём уже на двух дескрипторах.
    Если просигналит клиентский - значит, клиент прислал данные, вычитываешь их через recv.
    Если просигналил серверный - значит, пришёл ещё один клиент, и его тоже надо принять через accept и добавить в массив для poll’a.

    Важно: каждый раз перед вызовом poll ты должен обнулить revents у всех элементов в массиве.
    --- Сообщение объединено, 23 фев 2024 ---
    Структура pollfd выглядит так:
    Код (C):
    1. struct pollfd {
    2.     int fd; /* file descriptor */
    3.     short events; /* requested events */
    4.     short revents; /* returned events */
    5. };
    Подробное описание ты можешь найти в манах: https://man7.org/linux/man-pages/man2/poll.2.html
     
  15. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.242
    если в среднем на одного клиента тратим 1000кб == получаем ужо 100 гиг памяти, а по лагам совсем швах == примерно 1мс на клиента :)
     
  16. Hacker

    Hacker Member

    Публикаций:
    0
    Регистрация:
    9 авг 2018
    Сообщения:
    180
    Вот что мне ответил GPT:
    Код (Text):
    1.  
    2. В C на Linux вы можете использовать функцию `poll` для опроса сокетов и чтения данных из стандартного ввода. Вот пример, который демонстрирует, как это сделать:
    3.  
    4. ```c
    5. #include <stdio.h>
    6. #include <stdlib.h>
    7. #include <string.h>
    8. #include <unistd.h>
    9. #include <poll.h>
    10. #include <arpa/inet.h>
    11.  
    12. #define PORT 12345
    13. #define MAX_CLIENTS 10
    14. #define BUFFER_SIZE 1024
    15.  
    16. int main() {
    17.     int server_fd, client_fd;
    18.     struct sockaddr_in address;
    19.     int opt = 1;
    20.     socklen_t addrlen = sizeof(address);
    21.     char buffer[BUFFER_SIZE];
    22.  
    23.     // Создаем сокет
    24.     if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
    25.         perror("socket failed");
    26.         exit(EXIT_FAILURE);
    27.     }
    28.  
    29.     // Привязываем сокет к порту
    30.     if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
    31.         perror("setsockopt");
    32.         exit(EXIT_FAILURE);
    33.     }
    34.     address.sin_family = AF_INET;
    35.     address.sin_addr.s_addr = INADDR_ANY;
    36.     address.sin_port = htons(PORT);
    37.  
    38.     if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
    39.         perror("bind failed");
    40.         exit(EXIT_FAILURE);
    41.     }
    42.  
    43.     // Начинаем прослушивание
    44.     if (listen(server_fd, 3) < 0) {
    45.         perror("listen");
    46.         exit(EXIT_FAILURE);
    47.     }
    48.  
    49.     printf("Сервер запущен. Ожидание подключения...\n");
    50.  
    51.     // Создаем массив для структуры pollfd
    52.     struct pollfd fds[MAX_CLIENTS + 1];
    53.     int nfds = 1; // Количество отслеживаемых дескрипторов
    54.     fds[0].fd = server_fd;
    55.     fds[0].events = POLLIN; // Ожидаем события чтения
    56.  
    57.     while (1) {
    58.         // Ожидаем событий
    59.         int poll_count = poll(fds, nfds, -1);
    60.         if (poll_count < 0) {
    61.             perror("poll");
    62.             exit(EXIT_FAILURE);
    63.         }
    64.  
    65.         // Обрабатываем события
    66.         for (int i = 0; i < nfds; i++) {
    67.             if (fds[i].revents & POLLIN) {
    68.                 if (fds[i].fd == server_fd) {
    69.                     // Обработка входящего соединения
    70.                     if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0) {
    71.                         perror("accept");
    72.                         exit(EXIT_FAILURE);
    73.                     }
    74.                     printf("Подключен клиент: %s:%d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
    75.  
    76.                     // Регистрируем новый клиентский сокет
    77.                     fds[nfds].fd = client_fd;
    78.                     fds[nfds].events = POLLIN;
    79.                     nfds++;
    80.                 } else {
    81.                     // Чтение данных от клиента
    82.                     int bytes_read = read(fds[i].fd, buffer, sizeof(buffer) - 1);
    83.                     if (bytes_read > 0) {
    84.                         buffer[bytes_read] = '\0'; // Завершаем строку
    85.                         printf("Получено от клиента: %s\n", buffer);
    86.                     } else {
    87.                         // Клиент закрыл соединение
    88.                         printf("Клиент отключился.\n");
    89.                         close(fds[i].fd);
    90.                         fds[i] = fds[nfds - 1]; // Перемещаем последний элемент на место отключенного
    91.                         nfds--;
    92.                     }
    93.                 }
    94.             }
    95.         }
    96.  
    97.         // Чтение данных из стандартного ввода
    98.         if (fds[0].revents & POLLIN) {
    99.             if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
    100.                 printf("Вы ввели: %s", buffer);
    101.             }
    102.         }
    103.     }
    104.  
    105.     close(server_fd);
    106.     return 0;
    107. }
    108. ```
    109.  
    110. ### Объяснение кода:
    111.  
    112. 1. **Создание сокета**: Мы создаем TCP-сокет и связываем его с локальным адресом и портом 12345.
    113.  
    114. 2. **Настройка сокета**: Устанавливаем опцию `SO_REUSEADDR`, чтобы избежать ошибок при повторном запуске сервера.
    115.  
    116. 3. **Прослушивание**: Запускаем сервер в режиме прослушивания.
    117.  
    118. 4. **Создание массива `pollfd`**: Мы создаем массив, чтобы отслеживать события для серверного сокета и клиентских сокетов.
    119.  
    120. 5. **Основной цикл**: В бесконечном цикле мы ожидаем событий с помощью `poll`:
    121.    - Если событие происходит на серверном сокете, мы принимаем новое соединение и добавляем его в массив `pollfd`.
    122.    -
    123.  
    124.  
    я не пойму как организовать чтение\запись на asm :dntknw:

    или вот...
    Код (Text):
    1.  
    2. Для выполнения опроса сокета с использованием функции `poll`, а также чтения и записи в стандартный ввод/вывод на ассемблере NASM для x64 Linux, вам нужно создать TCP-сервер, который будет обрабатывать входящие соединения и данные. Вот пример программы, которая демонстрирует, как это сделать.
    3.  
    4. ### Пример кода на NASM
    5.  
    6. ```nasm
    7. section .data
    8.     PORT        db 12345
    9.     msg         db 'Вы ввели: ', 0
    10.     msg_len     equ $ - msg
    11.     buffer      rb 256
    12.  
    13. section .bss
    14.     fds         resb 8             ; массив для структуры pollfd (2 поля по 4 байта)
    15.     addr        resb 16            ; адрес для сокета
    16.     client_fd   resd 1            
    17.  
    18. section .text
    19.     global _start
    20.  
    21. _start:
    22.     ; Создание сокета
    23.     mov rax, 41                   ; syscall: socket
    24.     mov rdi, 2                    ; AF_INET
    25.     mov rsi, 1                    ; SOCK_STREAM
    26.     xor rdx, rdx                  ; protocol = 0
    27.     syscall
    28.     mov rdi, rax                  ; сохранить сокет в rdi
    29.  
    30.     ; Привязка сокета
    31.     mov rax, 49                   ; syscall: bind
    32.     mov rsi, addr                 ; указатель на адрес
    33.     mov dword [rsi], 2           ; sin_family = AF_INET
    34.     mov word [rsi + 2], PORT      ; sin_port = htons(12345)
    35.     mov dword [rsi + 4], 0        ; sin_addr = INADDR_ANY
    36.     mov rdx, 16                   ; размер структуры sockaddr
    37.     syscall
    38.  
    39.     ; Начало прослушивания
    40.     mov rax, 50                   ; syscall: listen
    41.     mov rsi, 1                    ; backlog
    42.     syscall
    43.  
    44.     ; Настройка структуры pollfd
    45.     mov rax, fds                  ; адрес массива fds
    46.     mov dword [rax], rdi          ; fds[0].fd = сокет
    47.     mov dword [rax + 4], 1        ; fds[0].events = POLLIN
    48.  
    49. .loop:
    50.     ; Ожидание событий
    51.     mov rax, 0                    ; syscall: poll
    52.     mov rdi, fds                  ; указатель на fds
    53.     mov rsi, 1                    ; количество отслеживаемых дескрипторов
    54.     mov rdx, -1                   ; бесконечное ожидание
    55.     syscall
    56.  
    57.     ; Проверка, есть ли входящие соединения
    58.     mov rax, fds
    59.     mov eax, [rax + 4]            ; fds[0].revents
    60.     test eax, 1                   ; проверяем POLLIN
    61.     jz .loop                      ; если нет, продолжаем опрос
    62.  
    63.     ; Принятие входящего соединения
    64.     mov rax, 43                   ; syscall: accept
    65.     mov rsi, addr                 ; указатель на адрес
    66.     mov rdx, addr                 ; размер адреса
    67.     syscall
    68.     mov [client_fd], rax          ; сохранить дескриптор клиента
    69.  
    70.     ; Чтение данных от клиента
    71.     mov rax, 0                    ; syscall: read
    72.     mov rdi, [client_fd]          ; fd = клиентский сокет
    73.     mov rsi, buffer               ; указатель на буфер
    74.     mov rdx, 256                  ; количество байт для чтения
    75.     syscall
    76.  
    77.     ; Запись данных в стандартный вывод
    78.     mov rax, 1                    ; syscall: write
    79.     mov rdi, 1                    ; fd = stdout
    80.     mov rsi, msg                  ; указатель на сообщение
    81.     mov rdx, msg_len              ; длина сообщения
    82.     syscall
    83.  
    84.     ; Запись прочитанных данных в стандартный вывод
    85.     mov rax, 1                    ; syscall: write
    86.     mov rdi, 1                    ; fd = stdout
    87.     mov rsi, buffer               ; указатель на буфер
    88.     mov rdx, rax                  ; количество байт, прочитанных из сокета
    89.     syscall
    90.  
    91.     ; Закрытие клиентского сокета
    92.     mov rax, 3                    ; syscall: close
    93.     mov rdi, [client_fd]          ; fd = клиентский сокет
    94.     syscall
    95.  
    96.     jmp .loop                     ; повторяем
    97.  
    98. section .bss
    99. ```
    100.  
    101. ### Объяснение кода:
    102.  
    103. 1. **Создание сокета**: Мы создаем TCP-сокет с помощью системного вызова `socket`.
    104.  
    105. 2. **Привязка сокета**: Привязываем сокет к адресу и порту
    106.  
    107.  
     
    Последнее редактирование: 1 июл 2025
  17. Hacker

    Hacker Member

    Публикаций:
    0
    Регистрация:
    9 авг 2018
    Сообщения:
    180
    GigaChat:

    Вот пример кода на NASM x64 для Linux, демонстрирующий использование функции poll для опроса состояния сокета, а также чтение и запись в стандартный ввод-вывод (stdin, stdout):
    Код (Text):
    1.  
    2. section .data
    3.     prompt db 'Enter something:', 0xA ; приглашение к вводу
    4.     len_prompt equ $-prompt
    5.  
    6.     socket_address db '/tmp/my_socket', 0 ; адрес сокета
    7.  
    8. section .bss
    9.     buffer resb 1024           ; буфер для чтения/записи
    10.     server_socket resq 1       ; дескриптор сервера
    11.     client_socket resq 1       ; дескриптор клиента
    12.     poll_fd resq 1             ; структура pollfd
    13.  
    14. section .text
    15. global _start
    16.  
    17. _start:
    18.     ; создаем UNIX-доменный сокет
    19.     mov rax, SYS_SOCKET      ; syscall номер 41
    20.     mov rdi, AF_UNIX         ; семейство адресов (AF_UNIX)
    21.     mov rsi, SOCK_STREAM     ; тип сокета (SOCK_STREAM)
    22.     xor rdx, rdx             ; протокол (0 = default)
    23.     syscall                  ; создать сокет
    24.     cmp rax, 0               ; проверка на ошибку
    25.     jl error                 ; ошибка создания сокета
    26.     mov [server_socket], rax ; сохранить файловый дескриптор серверного сокета
    27.  
    28.     ; привязываем сокет к адресу
    29.     mov rax, SYS_BIND        ; syscall номер 49
    30.     mov rdi, [server_socket]
    31.     lea rsi, [socket_address]
    32.     mov rdx, sizeof_struct_sockaddr_un
    33.     syscall
    34.     cmp rax, 0
    35.     jl error
    36.  
    37.     ; слушаем подключения
    38.     mov rax, SYS_LISTEN      ; syscall номер 50
    39.     mov rdi, [server_socket]
    40.     mov rsi, 1               ; максимальное количество подключений в очереди
    41.     syscall
    42.     cmp rax, 0
    43.     jl error
    44.  
    45.     ; ждем подключение
    46.     mov rax, SYS_ACCEPT      ; syscall номер 43
    47.     mov rdi, [server_socket]
    48.     xor rsi, rsi             ; sockaddr struct
    49.     xor rdx, rdx             ; размер sockaddr struct
    50.     syscall
    51.     cmp rax, 0
    52.     jl error
    53.     mov [client_socket], rax ; сохраняем клиентский сокет
    54.  
    55.     ; настройка структуры pollfd
    56.     mov qword [poll_fd + PF_FD], [client_socket]
    57.     mov dword [poll_fd + PF_EVENTS], POLLIN | POLLOUT
    58.  
    59. loop_poll:
    60.     ; вызов poll
    61.     mov rax, SYS_POLL        ; syscall номер 7
    62.     lea rdi, [poll_fd]      ; указатель на структуру pollfd
    63.     mov rsi, 1              ; количество структур
    64.     mov rdx, -1             ; таймаут (-1 значит ждать бесконечно)
    65.     syscall
    66.     cmp rax, 0
    67.     je loop_poll            ; повторяем цикл, пока нет активности
    68.  
    69.     test byte [poll_fd + PF_REVENTS], POLLIN
    70.     jz no_input_available   ; пропускаем чтение, если нет входных данных
    71.  
    72.     ; читаем данные из сокета
    73.     mov rax, SYS_READ        ; syscall номер 0
    74.     mov rdi, [client_socket]
    75.     lea rsi, [buffer]
    76.     mov rdx, 1024
    77.     syscall
    78.     cmp rax, 0
    79.     jle exit                ; выход, если ничего не прочитано или произошла ошибка
    80.  
    81.     ; выводим сообщение в stdout
    82.     mov rax, SYS_WRITE       ; syscall номер 1
    83.     mov rdi, 1              ; fd=1 (stdout)
    84.     lea rsi, [buffer]
    85.     mov rdx, rax            ; длина считанных байтов
    86.     syscall
    87.  
    88. no_input_available:
    89.     ; проверяем наличие возможности записи
    90.     test byte [poll_fd + PF_REVENTS], POLLOUT
    91.     jz continue_loop        ; продолжаем цикл, если нельзя писать
    92.  
    93.     ; отправляем приглашения к вводу
    94.     mov rax, SYS_WRITE       ; syscall номер 1
    95.     mov rdi, 1              ; fd=1 (stdout)
    96.     lea rsi, [prompt]
    97.     mov rdx, len_prompt
    98.     syscall
    99.  
    100. continue_loop:
    101.     jmp loop_poll
    102.  
    103. exit:
    104.     ; закрываем сокеты
    105.     mov rax, SYS_CLOSE       ; syscall номер 3
    106.     mov rdi, [client_socket]
    107.     syscall
    108.     mov rax, SYS_CLOSE
    109.     mov rdi, [server_socket]
    110.     syscall
    111.  
    112.     ; завершаем программу
    113.     mov rax, SYS_EXIT        ; syscall номер 60
    114.     xor rdi, rdi             ; код выхода 0
    115.     syscall
    116.  
    117. error:
    118.     ; обработка ошибок
    119.     mov rax, SYS_WRITE       ; syscall номер 1
    120.     mov rdi, 2              ; fd=2 (stderr)
    121.     lea rsi, [err_msg]
    122.     mov rdx, err_len
    123.     syscall
    124.     jmp exit
    125.  
    126. section .rodata
    127.     err_msg db 'Error occurred.', 0xA
    128.     err_len equ $-err_msg
    129.  
    130. %de
    131. ...
    132.  
     
  18. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    262
    Скоро можно будет отмечать юбилей как пишется сервер?
     
  19. Hacker

    Hacker Member

    Публикаций:
    0
    Регистрация:
    9 авг 2018
    Сообщения:
    180
    Там наверное нужно ещё будет делать исключении серверных команд, что бы telnet клиент безопасно работал