Created
March 25, 2015 11:47
-
-
Save maghul/4ddaa5879ef0ece5dbe3 to your computer and use it in GitHub Desktop.
Discovery using UDP in GJS/Gnome-Shell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ------ | |
// Broadcasts discovery requests for Logitech Media Server and listens to replies and publish messages | |
// will send all updates as a hash to the callback. | |
// The content of the hash is | |
// IPAD: The address of the server | |
// NAME: The name of the server | |
// JSON: The port to connect to for the JSON API | |
// UUID: The unique identifier of the server instance. | |
// ------ | |
const GLib = imports.gi.GLib; | |
const Gio = imports.gi.Gio; | |
const ByteArray = imports.byteArray; | |
const Mainloop = imports.mainloop; | |
// Create a Discovery request. Variadaic function which takes | |
// a list of request strings and formats them for LMS server. | |
function appreq() | |
{ | |
var ba= GLib.ByteArray.new(256); | |
ba[0]= 0x65; // 'e'=Start of extended request | |
for(var jj=0; jj<arguments.length; jj++) | |
{ | |
var req= arguments[jj]; | |
for ( var ii=0; (ii<req.length); ++ii) { | |
var ch= req.charCodeAt(ii); | |
ba[ba.length]= ch; | |
} | |
ba[ba.length]= 0; | |
} | |
return ba; | |
} | |
// Slice a ByteArray and return as a string. | |
function sliceToString( bb, s, e ) | |
{ | |
var rv = ""; | |
for ( var ii=s; (ii<e); ++ii) { | |
rv += String.fromCharCode(bb[ii]); | |
} | |
return rv; | |
} | |
// Parse a discovery response. | |
// Will currently only handle extedended responses. (messages beginning with 'E') | |
function parseResponse( resp, n, src ) | |
{ | |
var rv= {}; | |
if (resp[0]==0x45) { | |
rv["src"]= src; | |
rv["IPAD"]= src.get_address().to_string(); | |
for ( var ii=1; (ii<n); ) { | |
var rtypa= sliceToString(resp,ii,ii+4); | |
var rlen= resp[ii+4]; | |
var rdata= sliceToString(resp,ii+5, ii+5+rlen); | |
rv[rtypa]= rdata; | |
ii += 5+rlen; | |
} | |
return rv; | |
} | |
return null; | |
} | |
function dump(bb,s,e) | |
{ | |
var rv = ""; | |
for ( var ii=s; (ii<e); ++ii) { | |
rv += " "+bb[ii]; | |
} | |
log( rv); | |
} | |
function scanForLMS(context, timer, callback) | |
{ | |
let socket = new Gio.Socket({family: Gio.SocketFamily.IPV4, protocol: Gio.SocketProtocol.UDP, type: Gio.SocketType.DATAGRAM}); | |
let address = new Gio.InetSocketAddress({ | |
address: Gio.InetAddress.new_any(Gio.SocketFamily.IPV4), | |
port: 3483, | |
}); | |
let dest = new Gio.InetSocketAddress({ | |
address: Gio.InetAddress.new_from_string("255.255.255.255"), | |
port: 3483, | |
}); | |
socket.init(null); | |
socket.bind( address, true); | |
socket.set_broadcast(true); | |
socket.set_blocking(false); | |
let wcond= GLib.IOCondition.IN | GLib.IOCondition.OUT; | |
var rx_fn= function () { | |
let buf = GLib.ByteArray.new(256); | |
for (let ii=0; (ii<256); ++ii) { | |
buf[ii]= 0; | |
} | |
buf[256]= 0; | |
let [n, src]= socket.receive_from(buf, null, dest ); | |
let rv= parseResponse(buf, n, src); | |
if (rv) { | |
callback(rv); | |
} | |
return true; | |
}; | |
var tx_fn= function () { | |
let n= socket.send_to( dest, appreq( "IPAD", "NAME", "JSON", "UUID" ), null); | |
return false; | |
}; | |
var timeout_fn= function() { | |
var wsource = socket.create_source(GLib.IOCondition.OUT,null); | |
wsource.set_callback( tx_fn ); | |
wsource.attach(context); | |
return true; | |
}; | |
var source = socket.create_source(GLib.IOCondition.IN,null); | |
source.set_callback( rx_fn ); | |
source.attach(context); | |
timeout_fn(); // Send first message immediately | |
let timeout= GLib.timeout_source_new(timer) | |
timeout.set_callback(timeout_fn); | |
timeout.attach(context); | |
} | |
// "Client" code | |
function cb(rv) | |
{ | |
log( "cb:rv="+JSON.stringify(rv) ); | |
} | |
function main() { | |
var mainloop= GLib.MainLoop.new(null, true); | |
var context= mainloop.get_context(); | |
scanForLMS(context, 5000, cb); | |
mainloop.run(); | |
log( " done..." ); | |
} | |
main(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is an example of UDP discovery specifically for discovering a | |
Logitech Media Server (LMS) on a local network. | |
This does *not* work since | |
1) Socket.create_source doesn't exist in typelib as it is marked | |
with (skip) in Glib/gio/gsocket.c | |
2) GJS will clone array arguments before passing them to the C-code which | |
will make the call to Socket.receive_from work and return the number of | |
bytes received as well as the source of the packet. The buffer content | |
will be unchanged as buffer actually read into is a freed clone. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment