PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/neko/net/servers/RealtimeServer.hx

http://caffeine-hx.googlecode.com/
Haxe | 290 lines | 232 code | 29 blank | 29 comment | 33 complexity | f66a07323a760ef10f81c3225903623c MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, BSD-3-Clause, LGPL-2.1
  1. /* ************************************************************************ */
  2. /* */
  3. /* haXe Video */
  4. /* Copyright (c)2007 Nicolas Cannasse */
  5. /* */
  6. /* This library is free software; you can redistribute it and/or */
  7. /* modify it under the terms of the GNU Lesser General Public */
  8. /* License as published by the Free Software Foundation; either */
  9. /* version 2.1 of the License, or (at your option) any later version. */
  10. /* */
  11. /* This library is distributed in the hope that it will be useful, */
  12. /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
  13. /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
  14. /* Lesser General Public License or the LICENSE file for more details. */
  15. /* */
  16. /* ************************************************************************ */
  17. package neko.net.servers;
  18. import neko.net.Socket;
  19. //import neko.net.UdpReliableSocket;
  20. private typedef ThreadInfos<SockType> = {
  21. var t : neko.vm.Thread;
  22. var socks : Array<SockType>;
  23. var wsocks : Array<SockType>;
  24. var sleeps : Array<{ s : SockType, time : Float }>;
  25. }
  26. typedef SocketInfos<SockType,Client> = {
  27. var sock : SockType;
  28. var handle : SocketHandle;
  29. var client : Client;
  30. var thread : ThreadInfos<SockType>;
  31. var wbuffer : String;
  32. var wbytes : Int;
  33. var rbuffer : String;
  34. var rbytes : Int;
  35. }
  36. /**
  37. This is an abstract base server. To create a server, use classes like
  38. TcpRealtimeServer
  39. **/
  40. class RealtimeServer<SockType : neko.net.Socket, Client> {
  41. public var config : {
  42. listenValue : Int,
  43. connectLag : Float,
  44. minReadBufferSize : Int,
  45. maxReadBufferSize : Int,
  46. writeBufferSize : Int,
  47. blockingBytes : Int,
  48. messageHeaderSize : Int,
  49. threadsCount : Int,
  50. };
  51. public var shutdown : Bool;
  52. var sock : SockType;
  53. var threads : Array<ThreadInfos<SockType>>;
  54. var select_function : Dynamic;
  55. public function new() {
  56. threads = new Array();
  57. config = {
  58. listenValue : 10,
  59. connectLag : 0.05,
  60. minReadBufferSize : 1 << 10, // 1 KB
  61. maxReadBufferSize : 1 << 16, // 64 KB
  62. writeBufferSize : 1 << 18, // 256 KB
  63. blockingBytes : 1 << 17, // 128 KB
  64. messageHeaderSize : 1,
  65. threadsCount : 10,
  66. };
  67. shutdown = false;
  68. }
  69. function createSock() : SockType {
  70. throw "createSock must be implemented";
  71. return null;
  72. }
  73. public function bind( host : neko.net.Host, port : Int ) {
  74. sock = createSock();
  75. sock.bind(host,port);
  76. sock.listen(config.listenValue);
  77. }
  78. function logError( e : Dynamic ) {
  79. var stack = haxe.Stack.exceptionStack();
  80. var str = "["+Date.now().toString()+"] "+(try Std.string(e) catch( e : Dynamic ) "???");
  81. neko.Lib.print(str+"\n"+haxe.Stack.toString(stack));
  82. }
  83. function cleanup( t : ThreadInfos<SockType>, s : SockType ) {
  84. if( !t.socks.remove(s) )
  85. return;
  86. try s.close() catch( e : Dynamic ) { };
  87. t.wsocks.remove(s);
  88. var i = 0;
  89. while( i < t.sleeps.length )
  90. if( t.sleeps[i].s == s )
  91. t.sleeps.splice(i,1);
  92. else
  93. i++;
  94. try {
  95. clientDisconnected(getInfos(s).client);
  96. } catch( e : Dynamic ) {
  97. logError(e);
  98. }
  99. }
  100. function readWriteThread( t : ThreadInfos<SockType> ) {
  101. var socks : { write : Array<SockType>, read : Array<SockType>, others : Array<SockType>};
  102. socks = select_function(t.socks,t.wsocks,null,config.connectLag);
  103. for( s in socks.read ) {
  104. var ok = try clientRead(getInfos(s)) catch( e : Dynamic ) { logError(e); false; };
  105. if( !ok ) {
  106. socks.write.remove(s);
  107. cleanup(t,s);
  108. }
  109. }
  110. for( s in socks.write ) {
  111. var ok = try clientWrite(getInfos(s)) catch( e : Dynamic ) { logError(e); false; };
  112. if( !ok )
  113. cleanup(t,s);
  114. }
  115. }
  116. function loopThread( t : ThreadInfos<SockType> ) {
  117. var now = neko.Sys.time();
  118. var i = 0;
  119. while( i < t.sleeps.length ) {
  120. var s = t.sleeps[i];
  121. if( s.time <= now ) {
  122. t.sleeps.splice(i,1);
  123. clientWakeUp(getInfos(s.s).client);
  124. } else
  125. i++;
  126. }
  127. if( t.socks.length > 0 )
  128. readWriteThread(t);
  129. while( true ) {
  130. var m : { s : SockType, cnx : Bool } = neko.vm.Thread.readMessage(t.socks.length == 0);
  131. if( m == null )
  132. break;
  133. if( m.cnx ) {
  134. t.socks.push(m.s);
  135. var inf = getInfos(m.s);
  136. inf.client = clientConnected(m.s);
  137. if( t.socks.length >= 64 ) {
  138. serverFull(inf.client);
  139. logError("Max clients per thread reached");
  140. cleanup(t,m.s);
  141. }
  142. } else {
  143. cleanup(t,m.s);
  144. }
  145. }
  146. }
  147. function runThread( t ) {
  148. while( true ) {
  149. try loopThread(t) catch( e : Dynamic ) logError(e);
  150. }
  151. }
  152. function initThread() {
  153. var t : ThreadInfos<SockType> = {
  154. t : null,
  155. socks : new Array(),
  156. wsocks : new Array(),
  157. sleeps : new Array(),
  158. };
  159. t.t = neko.vm.Thread.create(callback(runThread,t));
  160. return t;
  161. }
  162. function writeClientChar( c : SocketInfos<SockType,Client>, ch : Int ) {
  163. if( c.wbytes == 0 )
  164. c.thread.wsocks.push(c.sock);
  165. untyped __dollar__sset(c.wbuffer.__s,c.wbytes,ch);
  166. c.wbytes += 1;
  167. }
  168. function writeClientBytes( c : SocketInfos<SockType,Client>, buf : String, pos : Int, len : Int ) {
  169. if( len == 0 )
  170. return 0;
  171. if( c.wbytes == 0 )
  172. c.thread.wsocks.push(c.sock);
  173. neko.Lib.copyBytes(c.wbuffer,c.wbytes,buf,pos,len);
  174. c.wbytes += len;
  175. return len;
  176. }
  177. function addClient( s : SockType ) {
  178. throw "not implemented";
  179. }
  180. function getInfos( s : SockType ) : SocketInfos<SockType,Client> {
  181. return s.custom;
  182. }
  183. function clientWrite( c : SocketInfos<SockType,Client> ) : Bool {
  184. throw "not implemented";
  185. return false;
  186. }
  187. function clientRead( c : SocketInfos<SockType,Client> ) {
  188. var available = c.rbuffer.length - c.rbytes;
  189. if( available == 0 ) {
  190. var newsize = c.rbuffer.length * 2;
  191. if( newsize > config.maxReadBufferSize ) {
  192. newsize = config.maxReadBufferSize;
  193. if( c.rbuffer.length == config.maxReadBufferSize )
  194. throw "Max buffer size reached";
  195. }
  196. var newbuf = neko.Lib.makeString(newsize);
  197. neko.Lib.copyBytes(newbuf,0,c.rbuffer,0,c.rbytes);
  198. c.rbuffer = newbuf;
  199. available = newsize - c.rbytes;
  200. }
  201. try {
  202. c.rbytes += c.sock.input.readBytes(c.rbuffer,c.rbytes,available);
  203. } catch( e : Dynamic ) {
  204. if( !Std.is(e,neko.io.Eof) && !Std.is(e,neko.io.Error) )
  205. neko.Lib.rethrow(e);
  206. return false;
  207. }
  208. var pos = 0;
  209. while( c.rbytes >= config.messageHeaderSize ) {
  210. var m = readClientMessage(c.client,c.rbuffer,pos,c.rbytes);
  211. if( m == null )
  212. break;
  213. pos += m;
  214. c.rbytes -= m;
  215. }
  216. if( pos > 0 )
  217. neko.Lib.copyBytes(c.rbuffer,0,c.rbuffer,pos,c.rbytes);
  218. return true;
  219. }
  220. // ---------- API ----------------
  221. public function clientConnected( s : SockType ) : Client {
  222. return null;
  223. }
  224. public function readClientMessage( c : Client, buf : String, pos : Int, len : Int ) : Int {
  225. return null;
  226. }
  227. public function clientDisconnected( c : Client ) {
  228. }
  229. public function clientFillBuffer( c : Client ) {
  230. }
  231. public function clientWakeUp( c : Client ) {
  232. }
  233. public function isBlocking( s : SockType ) {
  234. return getInfos(s).wbytes > config.blockingBytes;
  235. }
  236. public function wakeUp( s : SockType, delay : Float ) {
  237. var inf = getInfos(s);
  238. var time = neko.Sys.time() + delay;
  239. var sl = inf.thread.sleeps;
  240. for( i in 0...sl.length )
  241. if( sl[i].time > time ) {
  242. sl.insert(i,{ s : s, time : time });
  243. return;
  244. }
  245. sl.push({ s : s, time : time });
  246. }
  247. /**
  248. Disconnect a client
  249. **/
  250. public function stopClient( s : SockType ) {
  251. var inf = getInfos(s);
  252. try s.shutdown(true,true) catch( e : Dynamic ) { };
  253. inf.thread.t.sendMessage({ s : s, cnx : false });
  254. }
  255. /**
  256. Called when the max number of clients per thread is reached,
  257. before the client is disconnected.
  258. **/
  259. public function serverFull( c : Client ) {
  260. }
  261. }