1

Hi I have a problem with the following code:

program client;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.Win.ScktComp, idContext, idGlobal;

var
  ClientSocket1: TClientSocket;
  m: TMethod;
  m2: TMethod;

procedure hi(Sender: TObject; Socket: TCustomWinSocket);
begin
  ClientSocket1.Socket.SendText('hello');
  Writeln('connect');
end;

Procedure read_data(Sender: TObject; Socket: TCustomWinSocket);
Var
  Raw: String;
Begin
  Raw := Socket.ReceiveText;
  Writeln(Raw);
End;

begin
  try

    ClientSocket1 := TClientSocket.Create(nil);
    ClientSocket1.Address := '127.0.0.1';
    ClientSocket1.Port := 123;
    ClientSocket1.Open;

    m.Code := @read_data;
    m.Data := ClientSocket1;
    ClientSocket1.OnRead := TSocketNotifyEvent(m);

    m2.Code := @hi;
    m2.Data := ClientSocket1;
    ClientSocket1.OnConnect := TSocketNotifyEvent(m2);

    while '1' = '1' do
    begin
      //
    end;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

end.

The problem is not receiving any information from the server and when connected correctly not detected in the program. The program is a console application to connect using the ClientSocket component.

the server program is run in a graphical application and uses the ServerSocket component enabled port 123, use these buttons:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ServerSocket1.Socket.Connections[ListBox1.Itemindex].SendText(Edit1.Text);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  lugar: Integer;
begin
  ListBox1.Clear;
  for lugar := 0 To ServerSocket1.Socket.ActiveConnections - 1 do
  begin
    ListBox1.Items.add(ServerSocket1.Socket.Connections[lugar].RemoteHost);
  end;
end;

Can someone help me?

Matt Olsen
  • 255
  • 2
  • 7
  • 13
  • 1
    Off-topic, but you may wish to choose a port other than `123` which is used for time synchronization. Usually custom non-standard ports are 4-5 digits for example `10155`. Refer to this list: http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers – Jerry Dodge Feb 28 '15 at 21:11
  • On another important note, `ScktComp` is a very, very, very outdated component set. This only exists for backwards compatibility. New software should never ever use these components. – Jerry Dodge Feb 28 '15 at 21:13
  • closed the question? Then where I have to ask? – Matt Olsen Feb 28 '15 at 21:16
  • Nobody has even voted to close your question. – Jerry Dodge Feb 28 '15 at 21:30
  • ok , try changing the port number but the problem still think I did wrong theme events , the problem is there is no documentation on how to do this in console, in graphical version if I encounter and to spare – Matt Olsen Feb 28 '15 at 21:36
  • I meant my comment was off-topic, not your question. I don't bother working with those components because they're extremely old and deprecated. Otherwise, I would test this for you. There's no documentation on these components because you're not supposed to be using them. I highly advise to switch to different socket components, such as Indy. Even when I started with Delphi some 8 years ago I was advised to never use those components. – Jerry Dodge Feb 28 '15 at 21:39
  • The TClientSocket component uses asynchronous sockets managed via windows messaging. This means that they need your program to idle in a message loop rather than the busy loop that you have. When used in a console application you are likely to need to use it in blocking mode (or as advised above switch to a more modern socket component). If you put similar code in a GUI program (without the busy loop) you will find that it works. – Kanitatlan Mar 01 '15 at 00:51
  • @Kanitatlan: "*The TClientSocket component uses asynchronous sockets managed via windows messaging.*" Only when you use it in non-blocking mode (which is it default setting). In blocking mode, there is no messaging involved. – Remy Lebeau Mar 01 '15 at 01:12

1 Answers1

0

By default, TClientSocket runs in non-blocking mode. In that mode, it uses window messages internally to trigger its events. But there is no message loop in your code, so you need to add one. You are also not setting up your event handlers correctly, either (you are missing an explicit Self parameter, since your handlers are not members of a class).

Try this:

program client;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Win.ScktComp, Winapi.Windows;

procedure socket_connected(Self: Pointer; Sender: TObject; Socket: TCustomWinSocket);
begin
  Writeln('connect');
  Socket.SendText('hello');
end;

procedure socket_disconnected(Self: Pointer; Sender: TObject; Socket: TCustomWinSocket);
begin
  Writeln('disconnect');
  PostQuitMessage(0);
end;

procedure socket_read(Self: Pointer; Sender: TObject; Socket: TCustomWinSocket);
var
  Raw: String;
begin
  Raw := Socket.ReceiveText;
  Writeln(Raw);
end;

procedure socket_error(Self: Pointer; Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
  Writeln('error');
  PostQuitMessage(0);
end;

var
  ClientSocket1: TClientSocket;
  m: TMethod;
  msg: TMsg;
begin
  try
    ClientSocket1 := TClientSocket.Create(nil);
    try
        ClientSocket1.Address := '127.0.0.1';
        ClientSocket1.Port := 123;

        m.Code := @socket_connected;
        m.Data := ClientSocket1;
        ClientSocket1.OnConnect := TSocketNotifyEvent(m);

        m.Code := @socket_disconnected;
        m.Data := ClientSocket1;
        ClientSocket1.OnDisconnect := TSocketNotifyEvent(m);

        m.Code := @socket_read;
        m.Data := ClientSocket1;
        ClientSocket1.OnRead := TSocketNotifyEvent(m);

        m.Code := @socket_error;
        m.Data := ClientSocket1;
        ClientSocket1.OnError := TSocketErrorEvent(m);

        ClientSocket1.Open;

        while GetMessage(msg, 0, 0, 0) do
        begin
          TranslateMessage(msg);
          DispatchMessage(msg);
        end;
      finally
        ClientSocket1.Free;
      end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;    
end.

Otherwise, change the socket to blocking mode instead, and get rid of the event handlers completely:

program client;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Win.ScktComp;

var
  ClientSocket1: TClientSocket;
  Raw: String;
begin
  try
    ClientSocket1 := TClientSocket.Create(nil);
    try
        ClientSocket1.Address := '127.0.0.1';
        ClientSocket1.Port := 123;
        ClientSocket1.ClientType := ctBlocking;

        try
          ClientSocket1.Open;
          Writeln('connect');
          ClientSocket1.Socket.SendText('hello');
          repeat
            Raw := ClientSocket1.Socket.ReceiveText;
            if Raw <> '' then Writeln(Raw);
          until not ClientSocket1.Connected;
          Writeln('disconnect');
        except    
          Writeln('error');
          raise;
        end;
      finally
        ClientSocket1.Free;
      end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;    
end.
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I think that this will be very very useful to me [because `SetThreadDesktop` api fails in VCL projects](http://stackoverflow.com/questions/41008676/how-make-setthreaddesktop-api-work-from-of-a-console-application/41069754?noredirect=1#comment69349858_41069754), my "client.exe" is in VCL and use `TClientSocket` component and now i'm needing change this project to console mode because `SetThreadDesktop` api :-( –  Dec 10 '16 at 05:10