diff --git a/uppsrc/Turtle/Event.cpp b/uppsrc/Turtle/Event.cpp index 414e86300..885a7784e 100644 --- a/uppsrc/Turtle/Event.cpp +++ b/uppsrc/Turtle/Event.cpp @@ -97,51 +97,56 @@ bool TurtleServer::ProcessEvent(bool *quit) void TurtleServer::WaitEvent(int ms) { websocket.Do(); - socket.Timeout(ms).WaitRead(); - socket.Timeout(0); + SocketWaitEvent we; + websocket.AddTo(we); + we.Wait(ms); } bool TurtleServer::IsWaitingEvent() { websocket.Do(); - for(;;) { - String s = websocket.Receive(); - if(websocket.IsClosed()) { - Ctrl::EndSession(); - sQuit = true; // Ugly... - return false; - } - if(s.GetCount() == 0) - break; - LLOG("Received data " << s); - StringStream ss(s); - while(!ss.IsEof()) { - String s = ss.GetLine(); - CParser p(s); - try - { - if(p.Id("S")) { - uint32 l = p.ReadNumber(); - uint32 h = p.ReadNumber(); - recieved_update_serial = MAKEQWORD(l, h); - stat_client_ms = p.ReadNumber(); - } - else - sEventQueue.AddTail(s); - } - catch(const CParser::Error& e) - { - LLOG("IsWaitingEvent() -> Parser error. " << e); + + String s = websocket.Receive(); + + if(websocket.IsClosed()) { + Ctrl::EndSession(); + sQuit = true; // Ugly.. + return false; + } + + if(s.GetCount() == 0) + return sEventQueue.GetCount(); + + LLOG("Received data " << s); + + StringStream ss(s); + while(!ss.IsEof()) { + String s = ss.GetLine(); + CParser p(s); + try + { + if(p.Id("S")) { + uint32 l = p.ReadNumber(); + uint32 h = p.ReadNumber(); + recieved_update_serial = MAKEQWORD(l, h); + stat_client_ms = p.ReadNumber(); } + else + sEventQueue.AddTail(s); } - if(recieved_update_serial == serial_0) { - serial_0 = 0; - stat_roundtrip_ms = msecs() - serial_time0; - serial_time0 = Null; + catch(const CParser::Error& e) + { + LLOG("IsWaitingEvent() -> Parser error. " << e); } } - if(socket.IsError()) - LLOG("ERROR: " << socket.GetErrorDesc()); + if(recieved_update_serial == serial_0) { + serial_0 = 0; + stat_roundtrip_ms = msecs() - serial_time0; + serial_time0 = Null; + } + + if(websocket.IsError()) + LLOG("ERROR: " << websocket.GetErrorDesc()); return sEventQueue.GetCount(); } diff --git a/uppsrc/Turtle/Init.cpp b/uppsrc/Turtle/Init.cpp index 5e880be9f..f624e051a 100644 --- a/uppsrc/Turtle/Init.cpp +++ b/uppsrc/Turtle/Init.cpp @@ -10,7 +10,6 @@ int TurtleServer::port = 8888; String TurtleServer::ip = "0.0.0.0"; int TurtleServer::connection_limit = 100; bool TurtleServer::debugmode; -TcpSocket TurtleServer::socket; WebSocket TurtleServer::websocket; int TurtleServer::mainpid; bool TurtleServer::quit; diff --git a/uppsrc/Turtle/Server.cpp b/uppsrc/Turtle/Server.cpp index 0a0c60d46..c7eb061c1 100644 --- a/uppsrc/Turtle/Server.cpp +++ b/uppsrc/Turtle/Server.cpp @@ -11,10 +11,46 @@ namespace Upp { -static Vector sProcessIds; +static Vector sChildPids; + +static bool sSendTurtleHtml(TcpSocket& s, const String& host, int port) +{ + HttpHeader h; + if(!h.Read(s)) + return false; + LLOG("Sending Turtle HTML"); + String html = String(turtle_html, turtle_html_length); + html.Replace("%%host%%", Format("ws://%s:%d", host, port)); + return HttpResponse(s, h.scgi, 200, "OK", "text/html", html); +} + +static void sUpdateChildList() +{ +#ifdef PLATFORM_POSIX + int i = 0; + while(i < sChildPids.GetCount()) { + if(sChildPids[i] && waitpid(sChildPids[i], 0, WNOHANG | WUNTRACED) > 0) { + TurtleServer::WhenTerminate(sChildPids[i]); + sChildPids.Remove(i); + } + else ++i; + } +#endif +} + +void TurtleServer::Broadcast(int signal) +{ +#ifdef PLATFORM_POSIX + if(getpid() == mainpid) + for(int i = 0; i < sChildPids.GetCount(); i++) + kill(sChildPids[i], signal); +#endif +} bool TurtleServer::StartSession() { + // TODO: See if we can add secure websocket (wss://) support. + LLOG("Connect"); #ifdef PLATFORM_POSIX @@ -27,11 +63,9 @@ bool TurtleServer::StartSession() TcpSocket server; #ifdef _DEBUG - int qq = 0; - for(;;) { - if(server.Listen(ipinfo, port, 5, false, true)) - break; - Cout() << "Trying to start listening (other process using the same port?) " << ++qq << "\n"; + int cnt = 0; + while(!server.Listen(ipinfo, port, 5, false, true)) { + LLOG("Trying to start listening (other process using the same port?) " << ++cnt); Sleep(1000); } #else @@ -42,67 +76,41 @@ bool TurtleServer::StartSession() #endif LLOG("Starting to listen on " << port << ", pid: " << getpid()); - socket.Timeout(0); // TODO: Not quite ideal way to make quit work.. + for(;;) { -#ifdef PLATFORM_POSIX - int i = 0; - while(i < sProcessIds.GetCount()) - if(sProcessIds[i] && waitpid(sProcessIds[i], 0, WNOHANG | WUNTRACED) > 0) { - WhenTerminate(sProcessIds[i]); - sProcessIds.Remove(i); - } - else - i++; -#endif + sUpdateChildList(); if(server.IsError()) server.ClearError(); - if(socket.Accept(server)) { - HttpHeader http; - if(http.Read(socket)) { - RLOG("Accepted, header read"); - if(websocket.WebAccept(socket, http)) { // TODO: Connection limit, info - if(sProcessIds.GetCount() >= connection_limit) { - socket.Close(); - continue; - } + TcpSocket socket; + if(!socket.Accept(server)) + continue; + if(!sSendTurtleHtml(socket, host, port)) + continue; + websocket.NonBlocking(); + while(!websocket.Accept(server)) + Sleep(20); + LLOG("Websocket connection accepted. IP: " << websocket.GetPeerAddr()); #ifdef PLATFORM_POSIX - if(debugmode) - break; - int newpid = fork(); - if(newpid == 0) - break; - else { - sProcessIds.Add(newpid); - WhenConnect(newpid, socket.GetPeerAddr()); - socket.Close(); - continue; - } -#else - break; -#endif - } - RLOG("Sending HTML"); - String html = String(turtle_html, turtle_html_length); - html.Replace("%%host%%", "ws://" + Nvl(GetIniKey("turtle_host"), Nvl(host, "localhost")) + ":" + AsString(port)); - HttpResponse(socket, http.scgi, 200, "OK", "text/html", html); - } - socket.Close(); + if(sChildPids.GetCount() >= connection_limit) + continue; + if(debugmode) + break; + int newpid = fork(); + if(!newpid) + break; + else { + LLOG("Process forked. Pid: " << newpid); + sChildPids.Add(newpid); + WhenConnect(newpid, websocket.GetPeerAddr()); + continue; } +#else + break; +#endif } - RLOG("Connection established with " << socket.GetPeerAddr() << ", pid: " << getpid()); + server.Close(); - if(socket.IsError()) - LLOG("CONNECT ERROR: " << socket.GetErrorDesc()); stat_started = GetSysTime(); return true; } - -void TurtleServer::Broadcast(int signal) -{ -#ifdef PLATFORM_POSIX - if(getpid() == mainpid) - for(int i = 0; i < sProcessIds.GetCount(); i++) - kill(sProcessIds[i], signal); -#endif -} } diff --git a/uppsrc/Turtle/Stream.cpp b/uppsrc/Turtle/Stream.cpp index 177380901..6d7ce6b30 100644 --- a/uppsrc/Turtle/Stream.cpp +++ b/uppsrc/Turtle/Stream.cpp @@ -103,4 +103,3 @@ void TurtleServer::Flush() websocket.SendBinary(s); } } - diff --git a/uppsrc/Turtle/Turtle.h b/uppsrc/Turtle/Turtle.h index 436707d22..f33b7800e 100644 --- a/uppsrc/Turtle/Turtle.h +++ b/uppsrc/Turtle/Turtle.h @@ -130,7 +130,6 @@ private: static void ResetImageCache(); private: - static TcpSocket socket; static WebSocket websocket; static dword mousebuttons; static dword modifierkeys; diff --git a/uppsrc/Turtle/issues b/uppsrc/Turtle/issues index dbb77c3e2..af7760dfe 100644 --- a/uppsrc/Turtle/issues +++ b/uppsrc/Turtle/issues @@ -12,7 +12,5 @@ Known Issues 3) The VirtualGui-based Turtle package inherits the problems of the old Turtle package. - a) One such major problem is that on windows platform, closing the remote app throws an - exception on the server side. This requires further investigation. - - b) The other one is high cpu usage even on idle state. + One such major problem is that on windows platform, closing the remote app throws an + exception on the server side. This requires further investigation.