Ir al contenido


Foto

¿Varios sockets escuhando por el mismo puerto en la misma máquina?


  • Por favor identifícate para responder
1 respuesta en este tema

#1 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 abril 2017 - 04:06

A raíz de una que se reviviera un antiguo hilo en CD, Mensajes Broadcast vía UDP entre varias instancias corriendo en una misma máquina, publiqué un ejemplo que rompe el esquema ampliamente conocido que afirma que no podemos tener más de un servidor socket escuchando por el mismo puerto. En general la afirmación es cierta pero es posible asociar un mismo puerto a dos sockets en dos circunstancias que pasan por usar una opción especial: SO_REUSEADDR con la AP Isetsockopt.  El uso de esta opción permite vincular el mismo puerto a dos sockets al mismo tiempo quedando de forma indeterminada quien de los dos realmente recibirá el mensaje salvo que estemos usando MultiCast, en cuyo caso ambos servidores recibirían el mensaje.

 

SO_REUSEADDR puede usarse maliciosamente para denegar un servicio de red debido a la indeterminación pero puede usarse para enviar un mensaje a varios servidores que escuchan en el mismo puerto como es el caso este tema, de tal forma que varias instancias de un ejecutable incluso corriendo en estaciones de ventana diferentes, pueden recibir el mensaje emitido por un cliente de red.

 

Me ha parecido interesante añadir la prueba de concepto en el foro. La prueba de concepto es simple, se trata de una aplicación servidor en un thread separado. esta aplicación vincula un socket UDP al un puerto elegido al azar, el 9090. podemos tener varias instancias ejecutándose. La otra aplicación es un cliente UDP que envía por el puerto 9090 a la dirección Broadcast de la red un mensaje simple: 'Hola'. Cuando los servidores lo reciban mostrarán el mensaje de texto con un MessageBox.

 

El código del servidor:


delphi
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, WinSock;
  8.  
  9. type
  10.   TServer = class(TThread)
  11.   private
  12.   protected
  13.     procedure Execute; override;
  14.   public
  15.   end;
  16.  
  17.  
  18.   TForm1 = class(TForm)
  19.     procedure FormCreate(Sender: TObject);
  20.   private
  21.     Server: TServer;
  22.   public
  23.     { Public declarations }
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.dfm}
  32.  
  33. procedure TServer.Execute;
  34. const
  35.   BufferSize = 1024;
  36.   Port = 9090;
  37. var
  38.   WSA: WinSock.TWSADATA;
  39.   Sock: WinSock.TSOCKET;
  40.   Addr: WinSock.sockaddr_in;
  41.   Buffer: array[0..BufferSize-1] of AnsiChar;
  42.   Len, AddrSize: integer;
  43.   dwTime: DWORD;
  44.   ValMulticast: AnsiCHAR;
  45. begin
  46.   if (WinSock.WSAStartup(MakeWord(2, 2), WSA) <> 0) then exit;
  47.   Sock := WinSock.socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
  48.   if (Sock <> INVALID_SOCKET) then
  49.   begin
  50.     dwTime:= 1000;
  51.     ValMulticast:= #1;
  52.     // Establecemos un timeout de bloqueo
  53.     setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, PAnsiCHAR(@dwTime), sizeof(dwTime));
  54.     // Permitimos vincular el puerto más veces
  55.     setsockopt(Sock, SOL_SOCKET, SO_REUSEADDR, @ValMulticast, sizeof(ValMulticast));
  56.     // Establecemos la opción multicast(Activa por defecto)
  57.     setsockopt(Sock, IPPROTO_IP, IP_MULTICAST_LOOP, nil, 1);
  58.     Addr.sin_family := AF_INET;
  59.     Addr.sin_addr.s_addr := INADDR_ANY;
  60.     Addr.sin_port := WinSock.htons(Port);
  61.     AddrSize := sizeof(Addr);
  62.  
  63.     // Asociamos el socket al puerto y a escuchar
  64.     if (bind(Sock, Addr, AddrSize) <> -1) then
  65.     begin
  66.       // Bucle de escucha...
  67.       while not Terminated do
  68.       begin
  69.         ZeroMemory(@Buffer[0], BufferSize);
  70.         Len := WinSock.recvfrom(Sock, Buffer, BufferSize-1, 0, Addr, AddrSize);
  71.         // Leemos el paquete enviado
  72.         if (Len > 0) and (Len < BufferSize) then
  73.         begin
  74.           Windows.Beep(1000, 100);
  75.           MessageBoxA(0, Buffer, 'Eureka',0);
  76.         end;
  77.       end;
  78.     end;
  79.     WinSock.closesocket(Sock);
  80.   end;
  81.   WinSock.WSACleanUp;
  82. end;
  83.  
  84. procedure TForm1.FormCreate(Sender: TObject);
  85. begin
  86.   Server:= TServer.Create(false);
  87. end;
  88.  
  89. end. 

El código del cliente:


