newbie trying to grasp the raw sockets

Discuss building things with or for the Mozilla Platform.
Post Reply
oh5nxo
Posts: 6
Joined: July 3rd, 2016, 2:21 pm

newbie trying to grasp the raw sockets

Post by oh5nxo »

Hi.

I'm trying to learn to use socket transport service, openInputStream() and a unix-domain socket. A toy GPS GeolocationProvider.

After things are set up and data is flowing, do I have to keep an explicit reference to the socket, the "transport" ? It would feel right, if I let off from the transport variable and just held on to the inputStream, closing the stream if an EOF or error occurs.

In NetUtil.jsm, a scriptableInputStream is created, used and then just forgotten, presumably to be garbage-collected. Is everything like this, no need to explicitly close ? If the close must be done manually, can I get the transport somehow back from the inputStream object ? stream.parent or something like that ?

What is the effect of the .QI() below ? I've seen it used in 2 different ways, sometimes for the return value,
z = xxx.QI(interface);
and sometimes just for some side-effect
yyy.QI(interface);

Code: Select all

this.stream = this.socket.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
LOTS more questions, but I'll stop now,
Juha

EDIT: Got that QI() now. Very helpful reading in https://developer.mozilla.org/en-US/Add ... UL_School/
oh5nxo
Posts: 6
Joined: July 3rd, 2016, 2:21 pm

Re: newbie trying to grasp the raw sockets

Post by oh5nxo »

Tried it then. Holding on to the inputStream only, letting the transport object appear and vanish on the fly.

I wouldn't know how to check for memory leaks, but at least filedescriptors are not leaking, and the Sockets-panel in about:networking shows one or zero Unix-domain sockets as it should, when opening/closing/opening.... a suitable test page for Geolocation.

The about: panel thinks the socket is TCP with no hostname, but as it did warn about being work-in-progress...

Juha

Code: Select all

"use strict";

const GPS_SOCK = "/var/run/gps.sock";

const Cc = Components.classes;
const Ci = Components.interfaces;

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");

// pvt already in DOMGeoPositionCoords format (.time is extra)

function GpsGeoPositionObject(pvt) {
  this.coords    = pvt;
  this.timestamp = pvt.time; // XXX not Date.now();
}

GpsGeoPositionObject.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGeoPosition])
};

function GpsGeolocationProvider() {}

GpsGeolocationProvider.prototype = {
  classID:        Components.ID("{4fdb9989-340d-11e6-a10c-64315071f638}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGeolocationProvider,
                                         Ci.nsIInputStreamCallback]),

  /* GeolocationProvider */

  setHighAccuracy: function (enable) {}, // Boolean enable, no-op here

  watch: function (geoservice) { this.geosvc = geoservice; },

  startup: function () { // watch(GeolocationService) comes immediately after
    if (this.stream)
      return;

    // stay on the GeolocationService thread ? XXX necessary ?

    this.thread = Services.tm.currentThread;
    this.buffer = "";

    // normal mishaps will be seen in the callback, .available() throws.
    // NOT_FOUND, EOF, etc.
    // UNBUFFERED has any value ?
    // .QI() needed ?
    // .asyncWait() might recurse back immediately, keep last ?

    this.stream = Cc["@mozilla.org/network/socket-transport-service;1"].
                  getService(Ci.nsISocketTransportService).
                  createUnixDomainTransport(new FileUtils.File(GPS_SOCK)).
                  openInputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0).
                  QueryInterface(Ci.nsIAsyncInputStream);

    this.stream.asyncWait(this, 0, 0, this.thread);
  },

  shutdown: function () {
    if (this.stream) {
      this.stream.asyncWait(null, 0, 0, this.thread); // remove callback
      this.stream.close();
    }
    this.stream = null;
    this.thread = null;
    this.geosvc = null;
    this.buffer = "";
  },

  /* InputStreamCallback */

  onInputStreamReady: function(stream) {
    var cc = 0, shuffle, line, pvt;

    try {
      cc = stream.available(); // 0 if EOF ? XXX can't we just read(4096) ?
    }
    catch (ex) { // FILE_NOT_FOUND, something else ? EOF ?
    }
    if (cc > 0) {
      // append to buffer, hold any partial line, pick LAST with a fix.
      // normally the input is one full line and buffer always keeps ""

      this.buffer += NetUtil.readInputStreamToString(stream, cc);
      shuffle = this.buffer.split("\n");
      this.buffer = shuffle.pop();

      while ((line = shuffle.pop()) !== undefined) // normally never "", but ...
        if (line.startsWith("fix "))
          break;

      if (line) {
        line = line.split(" ");

// 0   1    2        3        4  5    6    7   8 9      10
// fix good 61.05862 28.10380 93 5.00 7.90 0.0 0 KP41BB 2016-06-28T21:55:19.4Z
// fix stale 61...
// nofix
        pvt = {};
        pvt.latitude  = Number.parseFloat(line[2]);
        pvt.longitude = Number.parseFloat(line[3]);
        pvt.altitude  = Number.parseFloat(line[4]);
        pvt.speed     = Number.parseFloat(line[7]) / 3.6; // from km/h to m/s
        pvt.heading   = Number.parseFloat(line[8]);
        pvt.time      = Date.parse(line[10]);

        pvt.accuracy = pvt.altitudeAccuracy = 1e2; // XXX 100m error sphere

        if (this.geosvc)
          this.geosvc.update(new GpsGeoPositionObject(pvt));
      }
      stream.asyncWait(this, 0, 0, this.thread); // one-shot, must be reprimed
    } else {
      // EOF or error

      if (this.geosvc)
        this.geosvc.notifyError(Ci.nsIDOMGeoPositionError.POSITION_UNAVAILABLE);

      this.shutdown(); // XXX use a timer to try to reconnect later
    }
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GpsGeolocationProvider]);
Post Reply