создание сетевого приложения на c
Программирование сетевых приложений на языке C++
Доброго времени суток! Я хочу поделиться с вами опытом, накопленным за несколько месяцев работы над данной темой.
Сейчас сетевые игры и приложения продолжают набирать популярность, а вместе с тем возрастает число желающих написать свою собственную программу, работающую с сетью.
Началось всё с того, что мне необходимо было написать обычный чат на языке C++. Естественно, нужно было найти способ попроще, при этом не потратив много времени.
Среда C++ Builder предоставляет несколько вариантов написания сетевых приложений:
1. Использовать библиотеку winsock.h (библиотека Windows Sockets)
Данный способ хорош тем, что он универсален, и это является безусловным плюсом. Но если вы выбрали данный способ, будьте готовы к тому, что вам придётся копаться в WinAPI и создавать потоки, а это не каждому под силу. Поэтому данный способ не сильно подходит в ситуации, когда времени на написание готовой программы не так уж и много.
2. Использовать компоненты Indy
Если вы будете использовать этот способ, будьте готовы испытать его один серьёзный минус — слабая переносимость. Компоненты очень серьёзно зависят от версии библиотеки, а с каждой новой версии программной среды поставляется новая версия библиотеки Indy, в которой добавляются или перерабатываются новые свойства и методы компонент. И этот способ я тоже быстро отсёк, потому что хотелось найти нечто более универсальное.
3. Использовать компоненты TcpServer и TcpClient
Лично я пытался соорудить чат на этих компонентах, но так ничего и не вышло. Надеюсь, что у вас, уважаемые хабраюзеры, был успешный опыт использования данных компонент.
4. Использовать компоненты ServerSocket и ClientSocket
Фундаментально эти компоненты основаны на Windows Sockets, только здесь вам не придётся возиться с WinAPI. Всё сводится к тому, чтобы грамотно использовать свойства и методы этих компонент. Этот способ экономит достаточно времени, и поэтому для ситуаций, когда нужен результат за короткое время, он подходит идеально.
ServerSocket и ClientSocket могут работать в двух режимах: блокирующем и асинхронном. Отличия в том, что при блокирующем режиме программа-сервер приостановит свою работу до тех пор, пока не подключится новый клиент, а каждый клиент обслуживается в отдельном потоке. Поэтому оптимальнее выбрать асинхронный режим, в котором программа-сервер будет спокойно продолжать работу, не дожидаясь очередного клиента.
Для того, чтобы создать сервер, в компоненте ServerSocket достаточно лишь указать порт, который будет использоваться. Это целое число от 0 до 65535. После этого используются лишь следующие методы: Open() для создания сервера и Close() для его разрушения.
Используя компоненту ClientSocket, можно создать клиентскую часть приложения. У компоненты ClientSocket имеются следующие свойства:
ClientSocket1->Address; // переменная строкового типа, в которой прописывается IP-адрес сервера
ClientSocket1->Host; // переменная строкового типа, в которой прописывается DNS сервера
ClientSocket1->Port; // переменная целого типа, определяющая порт сервера, к которому производится подключение
Стоит отметить, что свойство Host является более приоритетном, чем свойство Address. Таким образом, если вы укажете оба свойства, то подключение установится с DNS сервера, который указан в свойстве Host. Подключение устанавливается или разрывается с помощью тех же методов Open() и Close().
Общим для обеих компонент является булево свойство Active, по которому определяется корректность (или активность) соединения.
Как видите, всё просто. Но будьте внимательнее с последовательностью событий, совершаемых в данных компонентах, и я верю, что через несколько дней вы сможете написать простенький чат для общения с друзьями.
Результат моего исследования этой темы вылился в методическую разработку, благодаря которой я успешно сдал экзамен по программированию. В ней же подробно расписаны принципы работы рассмотренных в данной статье компонент.
Также рекомендую ознакомиться с книгой Архангельского А.Я. «Приёмы программирования в C++ Builder 6 и 2006. Механизмы Windows, сети», в которой также освящена тема разработки сетевых приложений.
Пишем чат для локальной сети, используя C++ Builder. Серверная часть
Несколько месяцев назад понадобилось разработать чат для локальной сети одного офиса, а также выступить с этой программой на научной конференции. Делать его я решил в среде разработки Builder C++ 2006. При написании статьи у меня возникла одна самая главная проблема — полное отсутствие опыта в работе с сетями в билдере, поэтому статью пишу для таких же «программистов», как я. Отмечу сразу, в интернете найдется множество программ, которые, несомненно, будут лучше моей, но задание было не найти программу, а разработать. Статья получится большая, поэтому разделю ее на 2 части — серверную и клиентскую.
Первым делом надо было решить, что это будет за приложение.
Первое, что пришло в голову — открывать сервер на каждом компьютере, объединяя все компьютеры в кольцо. Сообщение передавать тому клиенту, к которому мы сейчас подключены. Там проверять, нам ли это сообщение, или нет; если нет — то отправлять дальше до тех пор, пока оно не достигнет адресата. Идея не понравилась, главным образом из-за того, что не хотелось, чтобы сообщение проходило через промежуточных пользователей.
Вторая идея мне понравилась. Пусть это будет клиент-серверное приложение. Открываем на одном компьютере сервер, все клиенты подключаются к нему. С клиента отправляем сообщение на сервер, а он уже перенаправляет его адресату. Кстати говоря, второе преимущество этой сетевой архитектуры состоит в том, что каждому клиенту нужно знать только один IP адрес — IP-адрес сервера. Да и при выходе клиента из сети не придется искать того, к кому он был подключен.
Конечно же, должен присутствовать графический интерфейс, причем такой, чтобы работал по принципу «plug and play» — запустил программу и сразу можно приниматься за переписку. Поэтому в окне программы будет минимум компонентов, даже не будет меню бара.
Как будем пересыпать сообщения? Используя сокеты, а именно стандартные компоненты билдера ClientSocket и ServerSocket, которые будут использоваться в программах клиента и сервера соответственно.
Реализация
Сервер
В событии OnConnect сервера запускается таймер. Почему-то код исполняемый таймером у меня не получилось выполнять сразу в событии, поэтому таймер выполняет его и сразу же отключается. По таймеру сервер с помощью цикла отправляет всем клиентам сообщение, в котором в одной строке содержатся все имена подключенных в данный момент клиентов. Зачем это сделано — я расскажу в описании клиента. Список же клиентов формирует функция online() «склеивающая» имена клиентов из массива.
Однако, все самое интересное происходит в событии сервера OnRead. Структура каждого сообщения, посылаемого как клиентом, так и сервером, обязательно содержит в начале 4 цифры. Это случайная комбинация цифр, придуманная мной для того, чтобы сервер и клиент могли различать сообщения, необходимые для «авторизации», или же сообщения, содержащие в себе текст для пересылки. Всего клиент может посылать серверу 4 типа сообщений. Сообщения с кодом 6141 посылаются серверу при первом подключении, они также сообщают серверу имя нового клиента, а сервер вносит его в массив и выводит в Memo (декоративном элементе, созданном просто чтобы знать, кто в данный момент подключен). Сообщение с кодами 5280 и 5487 потеряли свою актуальность, но почему то не были убраны мной из кода сервера. Сообщения с кодом 3988 самые важные. Это и есть сообщения содержащие в себе всю информацию для обмена сообщениями между пользователями. Структура такого сообщения:
При отключении какого либо из клиента массив очищается, клиентам опять отправляется запрос на получение имени (при этом имена отправляются и принимаются в том порядке в каком подключены клиенты) и массив заново заполняется. Также клиентам сразу же отправляется новый список клиентов, находящихся в сети. Делается это также внутри таймера. Сообщения с запросом имени отправляются клиентам с помощью цикла:
Графическая часть
Внешний вид окна сервера:
Изначально у меня не было в планах выводить какую либо информацию в окне сервера, но в итоге решил выводить самое важное: IP-адрес сервера, количество активных подключений, порт сервера, имя компьютера ка котором он запущен (почему-то всегда «1», исправить я это пока не смог), и список имен подключенных клиентов. Сервер сворачивается в область уведомлений. Реализовано это несколькими функциями, подробно разбирать я их не буду. Также на сервере полностью отключен отлов ошибок (которых вообще за 2 недели непрерывной работы не возникало, но мало ли, все таки полностью парализуется работа сети).
В заключение хочу сказать, что получилось достаточно примитивно написанный, однако стабильно работающий сервер, позволяющий одновременно переписываться 20 людям (больше я просто не проверял). Все исходники, exe-файлы и полный разбор кода клиента будут во второй статье.
Использование visual C# для создания удаленного сервера
В этой статье вы можете создать удаленный сервер, к котором можно получить доступ к другому приложению с помощью visual C#.
Оригинальная версия продукта: Visual C #
Исходный номер КБ: 307445
Сводка
Требования
В этой статье предполагается, что вы знакомы со следующими разделами:
Создание объекта удаленного сервера
Первым шагом в создании приложения-сервера является создание объекта сервера. Объект сервера — это то, с чем клиентские приложения мгновенно взаимодействуют и взаимодействуют на серверном компьютере. Клиентская заявка делает это с помощью прокси-объекта, созданного на клиенте. В этом примере объект сервера находится в библиотеке класса (DLL) и называется myRemoteClass.
В Обозревателе решений переименуем файл кода Class1.cs в ServerClass.cs.
Откройте ServerClass.cs и переименуйте class1 в myRemoteClass. Также необходимо переименовать конструктор по умолчанию для класса таким образом, чтобы он совпадал с именем класса. myRemoteClass должен наследовать от MarshalByRefObject класса. Теперь класс должен отображаться следующим образом:
Добавьте общедоступный метод в myRemoteClass, который принимает строку, отображает сообщение на консоль со значением строки и возвращает True, если строка не пуста.
Создайте проект для создания ServerClass.dll сборки.
Сохранить и закрыть проект.
Создание приложения удаленного сервера
После создания объекта сервера, с который клиент будет общаться, необходимо зарегистрировать этот объект в рамках Remoting. При регистрации объекта необходимо также запустить сервер и подключить сервер к порту для подключения к порту. Для этого необходим тип проекта, который выводит исполняемый файл.
Причина, по которой объект сервера включен в отдельный проект, заключается в том, что вы можете легко ссылаться на объект сервера из клиентского проекта. Если вы включаете его в этот проект, вы не можете ссылаться на него, так как ссылки могут быть заданной только для DLL-файлов.
В Обозревателе решений переименуем файл Class1.cs в RemoteServer.cs.
Добавьте ссылку на System.Runtime.Remoting пространство имен.
Добавьте ссылку на сборку ServerClass.dll, созданную в предыдущем разделе.
Используйте заявление в пространствах имен и имен, чтобы в дальнейшем в коде не требовалось квалифицировать объявления в этих пространствах using Remoting Remoting.Channels Remoting.Channels.TCP имен. Вы должны использовать using заявление до любых других заявлений.
Объявить соответствующую переменную. Объявить и инициализировать объект, который прослушивает подключение клиентов в определенном порту, который является TcpChannel портом 8085 в этом примере. Используйте метод RegisterChannel для регистрации канала службами канала. Добавьте следующий код объявления в Main Class1 процедуру:
Вызов метода объекта для регистрации объекта с помощью фреймворка Remoting и укажите RegisterWellKnownType RemotingConfiguration следующие ServerClass параметры:
Полное имя зарегистрированного объекта (в данном примере — ServerClass.myRemoteClass), а затем имя сборки ServerClass. Укажите как имя пространства имен, так и имя класса здесь. Так как в предыдущем разделе не было указано пространство имен, используется корневое пространство имен по умолчанию.
Назови конечную точку, где объект должен быть опубликован как RemoteTest. Клиенты должны знать это имя, чтобы подключиться к объекту.
Используйте режим SingleCall объекта, чтобы указать конечный параметр. Режим объекта указывает срок службы объекта при его активации на сервере. В случае объектов создается новый экземпляр класса для каждого вызова, который совершает клиент, даже если один и тот же клиент вызывает один и тот же SingleCall метод несколько раз. С другой стороны, Singleton объекты создаются только один раз, и все клиенты взаимодействуют с тем же объектом.
Используйте метод ReadLine объекта Console для поддержания работы серверного приложения.
Выполните построение проекта.
Сохранить и закрыть проект.
Ссылки
Кроссплатформенный многопоточный 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-заголовка не возможно.
BestProg
В данной теме описывается один из способов разработки простейшего распределенного приложения, которое использует удаленное взаимодействие между компьютерами в сети. По данному примеру можно научиться разрабатывать приложения, которые взаимодействуют между собой в локальной сети. Тема предусматривает наличие базовых навыков работы в Microsoft Visual Studio 2010.
Содержание
Поиск на других ресурсах:
Условие задачи
1. Разработать распределенное приложение, которое содержит следующие части:
⇑
Соображения
Для проверки правильности выполнения задачи, нужно наличие как минимум двух компьютеров, которые объединенные между собой в локальную сеть.
Разработка распределенного приложения осуществляется на одном компьютере в MS Visual Studio 2010. Тестирование взаимодействия клиентской и серверной частей осуществляются также на одном компьютере.
После завершения тестирования, серверный код размещается на компьютере-сервере. Клиентский код из компьютера-клиента делает запрос на компьютер-сервер, получает ответ и выводит этот ответ на экран. В нашем случае клиентский код вызывает серверную функцию решения квадратного уравнения.
⇑
Выполнение
1. Запустить Microsoft Visual Studio 2010
⇑
2. Разработка общей сборки с метаданными
2.1. Что такое общая сборка?
Для того, чтобы клиент и сервер взаимодействовали между собой, нужно наличие еще одной части программного кода – общей сборки. Общая сборка содержит метаданные типов, которые являются допустимыми для удаленного вызова. Метаданные типов – это информация о типах данных, которые используются при удаленном вызове. Эта информация нужна как клиенту, так и серверу.
В конечном счете, после создания, программный код общей сборки должен размещаться как на компьютере клиента, так и на компьютере сервера. В противном случае, связь между клиентом и сервером невозможно будет установить.
⇑
2.2. Создание модуля общей сборки
Рис. 1. Создание библиотеки по шаблону Class Library
⇑
2.3. Текст модуля общей сборки
Листинг модуля общей сборки следующий (файл Class1.cs ):
⇑
2.4. Объяснение к методу решения квадратного уравнения
⇑
На этом шаге нужно откомпилировать проект командой
Этот файл нужно будет разместить в папках с исполняемыми модулями на компьютере-клиенте и на на компьютере-сервере. Файл общей сборки готов.
Рис. 2. Файл ClassLibrary1.dll
⇑
2.6. Закрыть решение (проект)
Общая сборка разработана. Чтобы закрыть решение (Solution) нужно выполнить команду
На следующем этапе будет разрабатываться программный код серверной части распределенного приложения.
⇑
3. Создание программного кода, который будет размещаться на компьютере-сервере
После создания файла (модуля) общей сборки на одном и том же компьютере нужно разработать программный код, который будет размещаться на компьютере-сервере.
Чтобы сервер отвечал на запросы клиента, программный код серверной части должен быть запущен на выполнение в момент запроса.
3.1. Создание приложения типа Console Application
В результате откроется окно New Project (рисунок 3), в котором нужно:
Рис. 3. Окно создания консольного приложения
Листинг файла следующий
⇑
3.2. Добавление ссылок на сборки
Чтобы использовать программный код общей сборки нужно подключить:
⇑
3.2.1. Добавление ссылки на сборку System.Runtime.Remoting
Чтобы добавить ссылку на сборку System.Runtime.Remoting нужно выполнить следующую последовательность команд.
Рис. 4. Команда Add Reference…
В результате откроется диалоговое окно Add Reference (рисунок 5), в котором нужно:
Рис. 5. Подключение сборки System.Runtime.Remoting.dll
После этого в Solution Explorer в проекте ConsoleApplication1 будет отображена сборка System.Runtime.Remoting (рисунок 6).
Рис. 6. Подключенная к проекту сборка System.Runtime.Remoting
⇑
3.2.2. Добавление ссылки на сборку ClassLibrary1
В нашем случае выбирается папка (см. п. 2.4):
Рис. 7. Выбор папки и файла общей сборки
Рис. 8. Сборка ClassLibrary1 в перечне сборок серверного приложения
⇑
3.2.3. Подключение пространств имен сборок ClassLibrary1 и System.Runtime.Remoting в программный код файла Program.cs
После подключения сборок к проекту, нужно подключить необходимые пространства имен в файле Program.cs. Добавляются 4 пространства имен:
На данный момент текст файла Program.cs следующий
⇑
3.3. Написание кода серверной части
3.3.1. Действия, которые нужно выполнить на сервере
На компьютере-сервере нужно выполнить три основных действия:
⇑
3.3.2. Текст кода серверной части
Ниже приведен текст программного кода серверной части
Объясним некоторые фрагменты кода серверной части.
⇑
3.4. Компиляция. Файлы серверной части
На этом шаге нужно откомпилировать текст командой
для получения исполняемого ( *.exe ) файла. В результате будет создан исполняемый файл с именем
Рис. 9. Содержимое папки Debug с исполняемым файлом ConsoleApplication1.exe серверной части и файлом сборки ClassLibrary1.dll
Оба файла ConsoleApplication1.exe и ClassLibrary1.dll нужно будет разместить (скопировать) на компьютере-сервере.
После этого проект, который соответствует серверной части, завершен.
⇑
4. Разработка приложения клиентской части
4.1. Создание приложения клиентской части по шаблону Windows Forms
Проект по шаблону Windows Forms создается стандартным способом:
как показано на рисунке 10.
В проекте задаются:
После выбора OK будет создана новая форма проекта как показано на рисунке 11.
Рис. 11. Новая форма проекта
⇑
4.2. Проектирование формы
Нужно разместить на форме следующие элементы управления (рисунок 12):
Рис. 12. Размещение элементов управления на форме
⇑
4.2.2. Настройка свойств фомы и элементов управления
Подробное описание того, как осуществляется настройка элементов управления на форме можно найти в теме:
На этом шаге, с помощью окна Properties, нужно настроить следующие свойства формы и элементов управления:
После настройки элементов управления, окно формы будет иметь вид как показано на рисунке 13.
Рис. 13. Окно программы-клиента после настройки
После этого можно разрабатывать программный код клиента.
⇑
4.3. Текст модуля формы Form1.cs
⇑
4.4. Алгоритм работы программы-клиента
На компьютере клиенте нужно выполнить следующие действия:
⇑
4.5. Написание программного кода клиентской части
4.5.1. Добавление ссылок на сборки
Для того, чтобы использовать код общей сборки, на компьютере-клиенте нужно подключить:
Это осуществляется командой «Add Reference…» так же, как было сделано для серверной части (см. п.п. 3.2.1, 3.2.2).
После подключения, вкладка References в окне Solution Explorer будет иметь вид, как показано на рисунке 14.
⇑
4.5.2. Подключение пространств имен
⇑
4.5.3. Написание кода метода Form1_Load()
Ниже приведен текст обработчика события Form1_Load() :
⇑
4.5.4. Программирование события клика на кнопке «Вычислить». Текст клиентской части
На этом шаге нужно запрограммировать обработку события клика на кнопке «Вычислить» ( button1 ). Подробное описание того как программируется событие описывается в теме:
Текст программы-клиента с реализованным обработчиком события клика на кнопке «Вычислить» приведен ниже
⇑
5. Тестирование взаимодействия между клиентом и сервером на одном компьютере без использования локальной сети
Для проведения тестирования нужно:
Рис. 15. Выполнение серверной части
Приблизительный результат выполнения клиентского приложения приведен на рисунке 16.
Рис. 16. Клиентская программа
Важно: если закрыть программу-сервер и попробовать провести вычисления на клиенте, то возникнет исключительная ситуация. Система не сможет найти приложение-сервер. Значит, для проведения тестирования, сервер должен все время выполняться.
Клиентов можно запускать сколько угодно и производить запросы к серверу.
⇑
6. Копирование серверной части на другой компьютер
На этом шаге нужно скопировать файлы ConsoleApplication1.exe и ClassLibrary1.dll на компьютер-сервер. Это осуществляется с помощью, например, обычного файлового менеджера. Файлы копируются в заведомо подготовленную папку.
⇑
7. Определение имени сервера в сети из компьютера-клиента
Если компьютеры объединены в локальную сеть, то каждый из них имеет свое уникальное имя. Способ определения имени компьютера в сети зависит от конкретной реализации операционной системы. В общем случае, чтобы определить это имя, нужно вызвать утилиту Control Panel (Панель управления). В этой утилите можно выбрать элемент System (Система). Откроется окно, в котором можно прочитать имя компьютера.
⇑
8. Корректирование клиентского программного кода. Замена имени localhost
Если известно имя компьютера-сервера в локальной сети, тогда нужно в клиентской части сделать изменения. В исходном коде файла Form1.cs в методе Form_Load() вместо строки
где MyComp – имя компьютера-сервера в локальной сети. Скорей всего у вас будет другое имя.
Итак, текст обработчика события загрузки формы в нашем случае следующий:
Рис. 17. Папка с клиентским исполняемым модулем WindowsFormsApplication1
⇑
9. Тестирование взаимодействия между клиентом и сервером на разных компьютерах, объединенных между собой в локальную сеть
На этом этапе нужно протестовать работу между клиентом и сервером на разных компьютерах, которые объединенные между собой в локальную сеть.
Важно: чтобы между клиентом и сервером было взаимодействие, компьютер-клиент должен иметь соответствующие права доступа к компьютеру-серверу. Например, на сервере может быть включен общий доступ к его ресурсам. Это все осуществляется соответствующими настройками на компьютере-сервере. В данной теме это не рассматривается.