Solved

ICS Sockets, Buffering and numerics.

Posted on 2001-06-10
11
230 Views
Last Modified: 2010-04-06
Baically what I aim to do with the following code is to use command prefixes (numerics) to allow easy handling of incoming data to both clients and servers. I'm looking to do this in an IRC style way. The problem that arises is that I know nothing about doing it. Plainly hehe.

My question is really a request for code completion/improvement here. Preferably, the person answering this has done quite a bit of work with ICS, and handled buffering and numeric style command/response handling. This code is only a base model, I'm very open here.

Here's how it should work...
When the data is recieved, I want to make sure I have all of it, before I do anything with it. I also want to avoid having peices from the NEXT command (which can happen I've been told).

bacially I guess if you can see what I'm trying to do below and can improve it and make it work fully, you're the person for the answer hehe. I'll let my code do the talking...

procedure TForm1.SendCommand(Numeric: Integer; Length: Integer; Content: String);
var
Temp: TStringList;
begin
Data:= TStringList.Create;
Data.Insert(0, IntToStr(Numeric));
Data.Insert(1, IntToStr(Length));
Temp := TStringList.Create;
Temp.Text := Content;
Data.Insert(2, Temp.CommaText);
TextClient.Send(Data.CommaText); {Error on this line}
end;

procedure TForm1.TextServerDataAvailable(Sender: TObject; Error: Word);
var
ReciveBuffer: String;
FCurrentlyReciving: Boolean;
begin
Data:= TStringList.Create;

 ReciveBuffer := ReciveBuffer + TextServer.ReceiveStr;
 Data.CommaText := ReciveBuffer;

 if Data.Count < 3 then
 begin
 FCurrentlyReciving := True;
 exit;
  if Length(Data[2]) < StrToInt(Data[1]) then
   begin
    FCurrentlyReciving := True;
    exit;
   end else
   begin
    FCurrentlyReciving := False;
    Data.CommaText:= TextServer.ReceiveStr;
    Echo(Data.CommaText);
    ParseCommand(StrToInt(Data[0]), Data[2]);
   end;
   end;
end;

procedure TForm1.ParseCommand(Numeric: Integer; Content: String);
begin
{This is where I'll be parsing all commands}
end;


Thanks!
Psylord
0
Comment
Question by:Psylord
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 4
11 Comments
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6174632
Stream sockets do not guarantee any availability or data length to be transmitted at once. So you cannot rely on getting the data in as chunks as they were sent (and that's why you can have an incomplete command or onw tiah a part of the next). It looks like you're going to implement a text based protocol, so it would make sense to use a CR (carriage return, that is, ASCII char 13) as data separator, e.g. everytime you see a CRLF you know that one command was transmitted completely.

You need to add a buffer which is kept across your TextServerDataAvailable; to do this, just add a "FReceiveBuffer: string" to your TForm1 private declaration. Then work like this:


procedure TForm1.SendCommand(Numeric: Integer; Content: String);
begin
     TextClient.Send(Format('%d %s',[Numeric,StringReplace(StringReplace(Line,'\','\\',[rfReplaceAll]),#13,'\n',[rfReplaceAll])]));
end;


procedure TForm1.TextServerDataAvailable(Sender: TObject; Error: Word);
var
     Line: string;
     I: Integer;
begin
     FReciveBuffer:=FReciveBuffer+TextServer.ReceiveStr;
     I:=Pos(#13,FReceiveBuffer);
     while I>0 do begin
          Line:=Copy(FReceiveBuffer,1,I-1);
          Delete(FReceiveBuffer,1,I);
          Line:=StringReplace(StringReplace(Line,'\n',#13,[rfReplaceAll]),'\\','\',[rfReplaceAll]);
          I:=Pos(' ',Line);
          ParseCommand(StrToInt(Copy(Line,1,I-1)),Copy(Line,I+1,Length(Line));
          I:=Pos(#13,FReceiveBuffer);
     end;
end;


Of course, the code is made so that it will handle #13 in the data alright. BTW, in your code, you careate tons of TStringLists - but you never free them. That is called a memory leak and makes an application use more and more memory until the app is terminated or the available memory is exhausted. Try to free the mem you allocate, Delphi does *not* destroy class instances automatically!
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6174648
Oops. Damn copy-paste... ;-)
Should be like this of yourse:

procedure TForm1.SendCommand(Numeric: Integer; const Content: String);
begin
     TextClient.Send(Format('%d %s',[Numeric,StringReplace(StringReplace(Content,'\','\\',[rfReplaceAll]),#13,'\n',[rfReplaceAll])]));
end;
0
 

Author Comment

by:Psylord
ID: 6180472
Ok don't get upset yet... I've decided to use the borland sockets, as they're much easier... Especially when sending to specific clients...

Is there any way you can change the code around to just use '#13#10' as the end of the command? It think I might understand it a little better... In other words, instead of everything else I said, just wait until #13#10 is recieved and BOOM! we have the command.

Your code might (and probably does) have the same effect, but it's just a little confusing to me and I couldn't get it working with ICS because ICS SUCKS! So, if you would, with the normal Borland internete client/server...

If you can I'll increase the points 50 more. <:D

Here's what the recieve events look like for the sockets I'm using now:


{Server}
procedure TForm1.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin

end;

{Client}
procedure TForm1.ServerClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
begin

end;


When I parse these, I'll need to send the socket along with it so I know who to reply to when I parse the command, so my parsecommand will look like this:

procedure TForm1.ParseCommand(Socket: TCustomWinSocket; Numeric: Integer; Content: String);
begin

 case Numeric of
  100:
   begin
    {The numeric was "100"!}
   end;

 end; {Case}

end; {Proc}

Stupid comments and example code provided. :)

So basically I'll be sending my commands like this:

    Client.Socket.SendText(IntToStr(Numeric) + ' ' + Content);
    Server.Socket.SendText(IntToStr(Numeric) + ' ' + Content);

Appreciate the help,
Psylord
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:Psylord
ID: 6180488
Ok don't get upset yet... I've decided to use the borland sockets, as they're much easier... Especially when sending to specific clients...

Is there any way you can change the code around to just use '#13#10' as the end of the command? It think I might understand it a little better... In other words, instead of everything else I said, just wait until #13#10 is recieved and BOOM! we have the command.

Your code might (and probably does) have the same effect, but it's just a little confusing to me and I couldn't get it working with ICS because ICS SUCKS! So, if you would, with the normal Borland internete client/server...

If you can I'll increase the points 50 more. <:D

Here's what the recieve events look like for the sockets I'm using now:


{Server}
procedure TForm1.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin

end;

{Client}
procedure TForm1.ServerClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
begin

end;


When I parse these, I'll need to send the socket along with it so I know who to reply to when I parse the command, so my parsecommand will look like this:

procedure TForm1.ParseCommand(Socket: TCustomWinSocket; Numeric: Integer; Content: String);
begin

 case Numeric of
  100:
   begin
    {The numeric was "100"!}
   end;

 end; {Case}

end; {Proc}

Stupid comments and example code provided. :)

So basically I'll be sending my commands like this:

    Client.Socket.SendText(IntToStr(Numeric) + ' ' + Content);
    Server.Socket.SendText(IntToStr(Numeric) + ' ' + Content);

Appreciate the help,
Psylord
0
 

Author Comment

by:Psylord
ID: 6180508
Ok don't get upset yet... I've decided to use the borland sockets, as they're much easier... Especially when sending to specific clients...

Is there any way you can change the code around to just use '#13#10' as the end of the command? It think I might understand it a little better... In other words, instead of everything else I said, just wait until #13#10 is recieved and BOOM! we have the command.

Your code might (and probably does) have the same effect, but it's just a little confusing to me and I couldn't get it working with ICS because ICS SUCKS! So, if you would, with the normal Borland internete client/server...

If you can I'll increase the points 50 more. <:D

Here's what the recieve events look like for the sockets I'm using now:


{Server}
procedure TForm1.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin

end;

{Client}
procedure TForm1.ServerClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
begin

end;


When I parse these, I'll need to send the socket along with it so I know who to reply to when I parse the command, so my parsecommand will look like this:

procedure TForm1.ParseCommand(Socket: TCustomWinSocket; Numeric: Integer; Content: String);
begin

 case Numeric of
  100:
   begin
    {The numeric was "100"!}
   end;

 end; {Case}

end; {Proc}

Stupid comments and example code provided. :)

So basically I'll be sending my commands like this:

    Client.Socket.SendText(IntToStr(Numeric) + ' ' + Content);
    Server.Socket.SendText(IntToStr(Numeric) + ' ' + Content);

Appreciate the help,
Psylord
0
 

Author Comment

by:Psylord
ID: 6180511
Holy crap, it posted 3 times. Whoops. :)
0
 
LVL 14

Accepted Solution

by:
AvonWyss earned 200 total points
ID: 6180603
My code takes #13 as command terminator. Plus it does change all #13 which might have been there when sending the command so that they are not recognized as end-of-command. This happens transparently, e.g. you can send and receive *any* data (including #13) as command parameter. Still the actual transfer over the sockets is plain text.

You basically have to change two minor things in both the send and the receive procs to make it work with the borland sockets:

instead of
        FReciveBuffer:=FReciveBuffer+TextServer.ReceiveStr;
use
        FReciveBuffer:=FReciveBuffer+Socket.ReceiveText;

and when sending, use the following:  
        Socket.SendText(Format('%d %s',[Numeric,StringReplace(StringReplace(Command,'\','\\',[rfReplaceAll]),#13,'\n',[rfReplaceAll])]));

The StringReplace takes care of handling your #13 in the command properly (that is, handing them over to ParseCommand also).


I hope that you are aware that EE policy does state that a question in their terms is exactly one question. If you change your mind about what you want to use, but already got an answer, its not correct to reject the answer if it was correct (and mine was). Please review the EE Q&A guidelines at http://www.experts-exchange.com/jsp/cmtyQuestAnswer.jsp

It's the second time you rejected an answer posted by me for no reason. The other one is
        http://www.experts-exchange.com/jsp/qShow.jsp?ta=delphi&qid=20132897
and I expect you to rectify this ASAP.
0
 

Author Comment

by:Psylord
ID: 6184611
*Sigh* I will be specific in my qeustions from now on. Reviewing the code now. By the way, I have YET to get that winsock question's code to work...
0
 

Author Comment

by:Psylord
ID: 6184658
Still doesn't work. but ok.
0
 

Author Comment

by:Psylord
ID: 6184659
still can't seem to get it working but YEAH. Here's your points.
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6184749
Psylord, what does not work?
0

Featured Post

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…
Monitoring a network: why having a policy is the best policy? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the enormous benefits of having a policy-based approach when monitoring medium and large networks. Software utilized in this v…

717 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question