|
| 1 | +#!/usr/bin/env python |
| 2 | +# |
1 | 3 | # Send/receive UDP multicast packets. |
2 | 4 | # Requires that your OS kernel supports IP multicast. |
3 | | -# This is built-in on SGI, still optional for most other vendors. |
4 | 5 | # |
5 | 6 | # Usage: |
6 | | -# mcast -s (sender) |
7 | | -# mcast -b (sender, using broadcast instead multicast) |
8 | | -# mcast (receivers) |
| 7 | +# mcast -s (sender, IPv4) |
| 8 | +# mcast -s -6 (sender, IPv6) |
| 9 | +# mcast (receivers, IPv4) |
| 10 | +# mcast -6 (receivers, IPv6) |
9 | 11 |
|
10 | 12 | MYPORT = 8123 |
11 | | -MYGROUP = '225.0.0.250' |
| 13 | +MYGROUP_4 = '225.0.0.250' |
| 14 | +MYGROUP_6 = 'ff15:7079:7468:6f6e:6465:6d6f:6d63:6173' |
| 15 | +MYTTL = 1 # Increase to reach other networks |
12 | 16 |
|
13 | | -import sys |
| 17 | +import ipaddr |
14 | 18 | import time |
15 | 19 | import struct |
16 | | -from socket import * |
17 | | - |
| 20 | +import socket |
| 21 | +import sys |
18 | 22 |
|
19 | | -# Main program |
20 | 23 | def main(): |
21 | | - flags = sys.argv[1:] |
22 | | - # |
23 | | - if flags: |
24 | | - sender(flags[0]) |
| 24 | + group = MYGROUP_6 if "-6" in sys.argv[1:] else MYGROUP_4 |
| 25 | + |
| 26 | + if "-s" in sys.argv[1:]: |
| 27 | + sender(group) |
| 28 | + else: |
| 29 | + receiver(group) |
| 30 | + |
| 31 | +def _sockfam(ip): |
| 32 | + """Returns the family argument of socket.socket""" |
| 33 | + if ip.version == 4: |
| 34 | + return socket.AF_INET |
| 35 | + elif ip.version == 6: |
| 36 | + return socket.AF_INET6 |
25 | 37 | else: |
26 | | - receiver() |
| 38 | + raise ValueError('IPv' + ip.version + ' is not supported') |
| 39 | + |
| 40 | +def sender(group): |
| 41 | + group_ip = ipaddr.IP(group) |
27 | 42 |
|
| 43 | + s = socket.socket(_sockfam(group_ip), socket.SOCK_DGRAM) |
28 | 44 |
|
29 | | -# Sender subroutine (only one per local area network) |
30 | | -def sender(flag): |
31 | | - s = socket(AF_INET, SOCK_DGRAM) |
32 | | - if flag == '-b': |
33 | | - s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) |
34 | | - mygroup = '<broadcast>' |
| 45 | + # Set Time-to-live (optional) |
| 46 | + ttl_bin = struct.pack('@i', MYTTL) |
| 47 | + if group_ip.version == 4: |
| 48 | + s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_bin) |
35 | 49 | else: |
36 | | - mygroup = MYGROUP |
37 | | - ttl = struct.pack('b', 1) # Time-to-live |
38 | | - s.setsockopt(IPPROTO_IP, IP_MULTICAST_TTL, ttl) |
39 | | - while 1: |
40 | | - data = repr(time.time()) |
41 | | -## data = data + (1400 - len(data)) * '\0' |
42 | | - s.sendto(data, (mygroup, MYPORT)) |
| 50 | + s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin) |
| 51 | + |
| 52 | + while True: |
| 53 | + data = repr(time.time()).encode('utf-8') + b'\0' |
| 54 | + s.sendto(data, (group_ip.ip_ext_full, MYPORT)) |
43 | 55 | time.sleep(1) |
44 | 56 |
|
45 | 57 |
|
46 | | -# Receiver subroutine (as many as you like) |
47 | | -def receiver(): |
48 | | - # Open and initialize the socket |
49 | | - s = openmcastsock(MYGROUP, MYPORT) |
50 | | - # |
51 | | - # Loop, printing any data we receive |
52 | | - while 1: |
53 | | - data, sender = s.recvfrom(1500) |
54 | | - while data[-1:] == '\0': data = data[:-1] # Strip trailing \0's |
55 | | - print(sender, ':', repr(data)) |
| 58 | +def receiver(group): |
| 59 | + group_ip = ipaddr.IP(group) |
56 | 60 |
|
57 | | - |
58 | | -# Open a UDP socket, bind it to a port and select a multicast group |
59 | | -def openmcastsock(group, port): |
60 | | - # Import modules used only here |
61 | | - import string |
62 | | - import struct |
63 | | - # |
64 | 61 | # Create a socket |
65 | | - s = socket(AF_INET, SOCK_DGRAM) |
66 | | - # |
| 62 | + s = socket.socket(_sockfam(group_ip), socket.SOCK_DGRAM) |
| 63 | + |
67 | 64 | # Allow multiple copies of this program on one machine |
68 | 65 | # (not strictly needed) |
69 | | - s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) |
70 | | - # |
| 66 | + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 67 | + |
71 | 68 | # Bind it to the port |
72 | | - s.bind(('', port)) |
73 | | - # |
74 | | - # Look up multicast group address in name server |
75 | | - # (doesn't hurt if it is already in ddd.ddd.ddd.ddd format) |
76 | | - group = gethostbyname(group) |
77 | | - # |
78 | | - # Construct binary group address |
79 | | - bytes = list(map(int, string.split(group, "."))) |
80 | | - grpaddr = 0 |
81 | | - for byte in bytes: grpaddr = (grpaddr << 8) | byte |
82 | | - # |
83 | | - # Construct struct mreq from grpaddr and ifaddr |
84 | | - ifaddr = INADDR_ANY |
85 | | - mreq = struct.pack('ll', htonl(grpaddr), htonl(ifaddr)) |
86 | | - # |
87 | | - # Add group membership |
88 | | - s.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, mreq) |
89 | | - # |
90 | | - return s |
91 | | - |
92 | | - |
93 | | -main() |
| 69 | + s.bind(('', MYPORT)) |
| 70 | + |
| 71 | + # Join group |
| 72 | + if group_ip.version == 4: # IPv4 |
| 73 | + mreq = group_ip.packed + struct.pack('=I', socket.INADDR_ANY) |
| 74 | + s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) |
| 75 | + else: |
| 76 | + mreq = group_ip.packed + struct.pack('@I', 0) |
| 77 | + s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) |
| 78 | + |
| 79 | + # Loop, printing any data we receive |
| 80 | + while True: |
| 81 | + data, sender = s.recvfrom(1500) |
| 82 | + while data[-1:] == '\0': data = data[:-1] # Strip trailing \0's |
| 83 | + print(str(sender) + ' ' + repr(data)) |
| 84 | + |
| 85 | + |
| 86 | +if __name__ == '__main__': |
| 87 | + main() |
0 commit comments