In my last project I solved a big issue that I needed to receive notification from an hardware via a Multicast UDP socket.
If you want to know more about Multicast and Unicast in Flash Platform I really suggest to read Flashrealtime blog.
Like you know the Flash Platform can use multicast with RTMFP protocol so you can use it Flash to Flash or Flash to Flash Media Server but not Flash to hardware for example.
In my case I needed a Multicast UDP socket in a local application that communicate with an hardware that was my server, so I thought to realize a Python application for Mac OS X and Windows that could help me to solve this problem.
Python is my second favorite programming language after ActionScript (obviously) and this time Python saves me to accomplish my project.
I want to start with Python side, my goal is to connect to a multicast socket and get all data and then send them to a unicast UDP socket server that we will create in Adobe AIR in next example.
#!/usr/bin/env python # encoding: utf-8 import socket, select ANY = '0.0.0.0' MCAST_ADDR = '239.0.1.1' MCAST_PORT = 10300 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sock.bind((ANY,MCAST_PORT)) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) status = sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MCAST_ADDR) + socket.inet_aton(ANY)); sock.setblocking(0) print 'socket UDP multicast ready' udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); while 1: ins, outs, errs = select.select( [ sock ] , [], [], 1.0) try: data, addr = sock.recvfrom(1024) except socket.error, e: pass else: print data udp.sendto(data, (socket.gethostbyname('127.0.0.1'), 10303)) socket.close()
First few lines we define the multicast socket server ip (that in my case is pointing to the hardware) and the socket port to communicate with.
Then I create the UDP multicast socket and the UDP unicast socket (udp var); in the while loop I set the socket timeout and if I receive data trough the multicast socket I send everything to unicast one.
After that to create an application without any dependency in Windows or Mac OS X, you need to use a couple of Python libraries called py2app and py2exe.
Both allow you to create an executable file from your Python script for mac or win without any dependencies, in Windows side you have only to remember which kind of Python dll you have to incapsulate in your AIR application but we take a look at that in a while.
To create the executable file you have to create a setup script in Python for both operating systems, I suggest to create something like that:
FOR WINDOWS:
from distutils.core import setup import py2exe,sys,os origIsSystemDLL = py2exe.build_exe.isSystemDLL def isSystemDLL(pathname): if os.path.basename(pathname).lower() in ("msvcp71.dll", "dwmapi.dll"): return 0 return origIsSystemDLL(pathname) py2exe.build_exe.isSystemDLL = isSystemDLL setup(windows=['myPythonScript.py'])
FOR MAC:
from setuptools import setup APP = ['myPythonScript.py'] DATA_FILES = [] OPTIONS = {'argv_emulation': True} setup( app=APP, data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], )
Then if you want to create your executable files you have only need to launch the command on command prompt or terminal (you can find more informations on how to customize those setup scripts in each library’s website):
FOR WINDOWS:
python setup.py py2exe
FOR MAC:
python setup.py py2app
Now we can start with the AIR part, like you know with AIR 2 you can work with Native Process and this is the case to use them:
const WIN_PATH:String = "win/socket.exe"; const OSX_PATH:String = "socket.app/Contents/MacOS/socket"; var nativep:NativeProcessStartupInfo = new NativeProcessStartupInfo(); var finalPath:String; var f:File if(Capabilities.os.substr(0, 3) == "Win"){ finalPath = WIN_PATH }else{ finalPath = OSX_PATH } f = File.applicationDirectory.resolvePath(finalPath); nativep.executable = f; var process:NativeProcess = new NativeProcess(); process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData); process.addEventListener(NativeProcessExitEvent.EXIT, onExit); process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError); process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError); process.start(nativep); var udp:DatagramSocket = new DatagramSocket(); udp.addEventListener(DatagramSocketDataEvent.DATA, getData); udp.bind(10303); udp.receive(); function getData(e:DatagramSocketDataEvent):void{ trace(">>>>>" + e.data.readUTFBytes( e.data.bytesAvailable )) } function onOutputData(event:ProgressEvent):void{ trace(process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable)); } function onErrorData(event:ProgressEvent):void{ trace(process.standardError.readUTFBytes(process.standardError.bytesAvailable)); } function onExit(event:NativeProcessExitEvent):void{ trace(event.exitCode); } function onIOError(event:IOErrorEvent):void{ trace(event.toString()); }
In this simple script I’m choosing the right executable file for the operating system where my AIR application is working on and then I launch the native process to start the multicast socket.
Finally I listen for the unicast socket and I trace on the output panel the messages that I receive from the hardware.
I think this is an interesting way to extend Adobe AIR with Python that open new possibilities on the desktop side, think for example to create a Python bluetooth extension for Adobe AIR, it could be so interesting add this feature to AIR apps isn’t it?
What about using an higher-level api for networking like TwistedMatrix?
I didn’t know it, but I’ll take a look soon!
thank you for your suggestion Christopher 😀
See the PyAMF UDP example for a Twisted example: http://www.pyamf.org/tutorials/actionscript/udp.html
I’m trying to build the app on mac, but it giving me the following error:
error: argv-emulation is not supported for 64-bit executables
If I remember well there are some issue with 64bit executables, but try to see on py2app website
ok thanks I’ve been reading a lot of things about this. Could you email me, really want to get this running. maikeljsibbald@gmail.com
I’m running the script like this now: python PyVuzixConnector.py py2app
but seems to be giving me:
Traceback (most recent call last):
File “PyVuzixConnector.py”, line 14, in
sock.bind((ANY,MCAST_PORT))
File “”, line 1, in bind
socket.error: [Errno 48] Address already in use
is there a way to check if I’m connecting to that app with air?
If the error is “Address already in use” it means that you need to kill the previous script that is working in your activity monitor.
Then your script probably works fine.
I solve this issue closing before open any socket connection directly form python
Very efficient codes.It is the right executable file for the operating system where AIR application is working on and then I launch the native process to start the multicast socket
Very nice example thanks. Have you ever tried using python as a NativeExtension instead of NativeProcess? I have been looking for example of that since I noticed python was listed as a possible stack. Unfortunately have not found anything.
Hi, when I worked in this problem I had a constrain to work with AIR 2.6 so I couldn’t use NativeExtension.
Anyway NativeExtension couldn’t be created with Python but with C++, Objective-C or Java depends on the platform you are working on.
I hope this helps you