Я собираюсь создать программную пару - клиент-сервер, выступающую в том числе и в роли чата. (Ведь в нем тоже нужно делать передачу данных).
Эту пару я собираюсь делать в виде библиотек. То есть разработать API.
Все, что далее будет необходимо - это использовать это API при разработке графического или консольного интерфейса.
Поскольку мы будем реализовывать библиотеки а не целостные модули, то необходимо будет разработать интерфейс их общения с основной программой.
В добавок, необходимо будет реализовать без-ожидательное выполнение функций. То есть не должно быть никаких ожиданий при, например, чтении - приеме из сети.
Реализация должна быть проведена на C/C++.
P.S.:
======= П О Д Р О Б Н Е Е о Р Е А Л И З А Ц И И ======
------- С Е Р В Е Р -------
Сервер - создает сокет.
привязывает сокет.
ожидает запроса на подключение.
принимает запрос на подключение.
принимает пакеты данных.
отправляет реакцию на принятые пакеты данных.
Мы не будем делать сервер демоническим, хотя это было бы полезно. Наоборот - мы сделаем его отдельно запускаемым. Как приложение. К тому же с организацией получения команд от оператора. Аналогично будем организовывать работу и клиента.
------- К Л И Е Н Т -------
Клиент - создает сокет.
привязывает сокет.
подает заявку на подключение.
производит обмен данными.
при необходимости - отключается от сервера.
====== Е Щ Е П О Д Р О Б Н Е Е О Р Е А Л И З А Ц И И ========
Вспомогательная база.
/* Отдельные, служебные объявления - Размер данных Ввода-Вывода, Порты сервера и клиента, 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.
Эту пару я собираюсь делать в виде библиотек. То есть разработать API.
Все, что далее будет необходимо - это использовать это API при разработке графического или консольного интерфейса.
Поскольку мы будем реализовывать библиотеки а не целостные модули, то необходимо будет разработать интерфейс их общения с основной программой.
В добавок, необходимо будет реализовать без-ожидательное выполнение функций. То есть не должно быть никаких ожиданий при, например, чтении - приеме из сети.
Реализация должна быть проведена на C/C++.
P.S.:
- Для реализации безождитательности сетевых запросов будем использовать неблокируемые сокеты.
- Остается вопросом - Реализация неблокируемости ввода-вывода. Реализация распараллеливания нас духовно не впечатляет потому, как имеет слишком бругденгбергскую структуру межпроцессных сообщений. Оказывает, неблокируемы ввод-вывод мы могём реализовать используя вызов select с нулевым временем ожидания. При этом нет необходимости переключать поток ввода в посимвольный режим. Нам достаточно и построчного ибо только строка для нас чего-нибудь да значит, зане по <ENTER>-у определяется.
- Когда я говорю о библиотеке я не имею в виду подключаемые модули библиотеки, но набор исходников с заголовочными файлами. Ведь так любому будет удобнее что-нибудь поменять в этой "библиотеке".
======= П О Д Р О Б Н Е Е о Р Е А Л И З А Ц И И ======
Сервер - создает сокет.
привязывает сокет.
ожидает запроса на подключение.
принимает запрос на подключение.
принимает пакеты данных.
отправляет реакцию на принятые пакеты данных.
Мы не будем делать сервер демоническим, хотя это было бы полезно. Наоборот - мы сделаем его отдельно запускаемым. Как приложение. К тому же с организацией получения команд от оператора. Аналогично будем организовывать работу и клиента.
------- К Л И Е Н Т -------
Клиент - создает сокет.
привязывает сокет.
подает заявку на подключение.
производит обмен данными.
при необходимости - отключается от сервера.
====== Е Щ Е П О Д Р О Б Н Е Е О Р Е А Л И З А Ц И И ========
Вспомогательная база.
/* Отдельные, служебные объявления - Размер данных Ввода-Вывода, Порты сервера и клиента, 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.