delphi
  1. unit Unit2;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, WinSock, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls;
  8.  
  9. type
  10.   TForm2 = class(TForm)
  11.     Button1: TButton;
  12.     procedure Button1Click(Sender: TObject);
  13.   private
  14.     { Private declarations }
  15.   public
  16.     { Public declarations }
  17.   end;
  18.  
  19. TMIB_IPADDRROW = packed record
  20.   dwAddr: DWORD;
  21.   dwIndex: DWORD;
  22.   dwMask: DWORD;
  23.   dwBCastAddr: DWORD;
  24.   dwReasmSize: DWORD;
  25.   unused1: SmallInt;
  26.   wType: SmallInt;
  27. end;
  28.  
  29. TMIB_IPADDRTABLE = record
  30.   dwNumEntries: DWORD;
  31.   table: array[0..0] of TMIB_IPADDRROW;
  32. end;
  33. PMIB_IPADDRTABLE = ^TMIB_IPADDRTABLE;
  34.  
  35.  
  36. function GetIpAddrTable(IpAddrTable: PMIB_IPADDRTABLE; pdwSize: PULONG;
  37.   Order: BOOL): DWORD; stdcall; external 'iphlpapi.dll' name 'GetIpAddrTable';
  38.  
  39. var
  40.   Form2: TForm2;
  41.  
  42. implementation
  43.  
  44. {$R *.dfm}
  45. function GetCurrentIP: DWORD;
  46. var
  47.   Wsa: WSADATA;
  48.   Name: array[0..255] of AnsiChar;
  49.   hostinfo: PHOSTENT;
  50. begin
  51.   Result:= 0;
  52.   FillChar(Wsa, SizeOf(WSAData), 0);
  53.   if WSAStartup(MAKEWORD(2, 2), Wsa) = 0 then
  54.   begin
  55.     if gethostname(Name, SizeOf(Name)) = 0 then
  56.     begin
  57.       hostinfo:= gethostbyname(Name);
  58.       if hostinfo <> nil then
  59.         Result:= PDWORD(hostinfo^.h_addr_list^)^;
  60.     WSACleanup;
  61.     end;
  62.   end;
  63. end;
  64.  
  65. function GetBrodcastAddress: AnsiString;
  66. var
  67.   pIPAddrTable: PMIB_IPADDRTABLE;
  68.   dwSize: DWORD;
  69.   i: integer;
  70.   BroadCastInAddr: IN_ADDR;
  71. begin
  72.   BroadCastInAddr.S_addr:= 0;
  73.   dwSize:= 0;
  74.   GetIpAddrTable(nil, @dwSize, true);
  75.   GetMem(pIPAddrTable, dwSize);
  76.   if pIPAddrTable<>nil then
  77.   begin
  78.     if GetIpAddrTable(pIPAddrTable, @dwSize, true) = NO_ERROR then
  79.       for i:=0 to  pIPAddrTable^.dwNumEntries-1 do
  80.       begin
  81.         if GetCurrentIP = pIPAddrTable^.table[i].dwAddr then
  82.         begin
  83.           BroadCastInAddr.S_addr:= pIPAddrTable^.table[i].dwAddr or not pIPAddrTable^.table[i].dwMask;
  84.           break;
  85.         end;
  86.       end;
  87.     FreeMem(pIPAddrTable);
  88.   end;
  89.   Result:= inet_ntoa(BroadCastInAddr);
  90. end;
  91.  
  92. procedure SendUDP(Msg: AnsiString; IP: AnsiString; Port: WORD);
  93. var
  94.  Wsa: WSADATA;
  95.  S: TSocket;
  96.  Addr: WinSock.sockaddr_in;
  97.  Host: PHostent;
  98. begin
  99.   if WSAStartup(MAKEWORD(2, 2), Wsa) = 0 then
  100.   try
  101.     S:= Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  102.     if S <> INVALID_SOCKET then
  103.     begin
  104.       Host:= gethostbyname(PAnsiCHAR(IP));
  105.       Addr.sin_family:= AF_INET;
  106.       Addr.sin_addr.S_addr:= PInAddr(Host.h_addr_list^)^.S_addr;
  107.       Addr.sin_port:= htons(Port);
  108.       Sendto(S, PAnsiChar(Msg)^, Length(Msg), 0, Addr, SizeOf(sockaddr_in));
  109.      end;
  110.   finally
  111.     WSACleanup();
  112.   end;
  113. end;
  114.  
  115. procedure TForm2.Button1Click(Sender: TObject);
  116. begin
  117.   SendUDP('Hola', GetBrodcastAddress, 9090);
  118. end;
  119.  
  120. end.

El código está probado con delphi7 y Berlin en Win8 y Win10

 

Subo el ejemplo.

 

 

Saludos.

 

 

Archivos adjuntos


  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 14 abril 2017 - 10:00

Caramba!!! Eso rompe muchos paradigmas y me recuerda los problemas que tuve con el servidor de sockets que estaba desarrollando y que amablemente me ayudaste amigo escafandra.

Es impresionante como manejas el API. :o

Saludos
  • 0




IP.Board spam blocked by CleanTalk.