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.
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)
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:
- Network programming for PyS60 (XIII) In our last post we talked about multicast, a special...
- Network programming for PyS60 (XI) We have already talked about IP addressing in the post...
- Network programming for PyS60 (XV) As discussed in post III, TCP sockets have flow...
- Network programming for PyS60 (IV) Network programming is innocuous if you don't have at least...
- 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.
Hello, Marcello,
Your posts a really informative and nice.
I would like to discuss something about UDP. Because the protocol is not controlled, we face few problems, for example: we have an application in mobile phone, which is waiting for some data from server with real IP address. The problem is that we don’t have open ports in mobile phone while connecting through 3G or GPRS. I managed to use it by sending something from mobile phone first, and then using that connection with given random port in server. This way we need an open port in one side of connection. Maybe You have some suggestions about that.
Another problem in using UDP is that we have to manually control all traffic to UDP port. It is necessary to limit the traffic or there will be many lost packets.
Thanks for Your posts!
Hello Vygandas, fine ?
> his way we need an open port in one side of connection
I think this is an operator issue. In Brazil, some operators provides real IPs and do not block incoming connections to your phone. I did a test here with TIM/Brazil and I could connect to the phone. But I know that we have some operators that block incoming connections and provide just IPs reserved for internal networking. In this case, you need to pay for some special services. Please, check this info with your mobile operator.
> Another problem in using UDP is that we have to manually control all traffic to UDP port.
UDP issues… Possible suggestions: increase receiver buffer according to the equation:
buffer_size = maximum_latency*average_rate
But it is necessary to use setsockopt (option SO_RCVBUF in SOL_SOCKET group). I need to check if this option is available for PyS60.
yeah, my project is fine, but it doesn’t work with 3g. Also my software should be more accurate and safer to use. As you know, it is in deep beta version right now 🙂
> you need to pay for some special services
maybe not… as i said it is possible to open a session if mobile sends some data to server, and server gets that data from mob_ip:rand_port, so we can use that session to send data from server to mobile…
> buffer_size = maximum_latency*average_rate
i used something similar in my project.
Once again, thank you for your posts.
Yes, your solution avoid any additional costs. But I saw some projects where people just pay for better networks services and real IPs. But this may be a local issue.