Jump to content
AJ_Oldendorf

TCP Server + Client + Threaded and Bytes

Recommended Posts

Hello together,

actual I use nsoftware with TCPIP.

I have a application as Server and one application as client.

Clients can be in network more than one pc (maximum 15 different pcs with one client application).

My server wait for a connect from one client and when client don't disconnect they send everytime data to the client.

The transfer rate from server to client is ~80-90 different messages per second with different length (from 20 bytes to 60000 bytes) to each client as TBytes with 8 fix bytes at every end as EndOfLine terminator.

Client receive data as TBytes with information if EndOfLine is True or not. If not, i collect the data in DataIn-event of client till EndOfLine is received. Than I know the complete message block.

The transfer rate from client to server is ~20 different messages per second with different length (from 20 bytes to 60000 bytes) to server as TBytes with 8 fix bytes at every end as EndOfLine terminator.

Server receive data as TBytes with information if EndOfLine is True or not. If not, i collect the data in DataIn-event of server till EndOfLine is received. Than I know the complete message block.

 

This works with nsoftware ^^

 

Now I'm thinking about to change to ICS and have some questions.

 

At first, is my above-mentioned task possible with ICS?

In demo application I saw only, that Strings are send and receive and EndOfLine character is a String too.

Is it possible to do all with TBytes and if yes, is there a demo application for server and client to take a look at this?

 

All I want to program in background thread, not in main GUI :-)

Share this post


Link to post

ICS sends binary data at the lowest level, it has methods to send strings and TBytes to make life easier for the developer.  You need to be more cautious receive, in case of disconnection when your packet end is lost. 

 

I'd recommended building the OverbyteIcsIpStmLogTst sample, it uses the TIcsIpStrmLog component that is designed for simple data transfer like yours, it can be configured as a client or server to send and receive packets, the sample should interwork with your existing applications.   TIcsIpStrmLog saves a lot of code over using the lower level components, should be working in a day.

 

Angus

 

 

 

Share this post


Link to post

This demo look likes UDP, yes?

I want to use TCPIP, so packages will transfered without loosing (if no connection dropped) and repeat in case of trouble.

 

Actual I send the complete length of my message at the beginning of each message and receiver can check when EndOfLine is True, if collected input message length is the same like sender has set in message data. If true, I accept the incomming message and works with it.

 

The OverbyteIcsIpStmLogTst  demo looks like a file transfer.

In my opinion the demo OverbyteIcsThrdSrv looks like better but I have the problem, to understand how to transfer TBytes (ok, I saw the procedure SendTB with parameter TBytes) but how can I set the LineEnd to fix 8bytes and get a information in receive event, that EndOfLine is reached or not?

Share this post


Link to post

The TIcsIpStrmLog component can be configured for TCP or UDP.  

 

It can do file transfers, but has SendLogLine (const Line: TBytes) which is exactly what you requested.  For clients, the component handles connection retries if/when the connection drops. 

 

Reading your message again, the record end of eight bytes is not handled, only a single symbol or CRLF, but you could easily adapt the component receive loop to check for a longer end of record. 

 

OverbyteIcsThrdSrv is a very old server sample and has not been tested for many years.  Try OverbyteIcsSimpleSslServer, inSslWSocketServer1ClientConnect set a long LimieLimit and LineEnd to your six bytes.  Ignore the SSL stuff. 

 

Angus

 

 

Share this post


Link to post

I take a look to  OverbyteIcsSimpleSslServer and changed following:

 

ClientDataAvailable

found := False;
    tmpStr := ReceiveStr;

    tmpRcvdBytes := IcsStringToTBytes(tmpStr);
    EOL := False;

    for i := Low(tmpRcvdBytes) to High(tmpRcvdBytes) do
    begin
      if Found then
        Break;
      found := False;
      for j := Low(DefEOLB) to High(DefEOLB) do
      begin
        if tmpRcvdBytes[i] = DefEOLB[j] then
        begin
          found := True;
        end
        else
          found := False;

        if (j = High(DefEOLB)) and found then
        begin
          FoundPos := i;
          EOL := True;
          Break;
        end;
      end;
    end;

    if EOL then
    begin
      if Length(LastServerFracInputBytes) > 0 then
      begin
        RcvdBytes := LastServerFracInputBytes + tmpRcvdBytes;
        SetLength(LastServerFracInputBytes, 0);
      end
      else
      begin
        RcvdBytes := tmpRcvdBytes;
      end;

      Display('Received from ' + GetPeerAddr + ': ', RcvdBytes);
      ProcessData(Sender as TTcpSrvClient);
    end
    else
    begin
      LastServerFracInputBytes := LastServerFracInputBytes + tmpRcvdBytes;
    end;
