Turtle: Server side improvements, reduced CPU load on idle state, and cleanup.

git-svn-id: svn://ultimatepp.org/upp/trunk@15007 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
oblivion 2020-09-10 12:08:58 +00:00
parent 501da534a1
commit 06b8ae8869
6 changed files with 110 additions and 102 deletions

View file

@ -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();
}

View file

@ -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;

View file

@ -11,10 +11,46 @@
namespace Upp {
static Vector<int> sProcessIds;
static Vector<int> 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
}
}

View file

@ -103,4 +103,3 @@ void TurtleServer::Flush()
websocket.SendBinary(s);
}
}

View file

@ -130,7 +130,6 @@ private:
static void ResetImageCache();
private:
static TcpSocket socket;
static WebSocket websocket;
static dword mousebuttons;
static dword modifierkeys;

View file

@ -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.