|
Программирование на C++ с использованием сокетов в Linux |
||||||||||||
Содержание
1. ВведениеСокеты -- это механизм обмена данными между процессами. Эти процессы могут быть запущены на одной и той же машине, или на разных машинах, объединенных в компьютерную сеть. Будучи созданным, соединение между сокетами позволяет передавать данные в обеих направлениях, т.е. "туда и обратно", пока соединение не будет закрыто одной из сторон. Мне нужно было использовать сокеты в проекте, над которым я работал, и я написал и отладил несколько С++ классов, инкапсулирующих вызовы функций API для сокетов. Обычно приложение, запрашивающее данные, называется клиентом, а приложение, обслуживающее такие запросы -- сервером. Я создал два главных класса ClientSocket и ServerSocket, чтобы приложения, клиенты и серверы, могли их использовать для организации обмена данными. Цель данной статьи -- научить вас пользоваться классами ClientSocket и ServerSocket в собственных приложениях. Сначала мы коротко обсудим создание клиент-серверного соединения, а затем, в качестве примера, создадим простые приложения -- серверное и клиентское -- использующих два этих класса. 2. Обзор соединений клиент-сервер.Перед тем как перейти к рассмотрению кода, нам необходимо коротко рассмотреть по шагам создание типичного соединения между клиентом и сервером. Следующая таблица показывает эти шаги:
В основном все. Сначала сервер создает сокет, слушающий определенный порт, и ожидает попытку соединиться от клиентов. Клиент, со своей стороны, создает сокет и делает попытку соединиться с сервером. Затем сервер разрешает соединение, после чего может начинаться обмен данными. Когда все данные будут переданы, любая сторона может может закрыть соединение. 3. Реализация простого сервера и простого клиентаТеперь время погрузиться в код. В следующем разделе мы создадим и сервер и клиент, которые будут выполнять все описанные выше шаги. Мы реализуем эти операции в типичном порядке -- т.е. сначала создадим ожидающую соединения серверную часть, а затем создадим клиентскую часть, которая подключается к серверу и т.д. Полный код этого раздела можно найти в файлах simple_server_main.cpp и simple_client_main.cpp. Если вы хотите просто проверить, как работает этот код или поэкспериментировать с ним -- перейдите прямо в этот раздел. Там перечислены составляющие проект файлы, и обсуждается, как его скомпилировать и тестировать.3.1 Сервер -- создание ожидающего сокетаПервая вещь, которую мы должны сделать -- создать простой сервер, ожидающего входящих запросов от клиентов. Вот код, необходимый для создания серверного сокета: листинг 1 : создание серверного сокета (фрагмент файла simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Создание серверного сокета
ServerSocket server ( 30000 );
// оставшийся код -
// разрешение соединения, обработка запроса, и т.д...
}
catch ( SocketException& e )
{
std::cout << "Обнаружено исключение:" << e.description() << "\nВыход.\n";
}
return 0;
}
Вот и все. Конструктор класса ServerSocket вызывает необходимые API-функции для сокетов, и создает "слушающий" сокет, ожидающий соединения. Конструктор скрывает от вас детали, так что все, что вам нужно сделать, чтобы начать "слушать" локальный порт -- создать экземпляр этого класса. Обратите внимание на блок try/catch. ServerSocket и ClientSocket используют обработку исключений в стиле С++. Если выполнение метода класса по какой-либо причине завершается неудачно -- возбуждается исключение типа SocketException, описанное в SocketException.h. Не заканчивайте работу программы при возникновении исключения, лучше обработать его. Описание ошибки можно получить, вызвав метод description() класса SocketException, как показано выше. 3.2 Клиент -- подключение к серверуЗа второй шаг создания типичного клинт-серверного соединения -- попытку подключения к серверу -- отвечает клиент. Код похож на только что виденный вами код сервера: листинг 2 : создание клиентского сокета (фрагмент simple_client_main.cpp )
#include "ClientSocket.h"
#include "SocketException.h"
#include <iostream>
#include <string>
int main ( int argc, int argv[] )
{
try
{
// создание клиентского сокета
ClientSocket client_socket ( "localhost", 30000 );
// оставшийся код -
// посылка запроса, получение ответа, и т.д. ...
}
catch ( SocketException& e )
{
std::cout << "Обнаружено исключение:" << e.description() << "\n";
}
return 0;
}
Просто создавая экземляр класса ClientSocket, вы создаете linux сокет, подключенный к хосту и порту, указанным в параметрах конструктора. Как и в классе ServerSocket, если конструктор по какой-либо причине не срабатывает, возбуждается исключение. 3.3 Сервер -- разрешение попытки соединения для клиентаСледующий шаг клиент-серверного соединения происходит внутри сервера. Сервер отвечает за реализацию попытки соединения со стороны клиента, который открывает канал сообщения между сокетами. Мы добавим эту функциональность в наш простой сервер. Вот дополненная версия: листинг 3 : разрешение соединения (фрагмент simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Создание сокета
ServerSocket server ( 30000 );
while ( true )
{
ServerSocket new_sock;
server.accept ( new_sock );
// оставшийся код -
// чтение запроса, передача ответа, т.д. ...
}
}
catch ( SocketException& e )
{
std::cout << "Обнаружено исключение:" << e.description() << "\nВыход.\n";
}
return 0;
}
Разрешение соединиения достигается просто вызовом метода accept. Этот метод допускает попытку соединения и заполняет new_sock информацией о сокете, с которого производится попытка соединения. Как используется new_sock мы увидим в следующей разделе. 3.4 Клиент и сервер -- передача и прием данныхТеперь, когда сервер разрешил запрос клиента на соединение, самое время через соединенные сокеты посылать и принимать данные "туда и обратно". Продвинутая осбенность С++ -- возможность перегружать операторы, или, иными словами, заставить оператор выполнять определенные действия. В ClientSocket и ServerSocket я перегрузил операторы << и >>, и теперь они используются для записи данных в сокет и чтения их оттуда. Здесь дополненная версия простого сервера: листинг 4 : простая реализация сервера (simple_server_main.cpp)
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Создать сокет
ServerSocket server ( 30000 );
while ( true )
{
ServerSocket new_sock;
server.accept ( new_sock );
try
{
while ( true )
{
std::string data;
new_sock >> data;
new_sock << data;
}
}
catch ( SocketException& ) {}
}
}
catch ( SocketException& e )
{
std::cout << "Обнаружено исключение:" << e.description() << "\nВыход.\n";
}
return 0;
}
Переменная new_sock содержит всю информацию о нашем сокете, так что мы используем ее для обмена данными с клиентом. Строка "new_sock >> data;" должна читаться как "прочитать данные из new_sock, и поместить их в нашу строковую переменную 'data'." Таким же образом следующая строка посылает данные в 'data' назад через сокет клиенту. Если вы внимательны, вы обратите внимание, что здесь мы создали эхо-сервер. Каждая порция данных, которая отправлена от клиента на сервер, посылается назад клиенту, как есть. Мы можем написать клиент так, чтобы он отправлял порцию данных и затем выдавал на печать ответ сервера: листинг 5 : простая реализация клиента (simple_client_main.cpp)
#include "ClientSocket.h"
#include "SocketException.h"
#include <iostream>
#include <string>
int main ( int argc, int argv[] )
{
try
{
ClientSocket client_socket ( "localhost", 30000 );
std::string reply;
try
{
client_socket << "Test message.";
client_socket >> reply;
}
catch ( SocketException& ) {}
std::cout << "Мы получили от сервера сообщение:\n\"" << reply << "\"\n";;
}
catch ( SocketException& e )
{
std::cout << "Обнаружено исключение:" << e.description() << "\n";
}
return 0;
}
Мы посылаем строку "Test Message." на сервер, читаем ответ с сервера и печатаем его на стандартный вывод. 4. Компиляция и тестирование наших клиента и сервераТеперь, когда мы разобрали основы использования классов ClientSocket и ServerSocket, можно построить законченный проект и протестировать его. 4.1 Список файловСледующие файлы составляют наш проект:
4.2 Компиляция и тестированиеКомпиляция проста. Сначала сохраните все файлы проекта в одной под-директории, а потом введите команду в ответ на приглашение системы: prompt$ cd directory_you_just_created prompt$ make Эта команда скомпилирует все файлы в проекте и создаст выходные файлы simple_server и simple_client. Чтобы проверить эти два выходных файла, запустите сервер в одной консоли, а затем в другой консоли запустите клиент: первая консоль: prompt$ ./simple_server running.... вторая консоль: prompt$ ./simple_client We received this response from the server: "Test message." prompt$ Клиент будет посылать данные серверу, читать ответ и печатать его на стандартный вывод, как показано выше. Мы можем запустить клиент столько раз, сколько захотим, сервер будет отвечать на каждый запрос. 5. ЗаключениеСокеты -- простой и эффкетивный путь пересылки данных между процессами. В этой статье мы рассмотрели сокет-коммуникации и разработали пример сервера и клиента. Теперь у вас должна быть возможность добавить организацию связи с помощью сокетов в ваши собственные приложения! Rob TougherРоб работает программистом на C++ в Нью Йорке. Когда он не пишет код, его можно найти прогуливающимся по пляжу вместе с подружкой Николь и ее собакой Холли. Copyright (C) 2002, Rob Tougher.
|
||||||||||||
|
Вернуться на главную страницу |