How to create Socket.io client in Python to talk to a Sails server

I am running a SailsJS instance (v0.12.3) for which I have a controller MyModelController handling a WebSocket ( socket.io ) connection that is allowed if the user has authenticated.

MyModelController

module.exports = {
    /**
     * Socket connection
     */
    connect: function(req, res) {
      /* Checks it's a socket connection request */
      if (!req.isSocket) { return res.badRequest();}

      /* Checks it's authenticated */
      if (req.session.me) {
        User.findOne(req.session.me, function (err, me) {
          if (req.param('name')) {

            MyModel.findOne({
              name: req.param('name')
            }).exec(function(err, objectFound) {

              /* e.g. Join a room named after the params passed */
              sails.sockets.join(req, objectFound.name); 

              return res.ok({ message: "All is OK!"}});
            });
          } 
        });
      }
    }
}

From a SailsJS served page eg myPage.ejs , this works well:

<!DOCTYPE html>
<html>
<head>...</head>
<body>
    ...
    <script src="/js/dependencies/sails.io.js"></script>
    <script src="/js/dependencies/app.io.js"></script>
    <script src="/vendor/jquery/jquery.min.js"></script>
    <script type "text/javascript">
       // Use .get() to contact the server
       io.socket.get('/myModel/connect?name=john', function gotResponse(body, response) {
         console.log('Status code ' + response.statusCode + ' & data: ', body);
       });
    </script>
</body>
</html>

How do I connect to SailsJS socket.io server from a Python client?

In first my few attempts, I tried to leave the Authentication part for later. So let's say, we don't have to worry about it for now.

I installed a Python socket.io client pip install socketIO-client-2 See socketIO-client-2 doc.

And for starters tried this (oh and BTW I am using a secured connection with self-signed certs):

from socketIO_client import SocketIO

SocketIO('https://localhost:1337/myModel/connect', params={'name': 'john'}, verify=False)

but then I get an error immediately on the Sails server side:

verbose: Sending 400 ("Bad Request") response

with an error on the client side

Failed to establish a new connection: [Errno 61] Connection refused

So I commented out the socket connection request and authenticated checks, to make it simpler, hoping to figure it out...

connect: function(req, res) {

  if (req.param('name')) {
    MyModel.findOne({
      name: req.param('name')
    }).exec(function(err, objectFound) {
      console.log(req.socket.id);
      console.log(param('name'));
      sails.sockets.join(req, objectFound.name); 

      return res.ok({ message: "All is OK!"}});
    });
  } else {
    console.log('No params passed to the websocket');
  }
}

which gives me on the Sails side:

connect > found object: { name: 'john',
  id: 1,
  createdAt: '2016-11-04T15:20:38.000Z',
  updatedAt: '2016-11-04T15:20:38.000Z' }
Socket request undefined        <============== UNDEFINED
warn: Attempted to call `sailsSockets.join`, but the first argument was not a socket.
Socket Id: undefined

My Python log:

    /usr/local/lib/python2.7/site-packages/requests/packages/urllib3/connectionpool.py:843: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
Traceback (most recent call last):
  File "wsclient.py", line 17, in <module>
    SocketIO('https://localhost:1337/myModel/connect', params={'name': 'john'}, verify=False)
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/__init__.py", line 334, in __init__
    resource, hurry_interval_in_seconds, **kw)
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/__init__.py", line 51, in __init__
    self._transport
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/__init__.py", line 59, in _transport
    self._engineIO_session = self._get_engineIO_session()
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/__init__.py", line 73, in _get_engineIO_session
    transport.recv_packet())
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/transports.py", line 81, in recv_packet
    for engineIO_packet in decode_engineIO_content(response.content):
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/parsers.py", line 95, in decode_engineIO_content
    content, content_index)
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/parsers.py", line 202, in _read_packet_length
    while get_byte(content, content_index) not in [0, 1]:
  File "/usr/local/lib/python2.7/site-packages/socketIO_client/symmetries.py", line 28, in get_byte
    return six.indexbytes(x, index)
  File "/usr/local/lib/python2.7/site-packages/six.py", line 655, in indexbytes
    return ord(buf[i])
IndexError: string index out of range

Any pointers?

