клиент серверное приложение c linux
C: сокеты и пример модели client-server
Перевод с дополнениями. Оригинал – тут>>>.
Как правило – два процесса общаются друг с другом с помощью одного из Inter Process Communication (IPC) механизма ядра, таких как:
Кроме перечисленных IPC – в ядре присутствует много других возможностей, но что если процессам необходимо обмениваться данными по сети?
Тут используется ещё один механизм IPC – сокеты.
Что такое сокет?
Сокеты (англ. socket — разъём) — название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения.
Кратко говоря – существует два типа сокетов – UNIX-сокеты (или сокеты домена UNIX – Unix domain sockets) и INET-сокеты (IP-сокеты, network sockets).
UNIX-сокеты чвляются частью механизма IPC и позволяют обмен данными в обоих направлениях между процессами, работающими на одной машине.
INET-сокеты в свою очередь представляют собой механизм, позволяющий выполнять коммуникацию между процессами по сети.
Грубо говоря – если UNIX-сокет использует файл в файловой системе, то INET-сокет – требует присваивания сетевого адреса и порта.
Коммуникация в среде TCP/IP происходит по клиент-серверной модели, т.е. – клиент инициализирует связь, а сервер его принимает.
Ниже – пример сервера, который будет работать как демон и ожидать подключения клиента, а при инициализации клиентом соединения – передаст ему дату и время.
Socket сервер
Наш сервер будет выглядеть следующим образом:
Теперь – давайте рассмотрим сам код сервера.
Далее – вызывается функция bind() :
Socket клиент
Перейдём ко второй программе – клиенту.
Код её будет выглядеть следующим образом:
Кратко рассмотрим его:
И в конце-концов – клиент с помощью read() получает данные из своего сокета, в который поступают данные от сокета на сервере.
Собираем клиент, и пробуем подключиться к нашему серверу:
Кроссплатформенный многопоточный TCP/IP сервер на C++
Как-то раз встала задача по написанию простого и быстрого многопоточного TCP/IP сервера на C++ и при этом, чтобы работал на платформах Windows и Linux без требования как-либо изменять код за пределами класса самого сервера. Ранее, на чистом C++ без библиотек вроде Qt, Tcp-сервер не писал, и предвещал себе долгое время мучений с платформо-зависимостью. Но как оказалось всё гораздо проще чем казалось на первый взгляд, ведь в основном интерфейсы сокетов обоих систем похожи как две капли воды и различаются лишь в мелких деталях.
Итак класс сервера и клиента выглядит следующим образом:
TcpServer.h
Как можно заметить различия минимальны помимо разных подключаемых заголовочных файлов различаются разве что тип сокета — SOCKET для Windows и (как бы странно это не выглядело) int для Linux. Разница здесь лишь в том что Linux использует стандартный int для хранения данных сокета, в то время как в Windows задекларирован собственный тип который относительно архитектуры принимает разный размер и значность целочисленного типа, что можно увидеть в оригинальных заголовочных файлах:
Так же в Windows части TcpServer-хедера присутствует структура для обозначения используемой версии WinSocket — WSAData w_data; (см. WSAData)
Перейдём к реализации сервера:
TcpServer.cpp
Реализация для Linux и Windows практически идентична за исключением некотрых мест, обусловленных разве что различными структурами хранящим адреса( struct sockaddr_in/SOCKADDR_IN, struct sockaddr/SOCKADDR ) и сокеты( int/SOCKET ), а так же наличием у Windows объекта версии WinSocket( WSAData ).
main.cpp
UPDATE: Реализация многопоточного TCP-сервера представленная в данной статье имеет такой недостаток как «потокове голодание» при слишком большом колличестве подключённых к серверу клиентов, по скольку для обработки каждого клиента создаётся отдельный поток, который находится в состоянии активного ожидания данных от клиента. Для нивелирования данного недостатка было принято решение изменить модель организации многопоточности приложения. Новая модель представлена на следующей схеме:
Как можно заметить, при данной модели организации многопоточности имеется всего один поток-обходчик, который проходит все клиенты и проверяет наличие пришедших от них данных. При условии найденых данных поток-обходчик переходит в состояние обработки данных клиента предварительно создав новый поток-обходчик, который идёт дальше. После обработки данных поток-обработчик переносит объект клиента в конец очереди ожидания и самоуничтожается.
Реализацию с применением данного исправления можно увидеть в данном репозитории GitHub. Данный сервер рассчитан только для отправки «сырых» данных по протоколу TCP, поскольку отправляет вместе с данными заголовок с их размером в байтах, так что для реализации HTTP-сервера данная реализация не подходит. Реализованно это именно так по причине того что при работе с сокетами не в сыром режиме чтение размера из TCP-заголовка не возможно.
Клиент-сервер под linux на c++ общение клиентов «все со всеми» с использованием потоков
Начну с того, что была предложена работа на должность программиста с\с++. Задание это название темы.
Полез в интернет, кругом все напичкано чатами и общением по типу клиент-сервер, но увы кода с подобным заданием я так и не нашел. Был примитив типа ЭХО клиент-сервера, который я и решил взять за основу:
Это у нас клиент:
После всего этого в клиенте нужно отправить сообщение серверу используя функции send или write а на стороне сервера принять сообщение и переотправить его обратно клиенту используя функции read и send.
Вообще есть разные функции отправки и приема, к примеру send и recv вместе с сообщением шлют еще и флаг подтверждения, а функции read и write не требуют подтверждения, то есть сообщение может потерять байты при отправке и это не будет зафиксировано.
Так как сокеты это дуплекс и создавая связь между клиентом и сервером мы не можем писать туда сообщения из других подключенных сокетов, необходимо создать массив со всеми активными сокетами подключенными к серверу. И еще одно замечание очень важное:
Для общения между несколькими сокетами необходимо использовать функцию select, которая выбирает сокет из списка и отсылает ему сообщение, и так далее, пока не закончатся все записанные сокеты
После этого в массив сокетов будет записано правильное значение подключаемого сокета а далее остается лишь перебирать их при рассылке сообщений:
Запишем все это в функцию и создадим отдельный поток:
Что касаемо клиента, то необходимо создать два разных потока для чтения и записи в сокет:
Теперь все работает. Спасибо за снимание. Надеюсь что это пригодится тем, кто так же как и я пытался написать клиент-сервер, но не смог найти нужную информацию в сети.
Клиент-Сервер 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)
и перед тем как начать списывать код из источников стоит набросать на бумажке протокол: какие и как данные ходят туда-сюда, форматы сообщений, состояния соединений.
А разве и то и другое не через clone() выполняется?
Еще вопросик:
А у тебя по этому поводу свои мысли есть какие-нибудь? Мне просто интересно, есть ли у тебя мозг впринципе.
есть куча протоколов прикладного уровня. самое простое читать в стрим файл и send()/recv()
Re: Еще вопросик:
Лучше воспользоваться более высокоуровневым протоколом. Хоть http.
Глянь сюда, например:
Также открываешь файл на запись и пишешь.
К сожалению по заданию нельзя
не могли бы написать пример в коде?
Нет, не хочу тратить время. Читай man’ы.
Ты делаешь recv из сокета в буфер, а потом write (man 2 write) из буфера в файл
писал прям тут, думаю суть понятна
есть опечатки сразу сам вижу.. но чет редактирование поста не доступно
Пару опечаток исправил,но не компилируется. gcc пишет:expected declaration of statement at and of intut.
Код: FILE* f = fopen(«music.mp3»,«rb»);
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(rcv_len > 0);
у тебя в сервере 2 бесконечных цикла, и close(sock); после них.. ясное дело оно висит. бесконечные циклы это вообще зло.
у тебя цикл начинается прям перед accept. поставь close(sock) после fclose(f); вприеме файла на сервере
Убиваем ваш сервер. Нужно на сервере проверку ограничение на количество соединений поставить.
да там куча и маленькая тележка косяков.. в этом коде
пиши и выкладывай на форум) думаю, обосрать тут все горазды что угодно 😉
Доброго времени суток. Подскажите, вы реализовали все задачи, которые перед вами стояли в этой теме? Не могли бы поделиться данной системой передачи сообщений? Спасибо.
Что, уже сессия началась?
Еще нет, а вот курсовые уже нужно сдавать. Может кто-нибудь сможет помочь или подтолкнуть в нужное русло. Вообщем задача поставлена так: «Сетевой чат (TCP)fork()»
Есть решение этой проблемы? Спасибо.
А клиенты должны именно через выданный сокет работать? Т.е. все на одной машине? Как конкретно звучит задачка?
В твоей формулировке можно сделать так:
Завести shared memory и в ней хранить массив сокетов. Естественно размер массива задан статически. И стоит серелиазовать доступ к массиву (например мютексом).
Или использовать PF_UNIX сокеты и хранить их в определенном месте на диске.
В задаче ничего про сокеты не сказано. А сформулирована задача следующим образом. Реализовать клиент-сервер программу для передачи сообщений. Использовать TCP протокол и fork().
Т.е. реализовать чат один ко многим через TCP и fork. Будет огромным плюсом, если добавить приватный чат (один к одному)
А разве есть большая разница, если не на одной машине? Если на разных машинах клиент и сервер, то ведь клиенту всего-лишь нужно указать адрес и порт куда подключаться. Или я ошибаюсь и есть большая разница? В любом случае демонстрировать я буду на одной машине, но интересно, неужели есть большая разница.
Спасибо за подсказку. Надо попробовать с разделяемой памятью. А почему не работает обычное сохранение сокетов в файл? Ведь файл все время обновляется, сервер имеет к нему доступ, там хранятся все подключенные клиенты. Но вот сообщение не отправляет на клиенты, которые были подключены после клиента, который отправляет сообщение.
Я тебе неправильно сказал, насчет шаред мемори. Будет точно такая же проблема, т.к. это файловый дескриптор. При форке наследуются открытые дескрипторы, но дескриптор следующего наследника уже будет не валиден.
Надо каждому процессу выдать по уникальному порту, который потом он будет слушать. При этом сохранить все используемые порты в определенный файлик. Соотвественно, после форка каждый процесс открывает сокет. И по команде будет подключаться и слать сообщения другим клентам.
Как я понимаю, у каждого процесса есть свой порт, его и надо использовать. Он узнается с помощью getpid(); После вызова форка, я записываю порт в файл, вызывается функция где должно рассылаться наше сообщение. Там я считываю все порты, а что с ними делать дальше? Как прикрутить к рассылке. Я отправляю функцией sendto, где в параметрах указывается (номер сокета,сообщение, размер сообщения и т.д.) Вот раньше я сохранял номера сокетов и спокойно отправлял, но с известной проблемой. А что делать с портом? Как его прикрутить к отправке сообщений? Спасибо.
Вот класический пример tcp клиент/сервера
Запись и чтение можно делать при помощи read/write.
getpid возвращает пид процесса, с номером порта не связанно.
Программирование сокетов в Linux
Автор: Александр Шаргин
Опубликовано: 16.05.2001
Исправлено: 04.02.2006
Версия текста: 1.1
Введение
Socket API был впервые реализован в операционной системе Berkley UNIX. Сейчас этот программный интерфейс доступен практически в любой модификации Unix, в том числе в Linux. Хотя все реализации чем-то отличаются друг от друга, основной набор функций в них совпадает. Изначально сокеты использовались в программах на C/C++, но в настоящее время средства для работы с ними предоставляют многие языки (Perl, Java и др.).
Сокеты предоставляют весьма мощный и гибкий механизм межпроцессного взаимодействия (IPC). Они могут использоваться для организации взаимодействия программ на одном компьютере, по локальной сети или через Internet, что позволяет вам создавать распределённые приложения различной сложности. Кроме того, с их помощью можно организовать взаимодействие с программами, работающими под управлением других операционных систем. Например, под Windows существует интерфейс Window Sockets, спроектированный на основе socket API. Ниже мы увидим, насколько легко можно адаптировать существующую Unix-программу для работы под Windows.
ПРИМЕЧАНИЕ Большая часть материала, изложенного в статье, применимо ко всему семейству ОС Unix. Тем не менее, все приводимые далее факты и демонстрационные программы проверялись только под Linux, поэтому название этой ОС и вынесено в заголовок статьи. |
Основы socket API
Понятие сокета
Атрибуты сокета
Тип сокета определяет способ передачи данных по сети. Чаще других применяются:
Наконец, последний атрибут определяет протокол, используемый для передачи данных. Как мы только что видели, часто протокол однозначно определяется по домену и типу сокета. В этом случае в качестве третьего параметра функции socket можно передать 0, что соответствует протоколу по умолчанию. Тем не менее, иногда (например, при работе с низкоуровневыми сокетами) требуется задать протокол явно. Числовые идентификаторы протоколов зависят от выбранного домена; их можно найти в документации.
Адреса
Зачем понадобилось заключать всего одно поле в структуру? Дело в том, что раньше in_addr представляла собой объединение (union), содержащее гораздо большее число полей. Сейчас, когда в ней осталось всего одно поле, она продолжает использоваться для обратной совместимости.
ПРИМЕЧАНИЕ На некоторых машинах (к PC это не относится) порядок хоста и сетевой порядок хранения байтов совпадают. Тем не менее, функции преобразования лучше применять и там, поскольку это улучшит переносимость программы. Это никак не скажется на производительности, так как препроцессор сам уберёт все «лишние» вызовы этих функций, оставив их только там, где преобразование действительно необходимо. |
Установка соединения (сервер)
Установка соединения (клиент)
Обмен данными
Функция send используется для отправки данных и имеет следующий прототип.
Закрытие сокета
Параметр how может принимать одно из следующих значений:
Обработка ошибок
Отладка программ
Для простоты я буду использовать в демонстрационных примерах интерфейс внутренней петли.
Эхо-клиент и эхо-сервер
Теперь, когда мы изучили основные функции для работы с сокетами, самое время посмотреть, как они используются на практике. Для этого я написал две небольшие демонстрационные программы. Эхо-клиент посылает сообщение «Hello there!» и выводит на экран ответ сервера. Его код приведён в листинге 1. Эхо-сервер читает всё, что передаёт ему клиент, а затем просто отправляет полученные данные обратно. Его код содержится в листинге 2.
Листинг 1. Эхо-клиент.
Листинг 2. Эхо-сервер.
Обмен датаграммами
Как уже говорилось, датаграммы используются в программах довольно редко. В большинстве случаев надёжность передачи критична для приложения, и вместо изобретения собственного надёжного протокола поверх UDP программисты предпочитают использовать TCP. Тем не менее, иногда датаграммы оказываются полезны. Например, их удобно использовать при транслировании звука или видео по сети в реальном времени, особенно при широковещательном транслировании.
Листинг 3. Программа sender.
Листинг 4. Программа receiver.
Использование низкоуровневых сокетов
Низкоуровневые сокеты открывают перед вами новые горизонты. Они предоставляют программисту полный контроль над содержимым пакетов, которые отправляются в путешествие по сети. С другой стороны, они сложнее в использовании и обладают плохой переносимостью. Вот почему использовать их следует только в случае необходимости. Например, без них не обойтись при разработке системных утилит типа ping и traceroute.
Рисунок 1
Низкоуровневые сокеты позволяют вам включать в буфер с данными заголовки некоторых протоколов. Например, вы можете включить в ваше сообщение TCP- или UDP-заголовок, предоставив системе сформировать для вас IP-заголовок, а можете вообще сформировать все заголовки самостоятельно. Разумеется, при этом вам придётся изучить работу соответствующих протоколов и строго соблюсти формат их заголовков, иначе программа работать не будет.
Чтобы проиллюстрировать всё это примером, я переписал программу sender из предыдущего раздела с использованием низкоуровневых UDP-сокетов. При этом мне пришлось вручную формировать UDP-заголовок отправляемого сообщения. Я выбрал для примера UDP, потому что у этого протокола заголовок выглядит совсем просто (рисунок 2).
Рисунок 2
Листинг 5. Программа sender с использованием низкоуровневых сокетов.
Функции для работы с адресами и DNS
В этом разделе мы обсудим несколько функций, без которых можно написать учебный пример, но без которых вряд ли обойдётся реальная программа. Поскольку для идентификации хостов в Internet широко используются доменные имена, мы должны изучить механизм преобразования их в IP-адреса. Кроме того мы изучим несколько удобных вспомогательных функций.
Эта функция получает имя хоста и возвращает указатель на структуру с его описанием. Рассмотрим эту структуру более подробно.
Следует иметь в виду, что функции gethostbyname и gethostbyaddr возвращают указатель на статическую область памяти. Это означает, что каждое новое обращение к одной из этих функций приведёт к перезаписи данных, полученных при преыдущем обращении. |
Параллельное обслуживание клиентов
Способ 1
Этот способ подразумевает создание дочернего процесса для обслуживания каждого нового клиента. При этом родительский процесс занимается только прослушиванием порта и приёмом соединений. Чтобы добиться такого поведения, сразу после accept сервер вызывает функцию fork для создания дочернего процесса (я предполагаю, что вам знакома функция fork ; если нет, обратитесь к документации). Далее анализируется значение, которое вернула эта функция. В родительском процессе оно содержит идентификатор дочернего, а в дочернем процессе равно нулю. Используя этот признак, мы переходим к очередному вызову accept в родительском процессе, а дочерний процесс обслуживает клиента и завершается ( _exit ).
С использованием этой методики наш эхо-сервер перепишется, как показано в листинге 6.
Листинг 6. Эхо-сервер (версия 2, fork)
Способ 2
Листинг 7. Эхо-сервер (версия 3, неблокирующие сокеты и select).
Работа по стандартным протоколам
Как я уже говорил, сокеты могут использоваться при написании приложений, работающих по протоколам прикладного уровня Internet (HTTP, FTP, SMTP и т. д.). При этом взаимодействие клиента и сервера происходит по той же самой схеме, что и взаимодействие эхо-клиента и эхо-сервера в нашем примере. Разница в том, что данные, которыми обмениваются клиент и сервер, интерпретируются в соответствии с предписаниями соответствующего протокола.
Например, веб-сервер может работать по следующему алгоритму.
Веб-броузер, который является клиентом по отношению к веб-серверу, может использовать похожий алгоритм.
Как видим, в работе по стандартным протоколам нет ничего сложного или принципиально нового.
Прорыв за пределы платформы
В мире Internet взаимодействие программ, работающих на разных платформах, встречается сплошь и рядом. Так, практически ежесекундно очередной Internet Explorer подсоединяется к веб-серверу Apache, а очередной Netscape Navigator совершенно спокойно подключается к IIS. Вот почему весьма полезно писать программы так, чтобы их можно было без труда переносить на другие платформы. В этом разделе мы посмотрим, как переносить Linux-программы, использующие сокеты, на платформу Windows.
Список основных отличий socket API и Winsock API выглядит примерно так.
Если переписать наш эхо-клиент с учётом приведённых особенностей Winsock API, а затем скомпилировать его под Windows (например, с помощью Visual C++), он вполне сможет взаимодействовать с эхо-сервером, работающим под Linux. Таким образом, сокеты позволяют решить проблему кроссплатформенного взаимодействия двух приложений.
Заключение
В этой статье мы рассмотрели целый ряд важных аспектов программирования сокетов. Тем самым мы заложили прочную основу для дальнейших исследований в этой области. Разумеется, большое количество деталей осталось за рамками нашей беседы. Но теперь вы сможете самостоятельно почерпнуть недостающую информацию из man-страниц Linux и из собственного практического опыта. Желаю удачи.