LastServerFracInputBytes : TBytes;
DefEOLB      : TBytes;

are global variables in form.

 

FormCreate

SetLength(LastServerFracInputBytes, 0);

  SetLength(DefEOLB, 8);
  FillChar(DefEOLB[0], 1, 65);
  FillChar(DefEOLB[1], 1, 65);
  FillChar(DefEOLB[2], 1, 66);
  FillChar(DefEOLB[3], 1, 66);
  FillChar(DefEOLB[4], 1, 67);
  FillChar(DefEOLB[5], 1, 67);
  FillChar(DefEOLB[6], 1, 68);
  FillChar(DefEOLB[7], 1, 68);

StartButton

 

SslWSocketServer1.Proto          := 'tcp';
SslWSocketServer1.Addr           := '0.0.0.0'; // Use any interface
SslWSocketServer1.Port           := PortEdit.Text;
SslWSocketServer1.SslEnable      := FALSE;
SslContext1.SslCertFile          := CertFileEdit.Text;
SslContext1.SslPassPhrase        := PassPhraseEdit.Text;
SslContext1.SslPrivKeyFile       := PrivKeyFileEdit.Text;
SslContext1.SslCAFile            := CAFileEdit.Text;
SslContext1.SslCAPath            := CAPathEdit.Text;
SslContext1.SslVerifyPeer        := VerifyPeerCheckBox.Checked;
//SslWSocketServer1.SetAcceptableHostsList(AcceptableHostsEdit.Text);
SslWSocketServer1.Listen;
SslWSocketServer1.ClientClass    := TTcpSrvClient; // Use our component
Display('Listenning...');

ClientConnect

with Client as TTcpSrvClient do begin
        Display('Client connected.' +
                ' Remote: ' + PeerAddr + '/' + PeerPort +
                ' Local: '  + GetXAddr + '/' + GetXPort);
        Display('There is now ' +
                IntToStr(TWSocketServer(Sender).ClientCount) +
                ' clients connected.');
        LineMode            := TRUE;
        LineEdit            := TRUE;
        LineLimit           := 50; { Do not accept long lines }
        OnDataAvailable     := ClientDataAvailable;
        OnLineLimitExceeded := ClientLineLimitExceeded;
        OnBgException       := ClientBgException;
        OnSslVerifyPeer     := ClientVerifyPeer;
        ConnectTime         := Now;
    end;

So, my client connect and I got message, that one is client is connected.

When LineLimit is 50, I got "ClientLineLimitExceeded" event after some second and connection will closed.

When LineLimit is 62000, no event is calling (or ClientLineLimitExceeded takes a long time...).

All the time, no ClientDataAvailable is called.

Do you have a idea?

Edited by AJ_Oldendorf

Share this post


Link to post

You are right, I forgot...

 

var
  EOLData : AnsiString;
begin
  EOLData := '';
  SetLength(EOLData,Length(DefEOLB));
  Move(DefEOLB[0],EOLData[1],Length(DefEOLB));

var
  EOLData : AnsiString;
begin
  EOLData := '';
  SetLength(EOLData,Length(DefEOLB));
  Move(DefEOLB[0],EOLData[1],Length(DefEOLB));
  
  LineEnd             := EOLData;

 

Now it works and ClientDataAvailable Event is called.

 

One question, is the event everytime fired, when LineEnd is founded or can it happens, that the event is fired and in ReceiveStr is only a part of my message and I have to collect the received data?

Share this post


Link to post

LineMode/LineEnd is normally only used for ASCII protocols with limited line lengths, ie HTTP/SMTP etc, The DataAvailable event should only be triggered by LineEnd, but might also be if the connection is closed, or maybe not.   

 

That was why I suggested the  TIcsIpStrmLog componemt which has more options to avoid data being lost, but unfortunately not long LineEnds.   But I'd still use it for your clients, unless the server also sends long LineEnds. 

 

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
×