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 224.202.20.30. 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
# marcelobarrosalmeida@gmail.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 = "224.202.20.30"
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.

Multicasting from mobile.

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 = "224.202.20.30"
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)

Receiving multicast

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

Related posts:

  1. Network programming for PyS60 (XIII) In our last post we talked about multicast, a special...
  2. Network programming for PyS60 (XI) We have already talked about IP addressing in the post...
  3. Network programming for PyS60 (XV) As discussed in post III, TCP sockets have flow...
  4. Network programming for PyS60 (IV) Network programming is innocuous if you don't have at least...
  5. Network programming for PyS60 (VI) Before presenting some server code it is important to discuss...

Related posts brought to you by Yet Another Related Posts Plugin.