INTERESTING LINKS:

  • how-to-connect-socket-io-client-to-sails-js-server, but that's using NodeJS
  • sails.io.js for nodejs
  • When NodeJS + sails.io.js

    I guess I want to achieve this, but in python

    var socketIOClient = require('socket.io-client');
    var sailsIOClient = require('sails.io.js');
    
    // Instantiate the socket client (`io`)
    // (for now, you must explicitly pass in the socket.io client when using this library from Node.js)
    var io = sailsIOClient(socketIOClient);
    
    // Set some options:
    // (you have to specify the host and port of the Sails backend when using this library from Node.js)
    io.sails.url = 'http://localhost:1337';
    // ...
    
    // Send a GET request to `http://localhost:1337/hello`:
    io.socket.get('/hello', function serverResponded (body, JWR) {
      // body === JWR.body
      console.log('Sails responded with: ', body);
      console.log('with headers: ', JWR.headers);
      console.log('and with status code: ', JWR.statusCode);
    
      // When you are finished with `io.socket`, or any other sockets you connect manually,
      // you should make sure and disconnect them, e.g.:
      io.socket.disconnect();
    
      // (note that there is no callback argument to the `.disconnect` method)
    });
    

    which gives this log when connecting

    $ DEBUG=* node sio-client.js 
    
      socket.io-client:url parse https://localhost:1337 +0ms
      socket.io-client new io instance for https://localhost:1337 +5ms
      socket.io-client:manager readyState closed +3ms
      socket.io-client:manager opening https://localhost:1337 +0ms
      engine.io-client:socket creating transport "websocket" +1ms
      engine.io-client:socket setting transport websocket +29ms
      socket.io-client:manager connect attempt will timeout after 20000 +0ms
      socket.io-client:manager readyState opening +7ms
      engine.io-client:socket socket receive: type "open", data "{"sid":"hj4FCwhk_pQ3hoTbAAAE","upgrades":[],"pingInterval":25000,"pingTimeout":60000}" +17ms
      engine.io-client:socket socket open +0ms
      socket.io-client:manager open +0ms
      socket.io-client:manager cleanup +1ms
      socket.io-client:socket transport is open - connecting +0ms
      engine.io-client:socket socket receive: type "message", data "0" +215ms
      socket.io-parser decoded 0 as {"type":0,"nsp":"/"} +0ms
      socket.io-client:socket emitting packet with ack id 0 +3ms
      socket.io-client:manager writing packet {"type":2,"data":["get",{"method":"get","headers":{},"data":{},"url":"/mymodel/connect?name=john"}],"options":{"compress":true},"id":0,"nsp":"/"} +0ms
      socket.io-parser encoding packet {"type":2,"data":["get",{"method":"get","headers":{},"data":{},"url":"/mymodel/connect?name=john"}],"options":{"compress":true},"id":0,"nsp":"/"} +2ms
      socket.io-parser encoded {"type":2,"data":["get",{"method":"get","headers":{},"data":{},"url":"/mymodel/connect?name=john"}],"options":{"compress":true},"id":0,"nsp":"/"} as 20["get",{"method":"get","headers":{},"data":{},"url":"/mymodel/connect?name=john"}] +0ms
      engine.io-client:socket flushing 1 packets in socket +1ms
    
    
      |>    Now connected to Sails.
    ___/   For help, see: http:
            (using sails.io.js node SDK @v1.1.0)
    
    
    
      engine.io-client:socket socket receive: type "message", data "30[{"body":{"mymodel":{"name":"john","id":1,"createdAt":"2016-11-04T15:20:38.000Z","updatedAt":"2016-11-04T15:20:38.000Z","assembly":"drive"},"message":"DRIVE, Joined room john"},"headers":{"Access-Control-Allow-Origin":"","Access-Control-Allow-Credentials":"","Access-Control-Allow-Methods":"","Access-Control-Allow-Headers":"","Access-Control-Expose-Headers":"","access-control-allow-origin":"","access-control-allow-credentials":"","access-control-allow-methods":"","access-control-allow-headers":"","access-control-expose-headers":""},"statusCode":200}]" +242ms
      socket.io-parser decoded 30[{"body":{"mymodel":{"name":"john","id":1,"createdAt":"2016-11-04T15:20:38.000Z","updatedAt":"2016-11-04T15:20:38.000Z","assembly":"drive"},"message":"DRIVE, Joined room john"},"headers":{"Access-Control-Allow-Origin":"","Access-Control-Allow-Credentials":"","Access-Control-Allow-Methods":"","Access-Control-Allow-Headers":"","Access-Control-Expose-Headers":"","access-control-allow-origin":"","access-control-allow-credentials":"","access-control-allow-methods":"","access-control-allow-headers":"","access-control-expose-headers":""},"statusCode":200}] as {"type":3,"nsp":"/","id":0,"data":[{"body":{"mymodel":{"name":"john","id":1,"createdAt":"2016-11-04T15:20:38.000Z","updatedAt":"2016-11-04T15:20:38.000Z","assembly":"drive"},"message":"DRIVE, Joined room john"},"headers":{"Access-Control-Allow-Origin":"","Access-Control-Allow-Credentials":"","Access-Control-Allow-Methods":"","Access-Control-Allow-Headers":"","Access-Control-Expose-Headers":"","access-control-allow-origin":"","access-control-allow-credentials":"","access-control-allow-methods":"","access-control-allow-headers":"","access-control-expose-headers":""},"statusCode":200}]} +244ms
      socket.io-client:socket calling ack 0 with [{"body":{"mymodel":{"name":"john","id":1,"createdAt":"2016-11-04T15:20:38.000Z","updatedAt":"2016-11-04T15:20:38.000Z","assembly":"drive"},"message":"DRIVE, Joined room john"},"headers":{"Access-Control-Allow-Origin":"","Access-Control-Allow-Credentials":"","Access-Control-Allow-Methods":"","Access-Control-Allow-Headers":"","Access-Control-Expose-Headers":"","access-control-allow-origin":"","access-control-allow-credentials":"","access-control-allow-methods":"","access-control-allow-headers":"","access-control-expose-headers":""},"statusCode":200}] +1ms
    hello again
    Sails responded with:  { mymodel: 
       { name: 'john',
         id: 1,
         createdAt: '2016-11-04T15:20:38.000Z',
         updatedAt: '2016-11-04T15:20:38.000Z',
         assembly: 'drive' },
      message: 'DRIVE, Joined room john' }
    with headers:  { 'Access-Control-Allow-Origin': '',
      'Access-Control-Allow-Credentials': '',
      'Access-Control-Allow-Methods': '',
      'Access-Control-Allow-Headers': '',
      'Access-Control-Expose-Headers': '',
      'access-control-allow-origin': '',
      'access-control-allow-credentials': '',
      'access-control-allow-methods': '',
      'access-control-allow-headers': '',
      'access-control-expose-headers': '' }
    and with status code:  200
    

    note that it is writing packet

     {  
       "type":2,
       "data":[  
          "get",
          {  
             "method":"get",
             "headers":{},
             "data":{},
             "url":"/myModel/connect?name=john"
          }
       ],
       "options":{  
          "compress":true
       },
       "id":0,
       "nsp":"/"
    }
    

    Trying sideeffect 's suggestion

    Here the code I am running pastebin

    on the Sails side

    verbose: Could not fetch session, since connecting socket has no cookie (is this a cross-origin socket?)
    Generated a one-time-use cookie:sails.sid=s%3AR-Sm_JeWKoqayZOku-EvxPR_uUpilwVU.3yRUVjmYSpCl%2BeT4sJIOH%2BUTOL3EjWFabDKbswSlkdIand saved it on the socket handshake.
    This will start this socket off with an empty session, i.e. (req.session === {})
    That "anonymous" section will only last until the socket is disconnected unless you persist the session id in your database,
    or by setting the set-cookie response header for an HTTP request that you *know* came from the same user (etc)
    Alternatively, just make sure the socket sends a `cookie` header or query param when it initially connects.
    

    on the socket.io python client DEBUG info:

    /usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:334: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
      SNIMissingWarning
    /usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:132: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
      InsecurePlatformWarning
    /usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py:843: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
      InsecureRequestWarning)
    DEBUG:root:192.168.178.20:1337/socket.io [transport selected] websocket
    DEBUG:root:192.168.178.20:1337/socket.io [heartbeat reset]
    DEBUG:root:192.168.178.20:1337/socket.io [socket.io packet sent] 21["get", {"url": "/path/connect", "headers": {}, "data": {"name": "john"}, "method": "get"}]
    DEBUG:root:192.168.178.20:1337/socket.io [socket.io packet received] 0
    

    Seems it needs a time from your part to create a sails.io.js compatiblity python wrapper arround the given Socketio library. So i thought sharing the implementations while reading the source code of the client side library.

    What sails.io.js library here does that, it converts the .get , .post , .put functions to a request via socketio client that sends the following data.

    initialize the socketio client lib with http://localhost:1337

    pass data to emit function as a dictionary as described below,

    common emit data structure is

    emit_data = {
    
            'method' : 'get', #  get, post, delete depends on requirement
    
            'headers' : {'header_key': 'header_value'}, # for passing headers
    
             'data': {'key': 'value'},  # for sending /search/?q=hello, {'q': 'hello'}
    
             'url': '/hello' # 
    }
    

    If you need to convert the last nodejs snippet to this client as

    import requests
    from socketIO_client import SocketIO
    
    def server_responded(*body):
        print 'response', body
    
    # first we need to get cookie headers for connection
    r = requests.get('localhost:1337/__getcookie/')
    emit_data = {
      'method' : 'get',
      'url': '/hello',
      'headers': {'Cookie': r.headers['Set-Cookie']},
    } 
    # update emit_data with extra headers if needed
    
    with SocketIO('localhost', 1337) as socketIO:
        # note: event parameter is request method, here get for GET
        # second parameter is emit_data structured as described above
        socketIO.emit(emit_data['method'], emit_data, server_responded)
        # adjust with your requirements
        socketIO.wait_for_callbacks(seconds=1)
    

    I was never able to address my issue, so I worked around it by creating a relay server to forward messages from Sails.io connection to a socket.io connection and vice-versa.

    I created a socket.io server in my Python module using Flask SocketIO, then had my relay connect both the python server (socket.io) and the Sails server (Sails.io).

    When message received from SailsJS (Sails.io) then emit/forward it to the python server (socket.io).

    In my example, the Sails.io client authenticates to Sails first, but I have NOT implemented authentication for the python server.

    // Connects to SailsIO on SailsJS instance
    var sailsIO = require('sails.io.js')(require('socket.io-client'));
    
    // Connects to SocketIO server
    var socketIO = require('socket.io-client')('https://localhost:7000');
    
    socketIO.on('connect', function() {
        console.log("Connect");
    });
    socketIO.on('disconnect', function() {
        console.log("Disconnect");
    });
    
    var request = require('request');
    var inspect = require('eyespect').inspector({
        styles: {
            all: 'magenta'
        }
    });
    
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; // Ignore the certs
    
    /* Options */
    sailsIO.sails.url = 'https://192.168.178.20:1337';
    sailsIO.sails.rejectUnauthorized = false;
    // ...
    
    /* Authenticate */
    var authJson = {
        "email": "me@domain.com",
        "password": "mypassword"
    };
    
    var options = {
        method: 'put',
        body: authJson,
        json: true,
        url: 'https://192.168.178.20:1337/login',
        headers: {
            'Content-Type': 'application/json'
        }
    }
    
    request(options, function(err, res, body) {
        if (err) {
            inspect(err, 'error posting json')
            return
        }
        var headers = res.headers
        var statusCode = res.statusCode
        var cookie = headers['set-cookie'][0].split(';')[0]
        inspect(headers, 'headers');
        inspect(cookie, "Cookie")
        inspect(statusCode, 'statusCode');
        inspect(body, 'body');
    
        sailsIO.sails.headers = {
            'Cookie': cookie
        };
    
        /* Connects to SailsJS */
        sailsIO.socket.request({
            method: 'get',
            url: '/path/to',
            data: {
                name: 'john'
            },
            headers: {
                'Cookie': cookie
            }
        }, function(resData, jwres) {
            inspect(jwres, "jwres");
            if (jwres.error) {
                console.log(jwres.statusCode); // => e.g. 403
                return;
            }
            console.log(jwres.statusCode); // => e.g. 200
        });
    });
    
    sailsIO.socket.on('connecting', function() {
        console.log('Connecting to server');
    });
    
    sailsIO.socket.on('hello', function(data) {
        inspect(JSON.stringify(data), "hello (someone connected)");
    });
    
    /**
     * On message from Sails, re-emit to python SocketIO server via socket.io client
     */
    sailsIO.socket.on('event', function(data) {
        inspect(JSON.stringify(data), "Data received");
        socketIO.emit('event', data);
    });
    
    链接地址: http://www.djcxy.com/p/37332.html

    上一篇: c#7元组。 无法在mac dotnet core 1.1中使用元组

    下一篇: 如何在Python中创建Socket.io客户端以与Sails服务器交谈