Скачать презентацию
Идет загрузка презентации. Пожалуйста, подождите
Презентация была опубликована 11 лет назад пользователемmasy.nnover.ru
1 КЛИЕНТ-СЕРВЕРНОЕ ПРИЛОЖЕНИЕ (НА ПРИМЕРЕ ПРОТОТИПА МНОГОПОЛЬЗОВАТЕЛЬСКОЙ ДВУМЕРНОЙ СТРАТЕГИИ) Слушатель курса «Профессиональное программирование» Маковкин С.Ю. Научный руководитель: К. ф.-м. н., ф-та ВМК, доцент каф. ТУиДМ Городецкий Станислав Юрьевич
2 Архитектура клиент-сервер Клиент-серверная система характеризуется наличием двух взаимодействующих самостоятельных процессов - клиента и сервера, которые, в общем случае, могут выполняться на разных компьютерах, обмениваясь данными по сети. Клиент-серверные приложения применяются довольно широко в коммерческих и OpenSource проектах: ICQ (Oscar), Jabber (XMPP), все игровые MMORPG проекты, банковское ПО и т.д.
3 Постановка задачи. Техническое задание. Целью работы было создание клиент-серверной системы, которая передаёт данные от клиента к серверу, и от клиента до клиента с обработкой на сервере. Передача данных через сеть Передача различных типов сообщений Поддержка двумерной карты в клиенте Перемещение юнитов Многопоточность
4 Структура проекта 3 компонета: 1)Утилита редактор карт 2)Сервер-приложение 3)Клиент-приложение Редактор карт -Моделирование карты -Сохранение -Загрузка Сервер -Подгрузка карты -Контроль состояния клиентов -Пересылка списка юнитов -Пересылка списка ников -Пересылка чат сообщений -Решение задачи о поиске пути Клиент -Авторизация по логину/паролю -Получение карты -Отрисовка карты, юнитов ников, чат сообщений
5 Применённые решения Редактор карт Матрица TColor: std::vector Заполнение: for(int i = 0; i < Height; ++i) { std::vector line(Width, clGreen); TC.push_back(line); } Изменение размера: std::vector line(WidthN, clGreen); TC.resize(HeightN, line); for(int i = 0; i < TC.size(); ++i) { TC[i].resize(WidthN, clGreen); } Заполнение ячейки: if(TypeTerrain == 1) TC[R][C]= clBlue; Сервер ClientSocket1 ClientSocketThread::Execute() ClientSocket1->Active = true; thread = new ClientSocketThread(this, ClientSocket1->Socket); TWinSocketStream *stream = new TWinSocketStream(socket, 30000); stream->Write(&msgType, sizeof(msgType)); stream->Read(&msgType, sizeof(msgType));
6 Поток (thread) на клиенте void __fastcall ClientSocketThread::Execute() { TWinSocketStream *stream = new TWinSocketStream(socket, 30000); // Sending clients nick {... } while (! terminated) { if (stream->WaitForData(1000)) { MessageType msgType; int cnt = stream->Read(&msgType, sizeof(msgType)); if (cnt == -1) break; if (cnt > 0) { switch (msgType) { case mtMap: {... } case mtClientID: {... } case mtClientsNicks: {... } case mtUnitList: {... } case mtChatMessage: {... } Поток (thread) на сервере void __fastcall ServerSocketThread::Execute() { client = new Client; client->nick = "xxx"; client->socket = ClientSocket; client->stream = new TWinSocketStream(ClientSocket, 10000); client->thread = this; while ((! stopped) && ClientSocket->Connected) { MessageType msgType; int cnt = client->stream->Read(&msgType, sizeof(msgType)); if (cnt == 0) { break; } if (cnt > 0) { switch (msgType) { case mtInit: {... } case mtRegistered: {... } case mtGetNewData: {... } case mtMoveUnit: {... } case mtChatMessage: {... } socket = ClientSocket; client->stream = new TWinSocketStream(ClientSocket, 10000); client->thread = this; while ((! stopped) && ClientSocket->Connected) { MessageType msgType; int cnt = client->stream->Read(&msgType, sizeof(msgType)); if (cnt == 0) { break; } if (cnt > 0) { switch (msgType) { case mtInit: {... } case mtRegistered: {... } case mtGetNewData: {... } case mtMoveUnit: {... } case mtChatMessage: {... }">
7 Типы сообщений. Внутренний протокол enum MessageType { mtMap, mtInit, mtClientID, mtUnitList, mtGetNewData, mtMoveUnit, mtClientsNicks, mtChatMessage, mtRegistered }; Приём чат-сообщения в клиенте: case mtChatMessage: { int fromClientID; stream->Read(&fromClientID, sizeof(fromClientID)); int len; stream->Read(&len, sizeof(len)); char *message = new char[len + 1]; stream->Read(message, len); message[len] = '\0'; Player *from = form->players[fromClientID]; if (from) { form->ListBoxChat->Items->Add(from->nick + ": " + message); } delete[] message; break; } Передача чат-сообщения от сервера: MessageType msgType = mtChatMessage; client->stream->Write(&msgType, sizeof(msgType)); int fromClientID = msg->first; client->stream->Write(&fromClientID, sizeof(fromClientID)); char *message = msg->second.c_str(); int len = strlen(message); client->stream->Write(&len, sizeof(len)); client->stream->Write(message, len); Структура чат сообщения: mtChatMessage, toClientID, len, message Пересылка сообщения напрямую в сокет: MessageType msgType = mtChatMessage; ClientSocket1->Socket->SendBuf(&msgType, sizeof(msgType)); int toClientID = player->clientID; ClientSocket1->Socket->SendBuf(&toClientID, sizeof(toClientID)); char *message = Edit1->Text.c_str(); int len = strlen(message); ClientSocket1->Socket->SendBuf(&len, sizeof(len)); ClientSocket1->Socket->SendBuf(message, len); Player *me = players[clientID]; if (me) ListBoxChat->Items->Add(me->nick + ": " + Edit1->Text); stream->Write(&msgType, sizeof(msgType)); int fromClientID = msg->first; client->stream->Write(&fromClientID, sizeof(fromClientID)); char *message = msg->second.c_str(); int len = strlen(message); client->stream->Write(&len, sizeof(len)); client->stream->Write(message, len); Структура чат сообщения: mtChatMessage, toClientID, len, message Пересылка сообщения напрямую в сокет: MessageType msgType = mtChatMessage; ClientSocket1->Socket->SendBuf(&msgType, sizeof(msgType)); int toClientID = player->clientID; ClientSocket1->Socket->SendBuf(&toClientID, sizeof(toClientID)); char *message = Edit1->Text.c_str(); int len = strlen(message); ClientSocket1->Socket->SendBuf(&len, sizeof(len)); ClientSocket1->Socket->SendBuf(message, len); Player *me = players[clientID]; if (me) ListBoxChat->Items->Add(me->nick + ": " + Edit1->Text);">
8 Алгоритм поиска пути в лабиринте. Метод Дейкстры. Типа поверхностей: enum Terrain { Water, Stone, Sand, Grass, Road }; UnitGroundUnitWarrior Коэффициенты типов поверхностей (степени проходимости) для юнита-воина: int GroundUnit::terrainWeight(Terrain terrain) { switch (terrain) { case Water: return 999; case Stone: return 999; case Sand: return 8; case Grass: return 4; case Road: return 1; default: return 999; }
9 Возникшие трудности при реализации проекта Сложный выбор: TCPServerSocket основанный на событиях (Events), либо создание потоков (Threads) к каждому клиенту Режимы работы сокета: 1) Неблокирующий (ctNonBlocking) Работают события (Events), но не гарантируется считывание такого количества данных, которое нам нужно SendBuf() отошлёт сообщение в сокет. ReceiveBuf() вернёт нам ровно столько данных, сколько пришло, а не столько, сколько мы ожидаем. Получении 50 байт из 100, оставшиеся 50 байт нужно куда-то присоединить, чтобы при приходе следующих 50 «вспомнить» про первые 50 и составить из них сообщение. Причём, если во второй раз придёт не 50, а 40, то опять их присоединить, и ждать оставшиеся 10. 2) Блокирующий (ctBlocking) События (Events) не работают, но сообщения считыватся гарантированно. Неблокирующий сокет реализовывается проще, но в условиях реальной сети сообщения передаёт плохо.
10 Планы на будущее 1)Перевод серверной части с TCPClientServer на UDPClientServer. Сделать сервер высоконагрузочным 2)Реализация взаимодействия юнитов (шкала здоровья и урон) 3)Ведение статистики по каждому клиенту 4)Подключение SMS-Шлюза (SMS-Gate) 5)Окно характеристик персонажа 6)Неигровых персонажей, мобов, опыт (expirience) юнитов
11 Заключение Выполнены все цели поставленные для написания клиент- серверной системы, которая передаёт данные от клиента к серверу и от клиента до клиента с обработкой на сервере. 1) Полностью освоена и реализована клиент-серверная технология. 2) Реализован и освоен алгоритм поиска пути по методу Дейкстры. 3) Реализована интеграция базы данных в виде функционала «авторизация при входе». 4) Построен полностью рабочий экспериментальный билд проекта с возможностью его модификации и расширения.
Еще похожие презентации в нашем архиве:
© 2024 MyShared Inc.
All rights reserved.