Jump to content
plastkort

A proper way to set up a listening socket (WebHook)

Recommended Posts

hi!

 

I have been using TWSocket erarlier to create listening sockets, but this is several years back, now i have a case where i need to make it work again.

 

I got it to listen, but it eventually dies after some time, it could be 1 hour or  1 day.

 

I do the following: 

 

First i create the listening socket: 

 wsTradingWebHook.addr:='0.0.0.0';

 wsTradingWebHook.port:='80';
 wsTradingWebHook.Listen;

 

 

When incoming I do the following:

 

procedure TDataFunctions.wsTradingWebHookSessionAvailable(Sender: TObject;
  ErrCode: Word);
var
  html : TStringlist;
  FMySocket : TWSocket;
begin
  FMySocket := Twsocket.create(self);
  html  := TStringlist.Create;
  with FMySocket do
  try
    OnDataAvailable := wsTradingWebHookDataAvailable;

    HSocket := twsocket(sender).Accept;

    html.add('<html><head>');
    html.add('<title>Welcome to WhaleAlert</title>');
    html.add('</head><body>');
    html.add('Usage: <br>');
    html.add('{  "signal": "short",  "message": "short signal on 5 minutes" } <br>');
    html.add('{  "signal": "long",  "message": "long signal on 5 minutes" } <br>');
    html.add('</body></html>

    sendstr('HTTP/1.1 200 OK'#13#10);
    sendstr(format('Date: %s GMT+2'#13#10,[formatdatetime('ddd, dd mmm yyyy hh:mm:ss', date)]));
    sendstr('Server; myserver'#13#10);
    sendstr('Content-Type: text/html; charset=iso-8859-1'#13#10);
    sendstr(format('Content-length=%d'#13#10,[length(html.Text)]));
    sendstr(#13#10);
    sendstr(html.Text);
    sendstr(#13#10);
  finally
    freeAndNil(html);
  end;
end;

 

When I receive data from the server i handle it like this: ( I just want the json data received, so any data prior to the { and } will be deleted)

 

I am unsure if i need the "TWSocket(sender).Free;" is needed, or the socket will just clean itself, if i try connect with a normal webserver i receive the "info" message, but the client still seem to wait for more data...i.e. the waiting circle keeps spinning...

 

function TDataFunctions.ProcessWebHook(Received : string) : string;
var
  PostCommand : string;
begin
  result := '';
  if pos('POST',received) <> 0 then
  begin
    PostCommand := received;
    if (pos('{',PostCommand) <> 0) and
       (pos('}',PostCommand) <> 0) then
    begin
      Delete(postcommand, 1, pred(pos('{',PostCommand)));
      Result := PostCommand;
    end;
  end;

end;

 

procedure TDataFunctions.wsTradingWebHookDataAvailable(Sender: TObject;
  ErrCode: Word);
var
  ProcessedCommand : string;
  Signal : string;
  TrayIcon  : TRzTrayIcon;

  UserData : ISuperObject;
  ReceivedJSON : AnsiString;
begin
  try
    ProcessedCommand := ProcessWebHook(AnsiString(TSslWSocket(Sender).ReceiveStr));
    if (trim(ProcessedCommand) <> '') then
    try
      TrayIcon := frmMain.MyTrayicon;
      UserData := SO(ProcessedCommand);
      if userdata.s['signal'] = 'short' then
      begin
        Signal := userdata.s['message'];
        AddSignal(signal, false);
      end;

      if userdata.s['signal'] = 'long' then
      begin
        Signal := userdata.s['message'];
        AddSignal(signal, true);
      end;
    finally
      TWSocket(sender).Free;
    end;

    except
      AddSignal(ProcessedCommand, false,true);
    end;
end;

Share this post


Link to post

It looks like you are rewriting HTTP protocol. Why don't you use the HTTP server component. It does everything for you. See sample programs containing HTTP server component.

 

Share this post


Link to post

I looked at it, and it seems it needed a HTML folder, I just need to answer a few lines.

 

this works for me. for a short time. but the listening socket seems to die 

Share this post


Link to post

HTTP protocol is more complex than you think...

 

There is also THttpAppSrv component and his demo application OverbyteIcsWebAppServer.

THttpAppSrv is more oriented to answer with "computed" responses.

 

Share this post


Link to post
1 hour ago, FPiette said:

HTTP protocol is more complex than you think...

 

There is also THttpAppSrv component and his demo application OverbyteIcsWebAppServer.

THttpAppSrv is more oriented to answer with "computed" responses.

 

 

That one might do the trick, but i hope it can be simplified. I just require it to receive a POST message, and just reply a simple instruction page on the default.

 

but I would also appreciate if you have any answer to why my listening socket just stops listening after a while, im concerned that this happens on other projects i might be planning to do in the future. are there any sample codes around I can take a look at so i know that i do everything right ?

 

 

 

Share this post


Link to post

hmm.. it seems this appserver will overcomplicate my simple project.. I did however notice something in your sample files called:

OverbyteIcsSrv5

 

i use     "FNewSocket := _TWSocket(sender).Accept;"

 

but i did not apply the FnewSocket := TWSocket(sender).dup

 

is this a very important part i was missing ?

 

also i have added :

ShutDown(2); 
Close;       

 

and will see if this allows my server to live longer, note, it's only the listening socket that died earlier, and not the rest of my program. however im not sure if it just stopped listen or something else messed it up

 

 

Share this post


Link to post

 

Quote

 

is this a very important part i was missing ?


 

Look at TWSocketServer if you want something basic able to listen for client and instanciate new TWSocket for each client. It doesn't do anything special related to HTTP protocol which is handled by THttpSrv component. As you can see there is a full hierarchy of classes each one being more intelligent. TWSocket is below everything, TWSocketServer add support for server features above TWSocket. THttpServer add HTTP protocol support (server side) to TWSocketServer. THttpAppSrv add REST application layer to THttpServer. In this hierarchy, there are some more intermediate classes for "custom", "Line mode", "proxy", "SSL/TLS" and more.

  • Thanks 1

Share this post


Link to post

As François says, you should be using TWSocketServer which handles aspects of accepting and closing connections, you just plug in your existing onDataAvailable code. Also makes it easier to move to SSL in the future.

 

If you are using the HTTP protocol, there is a new component TSimpleWebSrv in OverbyteIcsSslHttpRest,pas which uses TWSocketServer and adds a simple HTTP protocol, just a few lines of code, ICS uses it for lightweight HTTP servers for OAuth2 and Lets Encrypt. 

 

Angus

  • Like 1

Share this post


Link to post

I can take a look at TWSocketServer, I think i used it before, but maybe 6-7 years ago.

 

As a sidenote, i think the shutdown(2) and close did the trick i think. program still accepting connections after 2 days.

 

 

 

 

 

Share this post


Link to post

Years ago I had TWSocketServers running and accepting thousands of short connections a day for a few months at a tine, had to workaround the 49 day GetTickCount wrap around.  Now my servers never run for more than 35 days, due to Microsoft being more aggressive with Windows Update and forcing reboots. 

 

Angus

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×