Клиент-серверная пара. API в исходнике

25 марта 2011 г.

Я собираюсь создать программную пару — клиент-сервер, выступающую в том числе и в роли чата. (Ведь в нем тоже нужно делать передачу данных).
Эту пару я собираюсь делать в виде библиотек. То есть разработать API.
Все, что далее будет необходимо — это использовать это API при разработке графического или консольного интерфейса.
Поскольку мы будем реализовывать библиотеки а не целостные модули, то необходимо будет разработать интерфейс их общения с основной программой.
В добавок, необходимо будет реализовать без-ожидательное выполнение функций. То есть не должно быть никаких ожиданий при, например, чтении — приеме из сети.
Реализация должна быть проведена на C/C++.

P.S.:

* Для реализации безождитательности сетевых запросов будем использовать неблокируемые сокеты.
* Остается вопросом — Реализация неблокируемости ввода-вывода. Реализация распараллеливания нас духовно не впечатляет потому, как имеет слишком бругденгбергскую структуру межпроцессных сообщений. Оказывает, неблокируемы ввод-вывод мы могём реализовать используя вызов select с нулевым временем ожидания. При этом нет необходимости переключать поток ввода в посимвольный режим. Нам достаточно и построчного ибо только строка для нас чего-нибудь да значит, зане по -у определяется.
* Когда я говорю о библиотеке я не имею в виду подключаемые модули библиотеки, но набор исходников с заголовочными файлами. Ведь так любому будет удобнее что-нибудь поменять в этой «библиотеке».

П О Д Р О Б Н Е Е о Р Е А Л И З А Ц И И

С Е Р В Е Р

Сервер

  • создает сокет.
  • привязывает сокет.
  • ожидает запроса на подключение.
  • принимает запрос на подключение.
  • принимает пакеты данных.
  • отправляет реакцию на принятые пакеты данных.

Мы не будем делать сервер демоническим, хотя это было бы полезно. Наоборот — мы сделаем его отдельно запускаемым. Как приложение. К тому же с организацией получения команд от оператора. Аналогично будем организовывать работу и клиента.

К Л И Е Н Т

Клиент –

  • создает сокет.
  • привязывает сокет.
  • подает заявку на подключение.
  • производит обмен данными.
  • при необходимости — отключается от сервера.

Е Щ Е П О Д Р О Б Н Е Е О Р Е А Л И З А Ц И И

Вспомогательная база.

/* Отдельные, служебные объявления - Размер данных Ввода-Вывода, Порты сервера и клиента, TimeToLive */
/* DataSize = 1k */
#define DataSize 0x400
#define ServerPort
#define ClientPort
#define TTL 0xfe

/* Общий класс для клиента и сервера */
class clsCommonClass {
public:
clsCommonClass();
~clsCommonClass();
int createSocket (int); // Создание сокета
int bindSocket (struct sockaddr*, socklen_t); // привязывает сокет
int RecieveData (); // Прием данных
int SendData (); // Посылка данных
virtual int AnalyzeData(); // читаем полученные данные генерируя отправляемые данные
protected:
int socketDomain;
int socketType;
int socketProtocol;
int Socket; // Сам сокет
char DataToRecv [DataSize]; // Принятые (Принимаемые) данные
char DataToSend [DataSize]; // Посланные (посылаемые данные)
fd_set fds; // Структура списка файл-дескрипоторов для ожидания (5ms)
struct timeval tv; // А это те самые 5 миллисекунд
struct sockaddr_in SocketAddr; // Адрес (структура адреса) для привязки
private:
}

/* PF_INET - TCP/IP, SOCK_STREAM - connection-oriented protocol, 0 - IP protocol (/etc/protocols) */
clsCommonClass::clsCommonClass ()
{
this->socketDomain = PF_INET;
this->socketType = SOCK_STREAM;
this->socketProtocol = 0;
}

int clsCommonClass::createSocket (int nonBlock)
{
int flags;
this->Socket = socket(this->socketDomain, this->socketType, this->socketProtocol);
if (this->Socket < 0) {
perror ("On socket creation ");
return (-1);
}

if (nonBlock) setsockopt(this->Socket, );

setsockopt(this->Socket, SOL_SOCKET, SO_RCVBUF, DataSize, sizeof(DataSize));
setsockopt(this->Socket, SOL_SOCKET, SO_SNDBUF, DataSize, sizeof(DataSize));
setsockopt(this->Socket, SOL_IP, IP_TTL, TTL, sizeof(TTL));

if (-1 == (flags = fcntl(this->Socket, F_GETFL, 0))) flags = 0;
if (fcntl(this->Socket, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("On setting non blocking mode to socket ");
return (-1);
}

return 0;
}

int clsCommonClass::bindSocket ()
{
bzero (&this->SocketAddr, sizeof(this->SocketAddr));
this->SocketAddr.sin_family = AF_INET;
this->SocketAddr.sin_port = htons (ServerPort);
this->SocketAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if ( bind (this->Socket, &this->SocketAddr, sizeof(this->SocketAddr)) ) {
perror ("On socket binding ");
return (-1);
}
return 0;
}

int clsCommonClass::RecieveData ()
{
tv.tv_sec = 0; // заполняем 5-ю миллисекундами
tv.tv_usec = 5;

FD_ZERO(&this->fds); // файл-дескриптор для проверки на чтение
FD_SET(this->Socket, &this->fds);

select(1, &this->fds, NULL, NULL, &tv); // Waiting for 5ms at reading.

if ( ! FD_ISSET(this->Socket, &this->fds) ) { // Ну - не судьбец нам их принять сейчас.
printf("Sorry, can't recieve data. I'll try it later.\n");
return (-2);
}

if ( send (this->Socket, &this->DataToRecv, DataSize, 0) < 0) { // Прием
if (errno == EAGAIN) {
printf("Assuming we've successfully recieved data.\n");
return 0;
}
perror("On send attempt ");
return (-1);
}

return 0;
}

int clsCommonClass::SendData () // Отправка - аналогично посылке
{
tv.tv_sec = 0;
tv.tv_usec = 5;

FD_ZERO(&this->fds);
FD_SET(this->Socket, &this->fds);

select(1, NULL, &this->fds, NULL, &tv); // Waiting for 5ms at writing.

if ( ! FD_ISSET(this->Socket, &this->fds) ) {
printf("Sorry, can't send data. I'll try it later.\n");
return (-2);
}

if ( send (this->Socket, &this->DataToSend, DataSize, 0) < 0) {
if (errno == EAGAIN) {
printf("Assuming we've successfully sent data.\n");
return 0;
}
perror("On send attempt ");
return (-1);
}

return 0;
}

~clsCommonClass::clsCommonClass ()
{
shutdown(this->Socket, 2); // close in and out chennels
}

Теперь дело за малым — опиать в обоих классах потомках класса clsCommonClass (серверном и клиентском) функцию AnalyzeData.

Теги: рубрика Программирование
  • Похожие статьи
  • Предыдущие из рубрики