Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   API de Windows (https://www.clubdelphi.com/foros/forumdisplay.php?f=7)
-   -   Duda con sockets (https://www.clubdelphi.com/foros/showthread.php?t=61055)

Khronos 23-10-2008 16:30:17

Duda con sockets
 
Hola, estuve investigando como funcionan las sockets de windows y realicé este código. En una aplicación visual consigo realizar la aplicación cliente-servidor, pero desde una aplicación de consola no hay forma... si consigo que se conecte el cliente pero no soy capaz de recibir sus mensajes ni de enviarle nada :(

Código Delphi [-]
unit Server;

interface

uses Windows, WinSock, Messages, SysUtils, Classes;

const
  WM_WINSOCK_ASYNC_MSG = WM_USER + 2988;

  type
    TServer = class(TThread)
      private
        InitDllSock  : TWSAData;
        TCP: TSocket;
        WHandle: THandle;
        Msg: TMsg;
        Addr: TSockAddrIn;
      protected
        procedure AWndProc(  Msg: TMsg);
      public
        procedure SendStr(str: string);
        constructor Create; reintroduce;
        procedure Execute; override;
    end;


implementation

procedure TServer.SendStr(Str: string);
begin
 send(TCP, Pointer(Str)^, Length(Str), 0);
end;

procedure TServer.AWndProc( Msg: TMsg);
begin
   case WSAGetSelectEvent(Msg.lParam) of
   FD_ACCEPT:
    begin
    Writeln('Conexión entrante');
    SendStr('Hola cliente');
    end;
   FD_READ: Writeln('Leyendo..');
   FD_CLOSE: Writeln('Conexión cerrada');
 end;
end;

procedure TServer.Execute;
var
sinsize: integer;
begin
WSAStartup( $0202, InitDllSock );
TCP := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
with addr do
  begin
    sin_family:= af_Inet;
    sin_port:= hToNs(1234);
    sin_addr.s_addr:= InAddr_Any;
  end;
bind(TCP, addr, SizeOf( addr ) );
//ioctlsocket( TCP, FIONBIO, 1);
WSAAsyncSelect(TCP, WHandle, WM_WINSOCK_ASYNC_MSG,
FD_READ or FD_CLOSE or FD_ACCEPT );
listen( TCP, 1);
while not Terminated do
    begin
      If GetMessage(Msg,WHandle, 0, 0) then
        begin
            TranslateMessage(Msg);
            Accept( TCP, @addr, @sinsize);
            DispatchMessage( Msg );
            AWndProc(Msg);
            Sleep(7000);
            SendStr('holaaaa');
        end;

    end;
end;

constructor TServer.Create;
begin
  inherited Create(True);
    FreeOnTerminate:= True;
    Execute;
end;


end.

Código Delphi [-]
program Servidor;

{$APPTYPE CONSOLE}

uses
  Server in 'Server.pas';

begin
TServer.Create;
end.

Saludos y gracias.

cHackAll 30-10-2008 01:39:28

  1. El primer error que estas cometiendo es utilizar una variable que no es asignada (WHandle), por ello WSAAsyncSelect debería enviar los mensajes a la ventana con identificador 0, como esto es ilógico retorna error (SOCKET_ERROR).
  2. Es importante comprender que una aplicación de consola (como su nombre lo dice), es de tipo consola y no una ventana, por ende NO tiene una cola de mensajes y GetMessage entraría a un dead lock infinito. Para prevenir esto en una consola lo óptimo es crear una ventana la cual creará indirectamente la cola de mensajes.
  3. Cuando llamas al método del hilo dentro del mismo constructor del hilo (TThread) y sumado a esto el método tiene un loop, va a realizar una llamada directa tal cual si fuese cualquier otro método diferente a un hilo hasta que termine el loop. Lo que te esta dando la ilusión de que el hilo esta bien construido es que dentro del loop estas procesando la cola de mensajes (GetMessage -> TranslateMessage -> DispatchMessage) equivalente al Application.ProcessMessages. Para corroborar éste error y notar su gravedad prueba:
    Código Delphi [-]
    procedure TForm1.Button1Click(Sender: TObject);
    begin
     TServer.Create;
     ShowMessage('Never see me !')
    end;
    Considerando que esta falla es remediada hay un segundo problema GetMessage esperará un nuevo mensaje en la cola sin considerar la posibilidad de que el hilo sea terminado, esto puede causar la perdida de control en algunas situaciones.
  4. WSAStartup tiene la función de inicializar las funciones de sockets para el proceso ejecutor (~LoadLibrary :D), esto debiese utilizarce cuando uno comenzará a utilizar los sockets y finalizarlo cuando uno termine, sin embargo en la mayoria de los sistemas éste tiempo es equivalente al tiempo de vida del proceso.
Aparte de MemoryLeaks y otros; aclaro que todo lo dicho fueron críticas constructivas a favor del conocimiento ;)

Me temo que es complicado solucionar (para consola), dichas fallas, sin cambiar la escencia de dicho objeto (WSAAsyncSelect)

Saludos

Khronos 30-10-2008 15:35:27

Muchas gracias cHackAll por tus consejos, voy a probar a crear una ventana dinamicamente con CreateWindowsEx para conseguir el handle de una ventana y poder caputar los mensajes.

Salu2


La franja horaria es GMT +2. Ahora son las 21:49:48.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi