2010-04-09 5 views
10

Sto cercando di scrivere un server/servizio che trasmette un messaggio sulla lan di un secondo o giù di lì, Un po 'come un servizio di scoperta.MultiCast Messaggi a più client sulla stessa macchina

Il messaggio deve essere ricevuto da molteplici programmi client che potrebbero essere sulla stessa macchina o differenti macchine. Ma potrebbe esserci più di un programma su ciascuna macchina che funziona allo stesso tempo su .

im utilizzando Delphi7, con indy 9.0.18

dove im bloccato è se dovrei usare UDP (TIdUDPClient/Server) o IP multicast (TIdIPMCastClient/Server) o se la sua ancora possibile ...

Ive è riuscito a farlo funzionare con IP Multi Cast con un client per macchina, ma anche dopo molti trys con collegamenti diversi ... porte max/min ecc., Non riesco a trovare una soluzione.

risposta

8

Penso che stiate cercando l'opzione socket SO_REUSEADDR. L'impostazione di tale opzione su un socket consente a più socket di ascoltare sulla stessa porta. Per Windows multicast garantisce che il messaggio venga recapitato a tutti i socket (altrimenti il ​​messaggio va solo a un socket, in modo casuale).

Solitamente si esegue questa operazione chiamando setsockopt, ma io non sono uno sviluppatore Delphi quindi non sono sicuro di come sia la tua API. Questo question sembra mostrare un esempio di qualcuno che fa qualcosa di simile in Delphi.

+0

Eccellente, SO_REUSEADDR questo è il suggerimento che mi serviva –

+1

su Mac OSX, ho dovuto aggiungere: 'sock.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEPORT, true)' –

5

Non l'ho mai fatto, ma sembra che "mailslot" sia quello che ti serve. Diffonderà un messaggio sulla rete locale e riceverà eventuali risposte da altre workstation che sanno come rispondere. È così che funziona il famoso gestore di licenze "armadillo" (per assicurarsi che le chiavi di registrazione non siano "sovra-sottoscritte"). La mia app (ClipMate) usa Armadillo come wrapper di protezione (wrapper shareware). Quando un utente registrato esegue l'app, controlla se quella stessa chiave è in uso da altri computer sulla stessa rete. In pratica dice: "Uso la licenza 1234, e tu?" Aspetta le risposte (lo faccio in un thread separato durante l'avvio in modo da non bloccare il mio avvio). Se altre workstation segnalano che stanno utilizzando la stessa chiave, controllo il conteggio sul numero di postazioni contenute nella licenza. Io non sono del tutto sicuro che sia così robusta su Windows7 ....

+1

Una semplice implementazione dello slot di posta esiste all'indirizzo http://www.funkypuppy.com/, funziona perfettamente con Delphi 7. – skamradt

+0

Gli slot di posta sembravano promettenti. Ho provato l'implementazione da funkypuppy ma non riesco a vedere più di 2 programmi che ricevono messaggi sullo stesso computer. Forse sto sbagliando? –

+0

Anche se i mailslot di Windows sono "pubblicizzati" come meccanismo di trasmissione, ciò si riferisce ai ricevitori su macchine diverse. Su una singola macchina solo un processo può possedere e leggere un mailslot di un nome particolare. –

1

RemObjects ha una bella soluzione per questo: ROZeroConf

Prima che era disponibile, ho fatto qualcosa di simile me stesso con TROBroadcastChannel di RemObjects SDK (basato su UDP e Indy). All'interno di quel componente, chiama TIdUDPBase.Broadcast da inviare e per ricevere le risposte.

(a proposito, UDP trasmissione funziona solo sulla stessa rete/sottorete, ROZeroConf è una soluzione migliore)

2

E 'sicuramente possibile.

Re "UDP o multicast", stai parlando di mele e arance. Multicast è un concetto IP, quindi puoi felicemente UDP su IP multicast o su IP broadcast.

Se si sta bene con la limitazione di avere tutti i client link-local (i router ecc. In genere non inoltrano pacchetti di trasmissione), direi semplicemente andare con broadcast. TIdUdpBase.Broadcast sarà tuo amico qui.

Aggiornamento: Con multicast o broadcast, si può avere un solo legame socket con qualsiasi particolare coppia IP/porta. Quindi, se vuoi che più client ascoltino tutti la trasmissione/multicast SAME, penso che avrai bisogno di un client di dispatcher in più. Questo client del dispatcher riceve le trasmissioni e notifica ogni client sulla macchina.

All'interno di ciascun client è presente una piccola procedura di registrazione che dice "Prova a collegarsi alla porta a cui sono inviate le trasmissioni. Se è possibile, impostare un client di dispatcher su quella porta. già creato e registrati a quel dispatcher. "

Questo processo di registrazione potrebbe essere semplice come il binding a qualsiasi porta disponibile sull'IP localhost e dire al dispatcher "Si prega di inviare trasmissioni a questo IP/porta".

Aggiornamento:Christopher Chase ha l'idea giusta. Ho appena finito di quasi la stessa soluzione esatta come la sua, tranne che mi patchato IdIPMCastClient, l'aggiunta di una ReuseAddr immobile: booleana e cambiando TIdIPMCastClient.GetBinding aggiungendo

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

tra le chiamate a AllocateSocket e Bind (dove SetReuseAddr: Integer).

+0

Grazie per la risposta, le limitazioni di UDP e multicast sono effettivamente richieste, cioè non voglio che i messaggi lascino la lan ecc, ma il problema che non riesco a risolvere è avere due client sullo stesso computer che ascoltano le trasmissioni –

+0

ho aggiunto una nuova proprietà ReuseSocket a TIdIPMCastClient in Indy 10. –

1

con il suggerimento da shf301, questo è il codice ho ottenuto di lavorare con

Ho creato un nuovo TIdIPMCastClient

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

aggiunta la procedura

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; 

copiato la GetBinding Codice da TIdIPMCastClient e aggiunto SetReUseAddr prima del binding

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

Ho aggiunto una nuova proprietà ReuseSocket a TIdIPMCastClient in Indy 10. –