Solved

Sockets

Posted on 1998-11-28
8
943 Views
Last Modified: 2012-08-14
I need more information on how to use the sockets provided with Delphi 3 C/S. Does anybody know a good website with info?
Thanks
0
Comment
Question by:franksymons
  • 3
  • 3
  • 2
8 Comments
 
LVL 8

Expert Comment

by:ZifNab
ID: 1348288
What info do you need?

I prefer the use of F.Piette's components. They are free (cardware), great support and don't need any dll, activeX etc.

http://www.rtfm.be/fpiette/indexuk.htm

Zif.
0
 

Author Comment

by:franksymons
ID: 1348289
Adjusted points to 75
0
 
LVL 3

Accepted Solution

by:
williams2 earned 100 total points
ID: 1348290
The socket components basically works this way:

client/server communication can appear in two different ways:
1: Synchronous - When A sends a packet B sends an acknowledgement. A waits for this and sends the next packet etc..
2: Asynchonous - A sends packets until last packet has been send, B sends an acknowledgement as return (This is what I do in the sample)

1. Server:
The TServerSocket is provided with a port number, basically number 21 is used for FTP, 80 for http etc. but you are free to decide which.

The Servers IP-adress is systemspecific. it's either given by your internetprovider or your systemsettings.
Per default the IP-address 127.0.0.1 is assigned to your own computer, this cannot be changed.

The Server application "Listens" for clients to connect. This means, that every time I client is trying to connect, the TServerSocket throws a OnClientConnect event. When this is done, the client can send/receive information from the server.

The serverapplication should be able to handle severel clients simultanously. That's why serverapplications oftently needs threads to take care of this business, but basically you can use a timer to enumerate through the different client processes, like this:

Time:         Client1     Client2     Client3   ... ClientX
0             receive     -           Send
1             disconnect  connect     receive
2             Idle        send        disconnect
3             -           receive     Idle
4             -           disconnect  -

2. The Client:
The Client is the user who wants some kind of service to be provided from some kind of remote server.

The Client can connect to the server regardless it's on your own computer or on the Internet. You do not have to take care of that part, system will do that for you.

You need to watch the states on both the server and client, to tell each what each should be expecting from each other. When you send a message 'Please send me your phonenumber', you expect the server to provide you with it, if it doesn't, you would know if an error has happened.

The following sample makes it possible for the client to send a file the the server.

I think you should take a good look at this sample, and then you may say if you want further help.

The sample is not handling errors very good, as you will see, that demands a lot more code.

Part A (Client):
Start up a new delphi application.
Drag two Buttons, a TOpenDialog and a TClientSocket component onto your form.
1. create an event procedure for Form1's OnCreate event.
2. create an event procedure for buttonclick on both buttons.
3. create an event procedure for OnConnect,OnError and OnRead.
4. Remeber to save the whole thing in some directory, or Delphi won't let you compile project from Part B.

Cut'n'Paste the following lines to Unit1:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ScktComp, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    ClientSocket1: TClientSocket;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure ClientSocket1Connect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
      ErrorEvent: TErrorEvent; var ErrorCode: Integer);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
  private
    { Private declarations }
    Stream: TMemoryStream;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  ClientSocket1.Address:= '127.0.0.1';
  ClientSocket1.Port:= 2500;
  ClientSocket1.Open;
end;

procedure TForm1.ClientSocket1Connect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  ShowMessage('Connected.. Now go load a file!');
end;

procedure TForm1.ClientSocket1Error(Sender: TObject;
  Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
  var ErrorCode: Integer);
begin
  ShowMessage('Did you startup the server? I cannot find it!');
end;


procedure TForm1.Button2Click(Sender: TObject);
var
  Size: Integer;
begin
  if OpenDialog1.Execute Then
  begin
    Stream.LoadFromFile(OpenDialog1.Filename);
    Size:= Stream.Size;
    ClientSocket1.Socket.SendBuf(Size,SizeOf(Size));
    ClientSocket1.Socket.SendStream(Stream);
  End;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  Stream:= TMemoryStream.Create;
end;

procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  S: String;
begin
  S:= Socket.ReceiveText;
  Socket.Close;
  ShowMessage('Client: '+S);
end;

end.

____________________________________________________________________________

Part B (Server): Now to some server things..

Start up a new delphi application.
Drag an TSaveDialog and a TServerSocket component onto your form.
1. create an event procedure for Form1's OnCreate event.
2. create an event procedure for OnClientConnect, OnClientRead, OnListen.
3. create an event procedure for OnConnect,OnError and OnRead.

Cut'n'Paste the following lines to Unit1:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ScktComp;

type
  TForm1 = class(TForm)
    ServerSocket1: TServerSocket;
    SaveDialog1: TSaveDialog;
    procedure FormCreate(Sender: TObject);
    procedure ServerSocket1ClientConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ServerSocket1Listen(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
  private
    { Private declarations }
    Stream: TMemoryStream;
    FSize: Integer;
    writing: Boolean;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ServerSocket1.Port:= 2500;
  ServerSocket1.Active:= True;
  Stream:= TMemoryStream.Create;
  writing:= False;
end;

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  ShowMessage('A client has connected');
end;

procedure TForm1.ServerSocket1Listen(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  ShowMessage('I''m listening');
end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  BytesReceived: Longint;
  CopyBuffer: Pointer; { buffer for copying }
  ChunkSize: Integer;
  TempSize: Integer;
const
  MaxChunkSize: Longint = 8192; { copy in 8K chunks }
begin
  If FSize=0 then
  begin
    If Socket.ReceiveLength>SizeOf(TempSize) then
    begin
      Socket.ReceiveBuf(TempSize,SizeOf(TempSize));
      Stream.SetSize(TempSize);
      FSize:= TempSize //Threadsafe code!
    End;
  End;
  If (FSize>0) and not(writing) then
  begin
    GetMem(CopyBuffer, MaxChunkSize); { allocate the buffer }
    writing:= True;
    While Socket.ReceiveLength>0 do
    Begin
      ChunkSize:= Socket.ReceiveLength;
      If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
      BytesReceived:= Socket.ReceiveBuf(CopyBuffer^,ChunkSize);
      Stream.Write(CopyBuffer^, BytesReceived); { ...write chunk }
      Dec(FSize,BytesReceived);
    End;
    If FSize=0 then
    If SaveDialog1.Execute then
    begin
      If FileExists(SaveDialog1.Filename) then
        DeleteFile(SaveDialog1.Filename);
      Stream.SaveToFile(SaveDialog1.Filename);
      Socket.SendText('File received!');
      Stream.SetSize(0);
      FSize:= 0;
    End;
    FreeMem(CopyBuffer, MaxChunkSize); { allocate the buffer }
    Writing:= False;
  End;
end;


end.


regards
/Williams
0
 

Author Comment

by:franksymons
ID: 1348291
Hi again,

I've had some time to look into this now and I want to thank you for the nice example, it's certainly worth an A grade. The ultimate goal of the program would be to create some kind of 'live update' program like Norton Antivirus has. I do have some additional questions on your example.

First, the program doesn't seem to work with very small files (170b). Which is weird, when running the program without using the debugger it shows this behaviour. When debugging line by line it doesn't.

Second, you talk about improving error handling, what do you suggest?

I'd like, and tried to add some way of transferring the file name right before the stream itself using sendtext, this works but I'm not sure whether this is the best way of doing this. In addition to the filename I'd like to be able to tell the server what he should do with the file... So I will probably have to send a second file with instructions or send each instruction as text. Do you have any suggestions on that?

Do you know of any good delphi books on this subject (or chapters in a book) or some webpages, programming examples. I've been looking on the Net for some time now, but could't find anything...

Thanks again for your example and hope to hear from you again soon.

Best regards from Belgium,
Frank.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 8

Expert Comment

by:ZifNab
ID: 1348292
Zoals ik reeds vertelde Frank, kijk naar F.Piette's webpaginas (url : comment above). Daar zul je verschillende links en voorbeelden vinden. Zif.
0
 
LVL 3

Expert Comment

by:williams2
ID: 1348293
Hi Frank.

You do observe things very well, and I must say, that this will maybe bring you a bright future. These exact problems is beeing discussed.

The very best book for understanding these matters are this:
"Distributed Systems - Concepts and Design"
2nd edition
by George Coulouris, Jean Dollimore and Tim Kindberg
Publisher: Addison-Wesley

1: Asynchonous tranfers can result in errors as the problems you have encountered. This can be solved by replacing the procedures followed by these answers:

2. The Norton Antivirus Update works apparently this way:
The client connects to a specific site
The client compares versioninfo (probably a version date+time)
The client requests the update setup system and might execute this when received successfully.

3. Typically you build a header-structure with information regarding requests, filenames, attributes, sizes etc. You will ofcourse have to expect that header on the server.
If you are sending complex structures, you should read more about Marshalling (Sending/receiving an object as a string or binary)

4. Error handling - You should provide errorhandling by using checksum errorchecking and f.ex. password checking/encryption to ensure dataintegrity. You will also have to check the time between the states to provide a timeout feature. All this is important, because of the unreliability of network systems.

I have been tought all this at Aarhus Business College in Denmark during my education as Computer Engineer. We primaly used the book I described above and can be ordered by most bookstores.

From question #1:

In the server project:

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
const
  MaxChunkSize = 8192; { copy in 8K chunks }
var
  BytesReceived: Longint;
  CopyBuffer: Array[0..MaxChunkSize] of byte; { buffer for copying }
  ChunkSize: Integer;
begin
  If FSize=0 then
  begin
    If Socket.ReceiveLength>=SizeOf(FSize) then
    begin
      Socket.ReceiveBuf(FSize,SizeOf(FSize));
      Stream.SetSize(FSize);
      Socket.SendText('Send next packet');
    End;
  End else
  begin
    While Socket.ReceiveLength>0 do
    Begin
      ChunkSize:= Socket.ReceiveLength;
      If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
      BytesReceived:= Socket.ReceiveBuf(CopyBuffer,ChunkSize);
      Stream.Write(CopyBuffer, BytesReceived); { ...write chunk }
      Dec(FSize,BytesReceived);
    End;
    If FSize=0 then
    Begin
      Socket.SendText('File received!');
      If SaveDialog1.Execute then
      begin
        If FileExists(SaveDialog1.Filename) then
          DeleteFile(SaveDialog1.Filename);
        Stream.SaveToFile(SaveDialog1.Filename);
        Stream.SetSize(0);
        FSize:= 0;
      End;
    End else
      Socket.SendText('Send next packet');
  End;
end;


In the client project:

procedure TForm1.Button2Click(Sender: TObject);
var
  Size: Integer;
begin
  if OpenDialog1.Execute Then
  begin
    Stream.LoadFromFile(OpenDialog1.Filename);
    Size:= Stream.Size;
    ClientSocket1.Socket.SendBuf(Size,SizeOf(Size));
    Stream.Position:= 0;
//    ClientSocket1.Socket.SendStream(Stream);
  End;
end;

procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);

  procedure SendNext;
  const
    MaxChunkSize = 8192;
  var
    CopyBuffer: Array[0..MaxChunkSize] of Byte;
    CopySize: Integer;
  begin
    CopySize:= Stream.Size-Stream.Position;
    If CopySize>MaxChunkSize then CopySize:= MaxChunkSize;
    Stream.WriteBuffer(CopyBuffer,CopySize);
    Socket.SendBuf(CopyBuffer,CopySize);
  End;

var
  S: String;
begin
  S:= Socket.ReceiveText;
  If S='Send next packet' then SendNext else
  begin
    Socket.Close;
    ShowMessage('Client: '+S);
  End;
end;

regards,
Williams
0
 

Author Comment

by:franksymons
ID: 1348294
Thanks for your answers and comments, you've been very helpfull. And thanks to ziff for the comments I've downloaded the suite and I'm checking it out.
0
 
LVL 3

Expert Comment

by:williams2
ID: 1348295
You are welcome, we could use some more network dudes here at EE.

Regards,
Williams
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

705 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

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now