Войти
ПроектыФорумУтилиты

Мой сетевой движок

Advanced: Тема повышенной сложности или важная.

Страницы: 1 2 314 15 Следующая »
#0
19:19, 3 окт. 2006

Предлагаю Вашему вниманию сетевой движок. Модель Клиент-Сервер. Между сервером и клиентом передаются сообщения, которые имеют тип и данные. Набор данных для каждого типа фиксирован. Хотелось бы знать, имеет ли смысл вылизать это и выложить в общий доступ, или я изобрел велосипед, и такая задача уже решена? Ниже - заголовочный файл с пояснениями

#define NL_MAX_MESSAGE   0x10000  // максимальный общий размер одного сообщения

// возможные типы передаваемых данных
enum {
NL_NONE,
NL_UI_8,   // unsigned char
NL_SI_8,   // signed char
NL_UI_16,  // unsigned short
NL_SI_16,  // signed short
NL_UI_32,  // unsigned long
NL_SI_32,  // signed long
NL_STR8,   // строка, 0-255 байт
NL_STR16,  // строка, 0-65535 байт
NL_BIN8,   // двоичные данные, 0-255 байт
NL_BIN16   // двоичные данные, 0-65535 байт
};

// номера ошибок
enum{
NL_ERROR_NO,
NL_ERROR_WINSOCK,
NL_ERROR_UNKNOWN_MESSAGE,
NL_ERROR_INCORRECT_MESSAGE,
NL_ERROR_MESSAGE
};

// структура для передачи двоичных данных
struct NL_TBinary{
  void *data;          // ссылка на двоичные данные
  unsigned short length;  // размер двоичных данных
};

// структура для хранения любого допустимого значения
union NL_Variable{
  unsigned char  ui_8;
  signed   char  si_8;
  unsigned short ui_16;
  signed   short si_16;
  unsigned long  ui_32;
  signed   long  si_32;
  char *     str;
  NL_TBinary bin;
};

// определение процедуры обработчика получения сообщения от клиента
// обработчик получает id клиента и посланные значения. Количество и типы значений
// определялись при задании обработчика сообщения
typedef void (*NLC_TInterpriterProcedure)(int id, NL_Variable *variable);

// определение процедуры обработчика получения сообщения от сервера
// обработчик получает посланные значения. Количество и типы значений
// определялись при задании обработчика сообщения
typedef void (*NLS_TInterpriterProcedure)(NL_Variable *variable);

// определение процедуры обработчика подключения нового клиента
// обработчик получает id подключения, и строковое представление адреса клиента
// обработчик возвращает флаг того, нужно ли его подключать (к примеру, этот адрес забанен)
typedef bool (*NL_TClientConnect)(int id, char *host);

// определение процедуры обработчика отключения клиента
typedef void (*NL_TClientDisconnect)(int id);


// инициализация сервера, с установкой порта прослушивания, обработчиков событий подключения и
// отключения клиента
// возвращает максимальное количество подключений, или 0, если произошла ошибка
int NLS_Init(unsigned short port, NL_TClientConnect connect, NL_TClientDisconnect disconnect);

// завершение работы сервера
void NLS_Close();

// инициализация клиента, с установкой соединения с сервером host:port
// возвращает флаг успеха
bool NLC_Init(unsigned short port, char *host);

// завершение работы клиента
void NLC_Close();


// установка формата сообщения от клиента. процедуре передается тип сообщения,
// процедура-обработчик сообщения (опционально), количество параметров и их типы.
void NLS_SetInterpriter(int message, NLS_TInterpriterProcedure proc, int count, ...);

// установка формата сообщения от сервера. процедуре передается тип сообщения,
// процедура-обработчик сообщения(опционально), количество параметров и их типы.
void NLC_SetInterpriter(int message, NLC_TInterpriterProcedure proc, int count, ...);

// отправка сообщения от сервера к клиенту
// процедуре передается id клиента, тип сообщения и параметры сообщения, в соответствии с
// определением сообщения (определение на стороне клиента
bool NLS_Message(int id, unsigned char message, ...);

// отправка сообщения от клиента к серверу
// процедуре передается тип сообщения и параметры сообщения, в соответствии с
// определением сообщения (определение на стороне клиента
bool NLC_Message(unsigned char message, ...);

// обновление состояния всех дескрипторов, подключение новых клиентов, получение сообщений от
// клиентов
void NLS_Refresh();

// получение сообщений от сервера
// возвращает состояние подключения к серверу. Если подключение есть, возвращает true, иначе false
bool NLC_Refresh();

// отключение клиента
void NLS_Disconnect(int id);

// функция возвращает номер ошибки последнего действия. NL_ERROR_NO (0) если ошибки не было
int NL_Error();

// функция возвращает описание ошибки. Одному номеру ошибки могут соответствовать разные описания
char *NL_ErrorDescription();

Пример текста сервера, без лишнего текста и обработки ошибок:

// обработчик сообщения от клиента
void InterpriterChar(int id, NL_Variable *variable)
{
  printf("Client %d, Char income '%c'\n", id, variable[0].ui_8);
}

// обработчик подключения клиента
bool cl_Connect(int id, char *host)
{
  // запомним где-нибудь этот id
  return true;
}

// обработчик отключения клиента
void cl_Disconnect(int id)
{
  // учтем, что id теперь не актуален
}

int main(int, char **)
{
  int clients;
// Запустиим сервер на порту 1234, установив обработчики событий подключения клиентов
  clients = NLS_Init(1234, cl_Connect, cl_Disconnect);
  printf("Max client %d\n", clients);

// Установим обработчики сообщений от клиента
  NLC_SetInterpriter(MSG_CLIENT_CHAR  , InterpriterChar  , 1, NL_UI_8);

// Установим формат сообщений сервера
  NLS_SetInterpriter(MSG_SERVER_INT, NULL, 1, NL_SI_32);
  NLS_SetInterpriter(MSG_SERVER_EMPTY, NULL, 0);

// Запустим вечный цикл сервера
  for( /* пока не поступит команда на выход*/ ){
    for( /* цикл по клиентам */ )
      NLS_Message(client_id, MSG_SERVER_INT, time());
    NLS_Refresh();
  }

  NLS_Close();
}

Движок с примером.


#1
23:41, 3 окт. 2006

Выглядит красиво ! Поделись полным сурсом !

#2
7:05, 4 окт. 2006

Мне тоже это интересно.

#3
9:56, 4 окт. 2006

Первое что заметил- id клиента числовой. Если мы будем начислять циклом id клиентов, то рано или поздно мы выйдем за границы id. Тоесть требуется дополнительная проверка, к томуже кто-то из первых мог остаться=> еще одна проверка. Не слишком ли расточительно? Думаю имеет смысл сделать id символьным типом. И тут уже преобразование в символьный тип или id по авторизации

#4
10:14, 4 окт. 2006

Работа с библиотекой простая (мне понравилась). Сейчас попробую какую-нибудь простенькую игру на несколько клиентов сделать. Потом отпишу отзыв.:)

#5
10:35, 4 окт. 2006

Motiv_studenta
Не выйдем. id уникальны в любой момент времени, но не уникальны в течении всего сеанса работы сервера. С учетом того, что при использовании WinSocket максимально возможное количество клиентов 32764 (по моему), id лежат в пределах 0..32763, и беспокоиться не о чем.
Назвать расточительностью цикл при коннекте клиента, от 0 до в среднем Sqrt(ТекущееКоличествоКлиентов), сложно. Этот цикл ищет уже неиспользуемые id и использует их.

#6
11:34, 4 окт. 2006

Tawny

Приятно работать с твоим сетевым движком:) Тестировал на 3-х клиентах в локалке - глюков не замечено. Его можно в своих играх использовать?:)

#7
12:06, 4 окт. 2006

Tawny
Тогда я думаю стоит этот цикл вынести в отдельный поток с низким приоритетом(аля сборщик мусора). Кстати внутренняя реализация какая? SELECT или многопоточность?

    for( /* цикл по клиентам */ )
      NLS_Message(client_id, MSG_SERVER_INT, time());
Цикл по id?  NLS_Message ждет какое-то время, потом отдаеьт управление? Вообщемто изделие неплохое, просто интересуюсь
P.S-жалко что не кроссплатформенное решение

#8
12:25, 4 окт. 2006

Motiv_studenta
select, так как недостаточно хорошо умею работать с многопоточностью.
Цикл по клиентам. В обработчик NL_TClientConnect подается id клиента, а что с ним будет делать программа, как использовать - движок не волнует.
Что касается кроссплатформенности, подумаю, но возможность отладить есть только на MS VS .Net и на Linux FedoreCore 4

#9
13:18, 4 окт. 2006

Vitorio
Да, использовать можно где угодно и как угодно.

#10
14:31, 4 окт. 2006

Tawny
собственно, Linux и интересует. Думаю на фряхе и солярке тоже заведется

#11
15:10, 4 окт. 2006

я писал свой высокоуровневый протокол ( надстройка над UDP ) и считаю ограничение размера сообщения неправомерным

#12
15:17, 4 окт. 2006

Bais
Снять ограничение можно без особых проблем, но при этом придется память под приходящие сообщения выделять динамически, что несколько медленней статичного выделения памяти. По моим прикидкам, для сетевых игр 65к за 1 пакет должно хватать за глаза, в противном случае эта игра в принципе не доступна  для модемщиков, даже ADSL не вытянет. Закачку же файлов лучше реализовывать с отдельного сервера отдельным каналом.

#13
15:42, 4 окт. 2006

Tawny
Сижу на работе нет времени посмотреть твой движок :(.
Скажи сразу Твой движок может обеспечить такое: есть сервер и N клиентов, в тот момент когда один из клиентов изменил свое состояние шлёт измененные данные серверу, а тот в свою очередь остальным клиентам?

#14
16:50, 4 окт. 2006

сурсами поделишься ?

Страницы: 1 2 314 15 Следующая »
ПроектыФорумУтилиты

Тема в архиве.