2014-05-23 7 views
2

J'ai un serveur UDP qui se lie à toutes les adresses sur un système, je voudrais savoir à quelle adresse IP le message a été adressé. Des idees pour faire cela?Ruby, obtenir l'adresse d'arrivée du message UDP

Voici mon code exemple:

sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) 
sock.bind(Addrinfo.udp('', 2400)) 
while(true) 
    sockset = IO.select([sock]) 
    sockset[0].each do |sock| 
     data = sock.recvfrom(1024) 
     puts "data: " + data.inspect 
    end 
end 
sock.close 

Cela produira quelque chose comme:

données: [ "message test \ n", # < addrinfo: 172.16.5.110:41949 UDP> Est-il possible de définir une option de socket, ou quelque chose, pour renvoyer l'adresse IP locale?

Juste une note, cela doit aussi fonctionner pour IPv6. Merci d'avance, Dave.

+0

Mais l'adresse IP locale n'est-elle pas "172.16.5.110" dans votre exemple? –

Répondre

1

UNIX Network Programming a ceci à dire à ce sujet même:

Avec un socket UDP, cependant, l'adresse IP de destination ne peut être obtenue en réglant l'option prise IP_RECVDSTADDR pour IPv4 ou l'option prise IPV6_PKTINFO pour IPv6 puis en appelant recvmsg au lieu de recvfrom.

bibliothèque socket Ruby a recvmsg qui est un peu plus facile à utiliser que la fonction C sous-jacente, mais doit encore un peu de travail pour obtenir les informations nécessaires. L'adresse IP de destination est incluse dans le tableau des données auxiliaires renvoyées par recvmsg. Voici une version de votre code adapté pour utiliser recvmsg et obtenir l'adresse de destination pour IPv4:

require 'socket' 
require 'ipaddr' 

sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) 

# Set the required socket option: 
sock.setsockopt :IPPROTO_IP, :IP_RECVDSTADDR, true 
sock.bind(Addrinfo.udp('0.0.0.0', 2400)) 

while(true) 
    sockset = IO.select([sock]) 
    sockset[0].each do |sock| 

     mesg, sender, _, *anc_data = sock.recvmsg 

     # Find the relevant data and extract it into a string 
     dest = IPAddr.ntop(anc_data.find {|d| d.cmsg_is?(:IP, :RECVDSTADDR)}.data) 

     puts "Data: #{mesg}, Sender: #{sender.ip_address}, Destination: #{dest}" 

    end 
end 

Et voici une version pour IPv6. Il y a aussi une option de socket RECVPKTINFO, qui peut avoir été remplacée par PKTINFO - selon votre système, vous devrez peut-être l'utiliser à la place.

require 'socket' 

sock = Socket.new(Socket::AF_INET6, Socket::SOCK_DGRAM, 0) 

# Set the socket option for IP6: 
sock.setsockopt :IPPROTO_IPV6, :IPV6_PKTINFO, true 

sock.bind(Addrinfo.udp('0::0', 2400)) 

while(true) 
    sockset = IO.select([sock]) 
    sockset[0].each do |sock| 

     mesg, sender, _, *anc_data = sock.recvmsg 

     # Find and extract the destination address 
     dest = anc_data.find {|d| d.cmsg_is?(:IPV6, :PKTINFO)}.ipv6_pktinfo_addr 

     puts "Data: #{mesg}, Sender: #{sender.ip_address}, Destination: #{dest.ip_address}" 
    end 
end 

Ruby fournit également un Socket.udp_server_loop method, ce qui donne le message et un objet UDPSource au bloc que vous fournissez, et cet objet source a un champ local_address. Looking at the source cela semble vérifier les données PKTINFO comme je le fais ci-dessus pour obtenir l'adresse de destination pour les demandes IPv6, mais pas pour IPv4. Cette méthode se lie individuellement à toutes les adresses IP disponibles et utilise simplement l'adresse de l'interface entrante pour les demandes IP4, ce qui peut ne pas être exact pour un weak end system model. Cependant, il pourrait être plus simple pour vous d'utiliser Socket.udp_server_loop.

+0

Une mise en garde: je ne suis pas vraiment capable de tester cela correctement puisque je n'ai qu'un seul système disponible. Il semble que ce soit correct, mais je serais prêt à entendre des confirmations ou des désaccords. – matt

Questions connexes