Solved

ICS Sockets, Buffering and numerics.

Posted on 2001-06-10
11
229 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
Independent Software Vendors: 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

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!

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Printing problem 2 111
Downloading email attachments 2 85
Press three keys together and trigger a function 3 67
how can i delete one control based on its tagstring ? 6 38
A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an antispam), the admini…
Finding and deleting duplicate (picture) files can be a time consuming task. My wife and I, our three kids and their families all share one dilemma: Managing our pictures. Between desktops, laptops, phones, tablets, and cameras; over the last decade…

726 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