2010-04-09 16 views
10

Im tratando de escribir un servidor/servicio que transmite un mensaje en la LAN cada segundo, más o menos, algo así como un descubrimiento de servicios.MultiCast Mensajes a varios clientes en la misma máquina

El mensaje debe ser recibido por programas clientes múltiples que podrían ser en la misma máquina o máquinas diferentes . Pero podría haber más de un programa en cada máquina corriendo al al mismo tiempo.

Im usando Delphi7, con Indy 9.0.18

donde im pegado es que si yo debería usar UDP (TIdUDPClient/Server) o IP Multicast (TIdIPMCastClient/Server) o si es incluso posible ...

He logrado que funcione con IP Multi Cast con un cliente por máquina, pero incluso después de muchos intentos con diferentes enlaces ... puertos máx/mín, etc., parece que no puedo encontrar una solución.

Respuesta

8

Creo que está buscando la opción de socket SO_REUSEADDR. Establecer esa opción en un socket permite que varios sockets escuchen en el mismo puerto. Para Windows multicast, garantiza que el mensaje se entregará a todos los sockets (de lo contrario, el mensaje solo se envía a un socket, al azar).

Normalmente hace esto llamando a setsockopt, pero no soy un desarrollador de Delphi, así que no estoy seguro de cómo es su API. Este question parece mostrar un ejemplo de alguien haciendo algo similar en Delphi.

+0

Excelente, SO_REUSEADDR esa es la sugerencia que necesitaba –

+1

En mac OSX, tuve que agregar: 'sock.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEPORT, True)' –

5

Nunca he hecho esto, pero parece que "mailslots" es lo que necesita. Transmitirá un mensaje en la red local y recibirá respuestas de otras estaciones de trabajo que saben cómo responder. Así es como funciona el administrador de licencia del "armadillo" popular (para asegurarse de que las claves de registro no estén "suscritas en exceso"). Mi aplicación (ClipMate) usa Armadillo como contenedor de protección (envoltorio de shareware). Cuando un usuario registrado ejecuta la aplicación, verifica si esa misma clave está siendo utilizada por otras máquinas en la misma red. Básicamente dice: "Estoy usando la licencia 1234, ¿y tú?" Espera respuestas (hago esto en un hilo separado durante el inicio, así que no bloqueo mi inicio). Si otras estaciones de trabajo informan que están usando la misma clave, verifico el conteo en función de la cantidad de asientos que contiene la licencia. No estoy completamente seguro de que es tan robusta en Windows 7 ....

+1

Existe una simple implementación de ranura de correo en http://www.funkypuppy.com/, funciona muy bien en Delphi 7. – skamradt

+0

Las ranuras de correo sonaron prometedoras. Probé la implementación desde funkypuppy pero parece que no puedo tener más de 2 programas recibiendo mensajes en la misma computadora. Tal vez estoy haciendo mal? –

+0

Aunque las listas de correo de Windows se 'publicitan' como un mecanismo de difusión, eso se refiere a receptores en diferentes máquinas. En una sola máquina, solo un proceso puede poseer y leer una ranura de correo de un nombre en particular. –

1

RemObjects tiene una buena solución para esto: ROZeroConf

Antes de que estaba disponible, he hecho algo así a mí mismo con TROBroadcastChannel de RemObjects SDK (UDP e Indy basados). Dentro de ese componente, llama al TIdUDPBase.Broadcast para enviar y TIdUDPClient.ReceiveBuffer para recibir respuestas.

(por cierto, la difusión UDP sólo funciona en la misma red/subred, ROZeroConf es una solución mejor)

2

Es definitivamente posible.

Re "UDP o multidifusión", estás hablando de manzanas y naranjas. La multidifusión es un concepto de IP, por lo que puede felizmente UDP sobre IP de multidifusión, o sobre IP de difusión.

Si está de acuerdo con la limitación de tener todos los clientes link-local (enrutadores, etc. generalmente no reenvían paquetes de difusión), yo diría que simplemente continúe con la transmisión. TIdUdpBase.Broadcast será tu amigo aquí.

Actualización: Con multidifusión o difusión, solo puede tener un socket vinculado a un par de IP/puerto en particular. Por lo tanto, si desea que varios clientes estén todos escuchando la MISMA transmisión/multidifusión, creo que necesitará un cliente de despachador adicional. Este cliente despachador recibe transmisiones y notifica a todos los clientes en la máquina.

Dentro de cada uno de sus clientes tiene un pequeño procedimiento de registro que dice "Intente vincularse al puerto al cual se envían las transmisiones. Si puede, configure un cliente de despachador en ese puerto. Si no puede, el despachador ya creado, y registrarse en ese despachador ".

Ese proceso de registro podría ser tan simple como vincularlo a cualquier puerto disponible en la IP de localhost, y decirle al despachador "Envíe transmisiones a esta IP/puerto".

Actualización:Christopher Chase tiene la idea correcta. Acabo de terminar casi la misma solución exacta como la suya, excepto remendé IdIPMCastClient, añadiendo un REUSEADDR propiedad: Boolean y cambiando TIdIPMCastClient.GetBinding añadiendo

if Self.ReuseAddr then begin 
    SetReuseAddr := Id_SO_True; 
    Bindings[i].SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, @SetReuseAddr, Sizeof(SetReuseAddr)); 
end; 

entre las llamadas a AllocateSocket y de enlace (donde SetReuseAddr: entero).

+0

Gracias por la respuesta, las limitaciones de UDP y multidifusión son realmente necesarias, es decir, no quiero que los mensajes salgan de la lengua, etc., pero el problema parece que no puedo resolver es tener dos clientes en la misma computadora para escuchar transmisiones –

+0

He agregado una nueva propiedad ReuseSocket a TIdIPMCastClient en Indy 10. –

1

con la indirecta de shf301, este es el código que tengo que trabajar con

he creado un nuevo TIdIPMCastClient

TIdReUseIPMCastClient = class(TIdIPMCastClient) 
    private 
    procedure SetReUseAddr(InBinding: TIdSocketHandle; const Value: boolean); 
    protected 
    function GetBinding: TIdSocketHandle; override; 
    public 
    end; 

añade el Procedimiento

procedure TIdReUseIPMCastClient.SetReUseAddr(InBinding: TIdSocketHandle; const Value: boolean); 
var 
    tempi: integer; 
begin 
    if Assigned(InBinding) and InBinding.HandleAllocated then 
    begin 
    tempi := iif(Value, 1, 0); 
    InBinding.SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, PChar(@tempi), SizeOf(tempi)); 
    end; 
end; 

copiado el GetBinding código de TIdIPMCastClient y agregó SetReUseAddr antes del enlace

Bindings[i].AllocateSocket(Id_SOCK_DGRAM); 
    SetReUseAddr(Bindings[i], True); 
    Bindings[i].Bind; 
+0

He agregado una nueva propiedad ReuseSocket a TIdIPMCastClient en Indy 10. –

Cuestiones relacionadas