Тестовый сокет-сервер для Windows
Не секрет, что для разработки суперприкольных и невероятно-многопользовательских реалтайм игр с гениальной механикой, разработчикам клиентской части не хватает умений реализовать и оттестировать сервер.
Мне этого тоже не хватало.
Представляю вашему вниманию тестовый сервер, а вернее целых 2!!!
Использование:
1. Скачайте архив.k0.zip
2. Распакуйте (желательно в c:\)
3. Если хотите тестить в локалке, то подправьте 2 конфиг файла для сервера и 1 для клиента
что вообще тут:
первый сервер - сервер отдачи кроссдомена.
запуск: \server\bin\crossdomain\start.bat
конфиги: \server\bin\crossdomain\kaon.ini
вообще там желательно менять только hostaddr=127.0.0.1 на ip компа в локалке, если будете тестить в локальной сети, если нет, то менять не надо.
второй сервер - сервер-ресивер данных
что делает: создаёт коннекты и каждое сообщение от клиентов передаёт всем подписавшимся клиентам.
Никакого парсинга не делает, поэтому данные могут быть в любом формате, кроме содержащих /0
запуск: \server\bin\server\start.bat
конфиги: \server\bin\server\server.ini
сервера специально разделил, ибо просто отдача кроссдомена - тоже нужная штука.
клиент - простой чат.
исходники клиента
UPD исходники клиента даю здесь текстом. Лень вытаскивать из проекта:
главный класс:
package chat { /** * ... * @author k0t0vich */ public class Chat extends Sprite { private var urlLoader:URLLoader; private var configManager:ConfigManager; private var socketConnector:XMLSocketConnector = new XMLSocketConnector(); // форматы сокета //private var socketConnector:SocketConnector = new SocketConnector(); //private var socketConnector:ProtoBufConnector = new ProtoBufConnector(); // гуй чата private var chatInput:ChatInput; public function Chat() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event=null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // поле чата и сообщений. addChild(new Output()); chatInput = new ChatInput(); addChild(chatInput); chatInput.addEventListener(DataEvent.DATA, sendToChat); loadConfig(); } private function sendToChat(e:DataEvent):void { socketConnector.send(e.data); } public function loadConfig():void { urlLoader = new URLLoader(new URLRequest("config.xml")); urlLoader.addEventListener(Event.COMPLETE, onLoadConfig); } private function onLoadConfig(e:Event):void { configManager = new ConfigManager(XML(urlLoader.data)); socketConnector.init(configManager); socketConnector.addEventListener(DataEvent.DATA, onSocketData); socketConnector.connect(); } private function onSocketData(e:DataEvent):void { Output.trace("message > " + e.data); } } }
package server /** * ... * @author k0t0vich */ [Event(name="data", type="flash.events.DataEvent")] public class XMLSocketConnector extends EventDispatcher { private var configManager:ConfigManager; private var serverHost:String; private var serverPort:int; private var socket:XMLSocket; private var socketPolicyPort:int = 843; private var timer:Timer; private var session:String=null; public function XMLSocketConnector() { super(); } /** * * @param configManager */ public function init(configManager:ConfigManager):void { this.configManager = configManager; serverHost = configManager.serverHost; serverPort = int(configManager.serverPort); socketPolicyPort = serverPort; } public function connect():void { Output.trace("ServerConnector.connect"); System.useCodePage = true; //Security.loadPolicyFile("xmlsocket://"+serverHost + ":" + socketPolicyPort); timer = new Timer(10, 1); timer.addEventListener(TimerEvent.TIMER, tryConnect); timer.start(); } //--------------------------tryConnect--------------------------------------- private function tryConnect(e:TimerEvent=null):void { Output.trace("ServerConnector.tryConnect > e : " + e); timer.removeEventListener(TimerEvent.TIMER, tryConnect); socket = new XMLSocket(); socket.addEventListener(Event.CONNECT, onTryConnect); socket.addEventListener(DataEvent.DATA, onTryData); socket.addEventListener(Event.CLOSE, onTryClose); if (serverHost && serverPort) { socket.connect(serverHost, socketPolicyPort); } } private function onTryConnect(e:Event):void { socket.removeEventListener(Event.CONNECT, onTryConnect); // посылаем первый пинг send("ping"); } /** * Хэндлер политики безопасности * Запускаем основной коннект * @param e */ private function onTryClose(e:Event=null):void { Output.trace("ServerConnector.onTryClose > e : " + e); socket.removeEventListener(Event.CLOSE, onTryClose); socket.removeEventListener(DataEvent.DATA, onTryData); socket.removeEventListener(Event.CONNECT, onTryConnect); baseConnect(); } private function onTryData(e:DataEvent):void { Output.trace("ServerConnector.onTryData > e : " + e); socket.close(); onTryClose(); } //--------------------------baseConnect--------------------------------------- private function baseConnect():void { Output.trace("ServerConnector.baseConnect"); configureListeners(socket); socket.connect(serverHost, serverPort); } /** * Посылка данных * @param data */ public function send(data:*):void { socket.send(data); } //----------------socket handlers--------------------------------------- private function configureListeners(dispatcher:IEventDispatcher):void { dispatcher.addEventListener(Event.CLOSE, closeHandler); dispatcher.addEventListener(Event.CONNECT, connectHandler); dispatcher.addEventListener(DataEvent.DATA, dataHandler); dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler); dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); } private function closeHandler(event:Event):void { Output.trace("closeHandler: " + event); // пробуем коннект после прихода политик } private function connectHandler(event:Event):void { Output.trace("connectHandler: " + event); } private function ioErrorHandler(event:IOErrorEvent):void { Output.trace("ioErrorHandler: " + event); } private function progressHandler(event:ProgressEvent):void { Output.trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal); } private function securityErrorHandler(event:SecurityErrorEvent):void { Output.trace("securityErrorHandler: " + event); } /** * Хэндлер сообщений * @param event */ private function dataHandler(event:DataEvent):void { //Output.trace("dataHandler: " + event); dispatchEvent(new DataEvent(DataEvent.DATA, false, false, event.data)); } //----------------socket handlers--------------------------------------- } }
Пример "прослойки" скоро будет, если у меня не будет авралов. Пожелайте мне удачи)
Всего комментариев 11
Комментарии
![]() ![]() |
|
Я так понимаю, что ничего ставить не нужно, для того, чтобы это работало?
|
![]() ![]() |
|
Нет. Просто распаковать. Нужные dll лежат рядом.
Если возникут какие нибудь проблемы, просьба описать. |
|
Обновил(-а) Котяра 05.12.2010 в 19:21
|
![]() ![]() |
|
Супер!
|
![]() ![]() |
|
Вообще это обрезанная и адаптированная под винду часть сервера на плюсах. Нагрузочные тесты показали хорошие результаты.
Кроме того это часть глобальной идеи которая перевернёт мир серверов для игр и вытеснит SmartFox)))) Идея в том что между приёмом и отправкой можно отправлять данные другому серверу, который будет их парсить. вместо клиент1- > сервер1- > клиент1... клиентN будет клиент1- > сервер1- >логика-> сервер1-> клиент1... клиентN прослойку логики подключаем на некий порт/адрес, который может быть вообще на другом сервере и написан на любом я зыке. Кстати, аналогичный функционал на Эрланге проиграл по тестам(тесты конечно были узконаправленными на конкретную ситуацию). Си - быстрее. Но именно Эрланг сподвиг меня на такое архитектурное решение. Мне понравилась идея отказа от ссылок. События и сообщения - наше всё. Даже сейчас можно прослойку логики реализовать на as. Т.е. это будет некий серверный клиент. Пример выложу, если дойдут руки. По мне очень удобно; пример: Я описал логику, оттестировал и предоставил исходники серверному программисту. Он просто перевёл её на плюсы и завёл нужные данные в БД. Изврат: Я запускаю 2 сервера на локалке (кроссдомен, и сервер) затем запускаю псевдосервер.swf (слой логика) затем подключаются клиенты. Псевдосервер читает шареды(псевдо БД) и рассылает авторизованным клиентам их данные, потом он фильтрует данные от реального сервера и отсылает откорректированныесвои. Клиенты хотя и принимают все данные, парсят только с пометкой ("от псевдосервера") На релизе, конечно это глупость, но для тестов офигенно. В реале - настоящий сервер не будет посылать эхо всем клиентам, а только сообщения от логики. Я мог бы и в тестовый сервер внести такую фичу, но это сразу подразумевает парсинг сообщений сервером, что мне совсем не подходит (вернее не подходит под условия задачи: сделать тестовый сервер для любых протоколов). |
|
Обновил(-а) Котяра 06.12.2010 в 03:04
|
![]() ![]() |
|
Здорово!
|
![]() ![]() |
|
Неплохо бы сделать пример работы с прослойкой на as.
|
![]() ![]() |
|
Ну, вот, бубен таки нашелся
![]() |
![]() ![]() |
|
2 Котяра:
Неплохо. Полезно. |
![]() ![]() |
|
Ну вы можете запустить сервер у себя на машине если есть выделенный ip.
А так простой виртуальный сервер за 5 баксов в месяц вряд ли подойдёт. можно и за 100$ взять, можно и дешевле. |
![]() ![]() |
|
а где взять ConfigManager? без него можно обойтись?
|
Последние записи от Котяра
- Страх и ненависть в Нью-Дели или сборка мультипака для arm7 и x86 c Adobe AIR 14 в FB (16.06.2014)
- Нативный EventDispatcher в старлинге (27.11.2013)
- Нужны ошибки компиляции при создании экземпляра синглетона извне? Запросто! (13.09.2013)
- ARP - новый формат упаковки ресурсов (07.02.2013)
- DropShadowFilter и GlowFilter в Starling (16.01.2013)