Клиент-сервер под linux на c++ общение клиентов «все со всеми» с использованием потоков
Начну с того, что была предложена работа на должность программиста с\с++. Задание это название темы.
Полез в интернет, кругом все напичкано чатами и общением по типу клиент-сервер, но увы кода с подобным заданием я так и не нашел. Был примитив типа ЭХО клиент-сервера, который я и решил взять за основу:
Это у нас клиент:
это код сервера:
После всего этого в клиенте нужно отправить сообщение серверу используя функции send или write а на стороне сервера принять сообщение и переотправить его обратно клиенту используя функции read и send.
Вообще есть разные функции отправки и приема, к примеру send и recv вместе с сообщением шлют еще и флаг подтверждения, а функции read и write не требуют подтверждения, то есть сообщение может потерять байты при отправке и это не будет зафиксировано.
Так как сокеты это дуплекс и создавая связь между клиентом и сервером мы не можем писать туда сообщения из других подключенных сокетов, необходимо создать массив со всеми активными сокетами подключенными к серверу. И еще одно замечание очень важное:
Для общения между несколькими сокетами необходимо использовать функцию select, которая выбирает сокет из списка и отсылает ему сообщение, и так далее, пока не закончатся все записанные сокеты
После этого в массив сокетов будет записано правильное значение подключаемого сокета а далее остается лишь перебирать их при рассылке сообщений:
Запишем все это в функцию и создадим отдельный поток:
Что касаемо клиента, то необходимо создать два разных потока для чтения и записи в сокет:
Теперь все работает. Спасибо за снимание. Надеюсь что это пригодится тем, кто так же как и я пытался написать клиент-сервер, но не смог найти нужную информацию в сети.
Источник
Сокеты в ОС Linux
В данной статье будет рассмотрено понятие сокета в операционной системе Linux: основные структуры данных, как они работают и можно ли управлять состоянием сокета с помощью приложения. В качестве практики будут рассмотрены инструменты netcat и socat.
Что такое сокет?
Сокет — это абстракция сетевого взаимодействия в операционной системе Linux. Каждому сокету соответствует пара IP-адрес + номер порта. Это стандартное определение, к которому привыкли все, спасибо вики. Хотя нет, вот здесь лучше описано. Поскольку сокет является только лишь абстракцией, то связка IP-адрес + номер порта — это уже имплементация в ОС. Верное название этой имплементации — «Интернет сокет». Абстракция используется для того, чтобы операционная система могла работать с любым типом канала передачи данных. Именно поэтому в ОС Linux Интернет сокет — это дескриптор, с которым система работает как с файлом. Типов сокетов, конечно же, намного больше. В ядре ОС Linux сокеты представлены тремя основными структурами:
struct socket — представление сокета BSD, того вида сокета, который стал основой для современных «Интернет сокетов»;
struct sock — собственная оболочка, которая в Linux называется «INET socket»;
struct sk_buff — «хранилище» данных, которые передает или получает сокет;
Как видно по исходным кодам, все структуры достаточно объемны. Работа с ними возможна при использовании языка программирования или специальных оберток и написания приложения. Для эффективного управления этими структурами нужно знать, какие типы операций над сокетами существуют и когда их применять. Для сокетов существует набор стандартных действий:
socket — создание сокета;
bind — действие используется на стороне сервера. В стандартных терминах — это открытие порта на прослушивание, используя указанный интерфейс;
listen — используется для перевода сокета в прослушивающее состояние. Применяется к серверному сокету;
connect — используется для инициализации соединения;
accept — используется сервером, создает новое соединение для клиента;
send/recv — используется для работы с отправкой/приемом данных;
close — разрыв соединения, уничтожение сокета.
Если о структурах, которые описаны выше, заботится ядро операционной системы, то в случае команд по управлению соединением ответственность берет на себя приложение, которое хочет пересылать данные по сети. Попробуем использовать знания о сокетах для работы с приложениями netcat и socat.
netcat
Оригинальная утилита появилась 25 лет назад, больше не поддерживается. На cегодняшний день существуют порты, которые поддерживаются различными дистрибутивами: Debian, Ubuntu, FreeBSD, MacOS. В операционной системе утилиту можно вызвать с помощью команды nc, nc.traditional или ncat в зависимости от ОС. Утилита позволяет «из коробки» работать с сокетами, которые используют в качестве транспорта TCP и UDP протоколы. Примеры сценариев использования, которые, по мнению автора, наиболее интересны:
перенаправление входящих/исходящих запросов;
трансляция данных на экран в шестнадцатеричном формате.
Опробуем операции в действии. Задача будет состоять в том, что необходимо отправить TCP данные через netcat в UDP соединение. Для лабораторной будет использоваться следующая топология сети:
Введем команду на открытие порта на машине Destination: nc -ulvvp 7878
Настроим машину Repeater. Так как передача из одного интерфейса этой машины будет происходить по протоколу TCP, а на другой интерфейс будет осуществляться передача по протоколу UDP, то для таких действий необходимо сделать соединитель, который сможет накапливать данные и пересылать их между открытыми портами. На такую роль отлично подходит FIFO файл. Поэтому команда для запуска будет выглядеть так: sudo mkfifo /tmp/repeater #создать FIFO файл
sudo nc -l -p 4545 > /tmp/repeater | nc -u 10.0.3.5 7878 IP адрес 10.0.3.5 — адрес машины Destination. Символы «|» и «> nc 10.0.2.4 4545
В итоге получаем возможность читать данные от машины Source:
В машине Destination:
Пример с трансляцией данных в шестнадцатеричном формате можно провести так же, но заменить команду на Destination или добавить еще один пайп на Repeater:
nc -l -p 4545 -o file
В результате будет создан файл, в котором можно будет обнаружить передаваемые данные в шестнадцатеричном формате:
Как видно из тестового сценария использования, netcat не дает контролировать практически ничего, кроме направления данных. Нет ни разграничения доступа к ресурсам, которые пересылаются, ни возможности без дополнительных ухищрений работать с двумя сокетами, ни возможности контролировать действия сокета. Протестируем socat.
socat
Инструмент, который до сих пор поддерживается и имеет весьма обширный функционал по склейке каналов для взаимодействия. Разработчиками инструмент именуется как netcat++. Ниже приведем небольшой список того что можно перенаправить через socat:
STDIO -> TCP Socket;
FILE -> TCP Socket;
TCP Socket -> Custom Application;
UDP Socket -> Custom Application;
Для повседневного использования достаточно опций, но если понадобится когда-то работать напрямую с серийным портом или виртуальным терминалом, то socat тоже умеет это делать. Полный перечень опций можно вызвать с помощью команды:
Помимо редиректов socat также можно использовать как универсальный сервер для расшаривания ресурсов, через него можно как через chroot ограничивать привилегии и доступ к директориям системы.
Чтобы комфортно пользоваться этим инструментом, нужно запомнить шаблон командной строки, который ожидает socat:
socat additionalOptions addr1 addr2
additionalOptions — опции, которые могут добавлять возможности логирования информации, управления направлением передачи данных;
addr1 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;
addr2 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;
Попробуем провести трансляцию данных из сокета в сокет. Будем использовать для этого 1 машину. Перед началом эксперимента стоит отметить, что особенностью socat является то, что для его корректной работы нужно обязательно писать 2 адреса. Причем адрес не обязательно должен быть адресом, это может быть и приложение, и стандартный вывод на экран.
Например, чтобы использовать socat как netcat в качестве TCP сервера, можно запустить вот такую команду:
socat TCP-LISTEN:4545, STDOUT
Для коннекта можно использовать netcat:
nc localhost 4545
При таком использовании, socat дает возможность пересылать сообщения в обе стороны, но если добавить флаг «-u», то общение будет только от клиента к серверу. Все серверные сообшения пересылаться не будут:
Настроим более тонко наш сервер, добавив новые опции через запятую после используемого действия:
socat TCP-LISTEN:4545,reuseaddr,keepalive,fork STDOUT
Дополнительные параметры распространяются на те действия, которые socat может выполнять по отношению к адресу. Полный список опций можно найти здесь в разделе «SOCKET option group».
Таким образом socat дает практически полный контроль над состоянием сокетов и расшариваемых ресурсов.
Статья написана в преддверии старта курса Network engineer. Basic. Всех, кто желает подробнее узнать о курсе и карьерных перспективах, приглашаем записаться на день открытых дверей, который пройдет уже 4 февраля.
Источник
Клиент-Сервер Socket linux (Система обмена сообщениями)
Здравствуйте! Помогите,пожалуйста,доработать Клиент-Сервер.Никак не получается. Завтра последний день сдачи Условия выполнения: «Задание: разработать приложение-клиент и приложение сервер, обеспечивающие функции мгновенного обмена сообщений между пользователями.
Основные возможности. Серверное приложение должно реализовывать следующие функции: 1) Прослушивание определенного порта 2) Обработка запросов на подключение по этому порту от клиентов 3) Поддержка одновременной работы нескольких клиентов через механизм нитей 4) Передача текстового сообщения одному клиенту 5) Передача текстового сообщения всем клиентам 6) Прием и ретрансляция входящих сообщений от клиентов 7) Обработка запроса на отключение клиента 8) Принудительное отключение указанного клиента
Клиентское приложение должно реализовывать следующие функции: 1) Установление соединения с сервером 2) Передача сообщения всем клиентам 3) Передача сообщения указанному клиенту 4) Прием сообщения от сервера с последующей индикацией 5) Разрыв соединения 6) Обработка ситуации отключения клиента сервером
Настройки приложений. Разработанное клиентское приложение должно предоставлять пользователю настройку IP-адреса или доменного имени сервера сообщений и номера порта сервера. »
Пока в работе у меня выполнено следующие условия:Сервер:1,2,3 ;Клиент:1,. В данном случае у меня клиент отправляет сообщение серверу,а сервер отправляет этоже сообщение обратно клиенту.
Деревянные треды лора: помогите решить ДЗ
3) Поддержка одновременной работы нескольких клиентов через механизм нитей
Это делается через epoll, тащемта. Вот как-то так, наверно
Вот ещё пример, как работать со многими клиентами:
man select && man poll && man kqueue (через последнюю функцию сделан event-base в iolib)
fork() и указанные в ТЗ нити — мягко говоря разные вещи
и перед тем как начать списывать код из источников стоит набросать на бумажке протокол: какие и как данные ходят туда-сюда, форматы сообщений, состояния соединений.
fork() и указанные в ТЗ нити — мягко говоря разные вещи
А разве и то и другое не через clone() выполняется?
Еще вопросик:
Как сделать так,чтобы клиент мог скачать с сервера песню?Например формата .mp3
А у тебя по этому поводу свои мысли есть какие-нибудь? Мне просто интересно, есть ли у тебя мозг впринципе.
есть куча протоколов прикладного уровня. самое простое читать в стрим файл и send()/recv()
Возможно как то так: На сервере как то так: FILE * file; file = fopen(«test.mp3»,«wt»); после делаю send. Далее на клиента пишу recv . а вот дальше не знаю,куда записать эту песню и т.д.
Re: Еще вопросик:
Лучше воспользоваться более высокоуровневым протоколом. Хоть http.
Глянь сюда, например:
Далее на клиента пишу recv . а вот дальше не знаю,куда записать эту песню и т.д.
Также открываешь файл на запись и пишешь.
К сожалению по заданию нельзя
не могли бы написать пример в коде?
Нет, не хочу тратить время. Читай man’ы.
Ты делаешь recv из сокета в буфер, а потом write (man 2 write) из буфера в файл
писал прям тут, думаю суть понятна
есть опечатки сразу сам вижу.. но чет редактирование поста не доступно
Пару опечаток исправил,но не компилируется. gcc пишет:expected declaration of statement at and of intut.
Код: FILE* f = fopen(«music.mp3»,«rb»);
fseek (f , 0 , SEEK_END); int fsize = ftell (f); rewind (f);
long sended = 0; long readed = 0;
>while(sended ( 19.01.14 18:03:55 )
Пару опечаток исправил,но не компилируется. gcc пишет:expected declaration of statement at and of intut.
jo_b1ack,Спасибо тебе большое. Теперь дошло как это работает.
Осталось разбираться с системой обмена сообщений.
Мог ли бы еще помочь в одной проблемке?
У меня все передаётся,но после сервер и клиент просто висят.
Если я убираю while то передается только 4кб и идет дальше по коду т.е. не висит .
Как это исправить?
попробуй заменить в приеме строчку
на while(rcv_len > 0);
возможно после закрытия сокета recv возвращает не 0 — закрытие соединения, а SOCKET_ERROR
Пробовал,все также. В коде пробовал задать время на while ,но не очень успешно,закоментил .
у тебя в сервере 2 бесконечных цикла, и close(sock); после них.. ясное дело оно висит. бесконечные циклы это вообще зло.
К сожалению ,все равно висит. Убрал все while того где идет передача файла в сервере и клиенте
у тебя цикл начинается прям перед accept. поставь close(sock) после fclose(f); вприеме файла на сервере
Все заработало,в сервере написал так: >while(sended != fsize);
Убиваем ваш сервер. Нужно на сервере проверку ограничение на количество соединений поставить.
да там куча и маленькая тележка косяков.. в этом коде
Здравствуйте! Тоже начал писать сервер и клиент на чистом С, вы могли бы указать косяки, желательно куски проблемного кода, очень интересно. И немного оффтоп вопрос, в двух словах, какие есть пути импрува сервера/клиента ? Какие фичи еще допилить можно ? Пока есть идея сделать простой клиент/сервер и сниффер, который будет парсить передаваемые пакеты, также есть мысль добавить шифрование траффика и потом попытаться заточить под это свой сниффер. Естественно я нацелен писать велосипеды, ради изучения как языка С, так и сетевого программирования.
пиши и выкладывай на форум) думаю, обосрать тут все горазды что угодно 😉
Доброго времени суток. Подскажите, вы реализовали все задачи, которые перед вами стояли в этой теме? Не могли бы поделиться данной системой передачи сообщений? Спасибо.
Что, уже сессия началась?
Еще нет, а вот курсовые уже нужно сдавать. Может кто-нибудь сможет помочь или подтолкнуть в нужное русло. Вообщем задача поставлена так: «Сетевой чат (TCP)fork()»
Столкнулся вот с такой проблемой: Отправка, прием, обратная отправка сообщений — все реализовано. Для каждого клиента создается новый процесс и создается массив сокетов подключенных к серверу, который обрабатывается в этом процессе и отправляет данные всем имеющимся в массиве клиентам. При еще одном подключении, опять создается новый процесс, обновяется массив сокетов и т.д. Но у первого клиента массив сокетов не обновляется по понятным причинам. т.е. он работает со старой версией массива сокетов и не видит новых клиентов, которые подключились после него. Допустим подключено 3 клиента. Если сообщение отправит первый клиент, то сервер отправит сообщение только ему. т.к. у него в массиве хранится только он сам. Если отправит второй клиент сообщение, то сервер отправит обратно уже первому и второму клиенту. Если отправит третий клиент сообщение, то сервер обработает отправку и в первый и во второй и в третий клиент, т.к. он содержит в массиве все подключенные клиенты. Пробовал записывать в файл всех новых клиентов, и при обработке запроса, он считывает с файла сокеты и соответственно отправляет всем. Но тоже ничего не выходит. Считывать — считывает, но сообщения не доходят до клиента.
Есть решение этой проблемы? Спасибо.
А клиенты должны именно через выданный сокет работать? Т.е. все на одной машине? Как конкретно звучит задачка?
В твоей формулировке можно сделать так:
Завести shared memory и в ней хранить массив сокетов. Естественно размер массива задан статически. И стоит серелиазовать доступ к массиву (например мютексом).
Или использовать PF_UNIX сокеты и хранить их в определенном месте на диске.
В задаче ничего про сокеты не сказано. А сформулирована задача следующим образом. Реализовать клиент-сервер программу для передачи сообщений. Использовать TCP протокол и fork().
Т.е. реализовать чат один ко многим через TCP и fork. Будет огромным плюсом, если добавить приватный чат (один к одному)
А разве есть большая разница, если не на одной машине? Если на разных машинах клиент и сервер, то ведь клиенту всего-лишь нужно указать адрес и порт куда подключаться. Или я ошибаюсь и есть большая разница? В любом случае демонстрировать я буду на одной машине, но интересно, неужели есть большая разница.
Спасибо за подсказку. Надо попробовать с разделяемой памятью. А почему не работает обычное сохранение сокетов в файл? Ведь файл все время обновляется, сервер имеет к нему доступ, там хранятся все подключенные клиенты. Но вот сообщение не отправляет на клиенты, которые были подключены после клиента, который отправляет сообщение.
Я тебе неправильно сказал, насчет шаред мемори. Будет точно такая же проблема, т.к. это файловый дескриптор. При форке наследуются открытые дескрипторы, но дескриптор следующего наследника уже будет не валиден.
Надо каждому процессу выдать по уникальному порту, который потом он будет слушать. При этом сохранить все используемые порты в определенный файлик. Соотвественно, после форка каждый процесс открывает сокет. И по команде будет подключаться и слать сообщения другим клентам.
Как я понимаю, у каждого процесса есть свой порт, его и надо использовать. Он узнается с помощью getpid(); После вызова форка, я записываю порт в файл, вызывается функция где должно рассылаться наше сообщение. Там я считываю все порты, а что с ними делать дальше? Как прикрутить к рассылке. Я отправляю функцией sendto, где в параметрах указывается (номер сокета,сообщение, размер сообщения и т.д.) Вот раньше я сохранял номера сокетов и спокойно отправлял, но с известной проблемой. А что делать с портом? Как его прикрутить к отправке сообщений? Спасибо.
Вот класический пример tcp клиент/сервера
Тут два разных процесса идет: создание серверного сокета для приема соединений. И создание сокета, для соединения с сервером. В твоем случае можно основной процесс сделать в виде сервера, а дочерние — подключенными к нему клиентами.
Запись и чтение можно делать при помощи read/write.
getpid возвращает пид процесса, с номером порта не связанно.
Источник