Network programming for PyS60 (XII)
by Marcelo Barros
Until now we have used only TCP in our examples due to its reliability. In fact, UDP could be used when reliability is not an issue or when you implement it in the application layer. However, UDP is your only solution when talking about multicast and broadcast. In this post we will explore the multicast topic from the programmers perspective (see the related theory in our last post). At this moment, I think it may be more interesting than generic UDP sockets issues (I will explore it later).
Consider the multicast address 188.8.131.52. The following PyS60 program will send a hello world message at each two seconds to this address and in the screenshot you can see the multicast frame, addressed to MAC 01:00:5e:4a:14:1e. Since we are dealing with UDP, the socket is created using SOCK_DGRAM and not SOCK_STREAM.
# Marcelo Barros de Almeida # email@example.com import time try: from btsocket import * except: from socket import * from appuifw import note, popup_menu def sel_access_point(): aps = access_points() if not aps: return None ap_labels = map(lambda x: x['name'],aps) item = popup_menu(ap_labels, u"Access point:") if item is None: return None apo = access_point(aps[item]['iapid']) set_default_access_point(apo) return apo GROUP = "184.108.40.206" PORT = 54321 apo = sel_access_point() if apo is not None: apo.start() sock = socket(AF_INET, SOCK_DGRAM) while True: n = sock.sendto('hello world',(GROUP,PORT)) print "Message sent (%d bytes)" % n time.sleep(2)
Instead the known send() function, here it is used sendto(). sendto has an additional parameter, indicating the message destination in the format (address,port). This is necessary since we do not have a phase for connection establishment, with explicit call to connect(), as it is usual in TCP sockets. Thus, client UDP sockets just need to be created and explicitly addressed in sendto(). You can connect an UDP socket, for instance, and use send()/recv() primitives instead non-connected versions sendto()/recvfrom() if you want.
For receiving multicast messages, first create the socket and bind it to the interface. After, it is necessary to join to the desired group, as discussed in our last post. This can be done using two calls to setsockopt() function. The first call enables multicast reception for the interface and the second one tell to the network card which IP we are waiting for. setsockoption has three parameters (there are many socket options and it is better to dedicate a post to the most important ones):
- level:it is used to selected a group of options, like SOL_SOCKET for socket options and SOL_IP for options related to IP layer.
- option name: the value of the option
- option value: new value to the option
inet_aton() converts an IPv4 address to 32-bit packed binary format, as a string four characters (see Python docs).
The last step is to call recvfrom(). Again, recvfrom() is similar to recv() but it will return the address of the incoming package as well (remember: we are not connected).
Using Python 1.9.5 (socket module) it was only possible to send broadcast messages. For receiving, it is necessary to select the access point first but access point functions are only in btsocket and they do not affect socket module. Moreover, btsocket does not have the necessary socket options. So, I did the following PC code for receiving the first ten multicast messages:
import time import sys import socket GROUP = "220.127.116.11" PORT = 54321 def join_grp(sock,grp,itf): # enabling multicasting option for itf sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(itf)) # joing to multicast group grp in interface itf sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(grp)+socket.inet_aton(itf)) def leave_grp(sock,grp): # removing sock.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(grp)+socket.inet_aton('0.0.0.0')) itf_addr = '10.0.0.101' sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) sock.bind((itf_addr,PORT)) join_grp(sock,GROUP,itf_addr) n = 0 while True: (data,addr) = sock.recvfrom(1500) print "Received ",data,"from",addr n = n + 1 if n > 10: break leave_grp(sock,GROUP)
In time: I had a lot of problem during this test session. They are below, just for helping you.
- Windows firewall enabled
- Access point with MAC filter enabled
- Wireshark only working in non-promiscuous mode for my Acer Aspire One
- Missing socket options in PyS60 socket API
- Python 1.9.5 seems with better network support than Python 1.4.5
- Network programming for PyS60 (XIII) In our last post we talked about multicast, a special...
- Network programming for PyS60 (XV) As discussed in post III, TCP sockets have flow...
- Network programming for PyS60 (XI) We have already talked about IP addressing in the post...
- Network programming for PyS60 (VII) Everything is about “protocols” in computer networks, doesn’t it ?...
- Network programming for PyS60 (IV) Network programming is innocuous if you don’t have at least...
Related posts brought to you by Yet Another Related Posts Plugin.