如何在 Python 中创建 Socket.io 客户端以与 Sails 服务器通信

Posted

技术标签:

【中文标题】如何在 Python 中创建 Socket.io 客户端以与 Sails 服务器通信【英文标题】:How to create Socket.io client in Python to talk to a Sails server 【发布时间】:2017-04-18 09:51:01 【问题描述】:

我正在运行一个 SailsJS 实例 (v0.12.3),我有一个控制器 MyModelController 处理 WebSocket (socket.io) 连接,如果用户允许该连接已通过身份验证。

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!");
            );
           
        );
      
    

来自 SailsJS 提供的页面,例如myPage.ejs,这很好用:

<!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>

如何从 Python 客户端连接到 SailsJS socket.io 服务器?

在最初的几次尝试中,我尝试将身份验证部分留到以后。因此,假设我们现在不必担心它。

我安装了一个 Python socket.io 客户端pip install socketIO-client-2 见socketIO-client-2 doc。

对于初学者来说,尝试过这个(哦,顺便说一句,我正在使用带有自签名证书的安全连接):

from socketIO_client import SocketIO

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

但随后我在 Sails 服务器端立即收到错误消息:

verbose: Sending 400 ("Bad Request") response

客户端出错

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

所以我把socket连接请求和认证检查都注释掉了,为了更简单,希望能搞清楚……

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');
  

这给了我在风帆方面:

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

我的 Python 日志:

    /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

任何指针?

有趣的链接:

how-to-connect-socket-io-client-to-sails-js-server,但那是使用 NodeJS sails.io.js for nodejs

当 NodeJS +sails.io.js

我想我想实现这个,但是在 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)
);

连接时会给出这个日志

$ 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

注意它正在写数据包

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

尝试副作用的建议

这是我正在运行的代码pastebin

在风帆一侧

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.

关于 socket.io python 客户端的调试信息:

/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

【问题讨论】:

这可能是一个愚蠢的问题,但你为什么使用 python 客户端? 不是一个愚蠢的问题,我基本上别无选择,否则我会选择 nodejs 客户端。我只需要将这个功能(与 Sails 的连接)包含到用 Python 编写的大量遗留软件中。其中一个 python 模块处理需要发送到 Sails 实例的数据。后者以一种非常特殊的方式实现 socket.io,我承认,我还没有完全掌握。我现在非常绝望。我想我可以创建一个 nodejs 套接字服务器,它可以使用 Sails.io.js 将消息从 Python 堆栈中继到 Sails ......不要太兴奋。有什么想法吗? 【参考方案1】:

您似乎需要一段时间来围绕给定的 Socketio 库创建一个sails.io.js 兼容性python 包装器。所以我想在阅读客户端库的源代码时分享实现。

这里的sails.io.js 库是做什么的,它将.get.post.put 函数转换为通过socketio 客户端发送以下数据的请求。

http://localhost:1337初始化socketio客户端库

将数据作为字典传递给emit函数,如下所述,

常见的emit数据结构是

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' # 

如果需要将最后一个nodejs sn -p 转换成这个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)

【讨论】:

我有一些非常接近的东西。现在就试试。干杯伙伴! 我收到一条 cookie 消息,但这似乎不是阻止程序。我希望我有更多的日志来了解它失败的地方。在问题中查看我的更新 某些东西可能缺少emit_data,或者它不太正确,因为它似乎没有收到'data': 'name':'john' 的参数......与NodeJS客户端相反,我不知道't get the verbose: Receiving incoming message from Socket.io: method: 'get', headers: , data: , url: '/robot/connect?name=qb57' etc... @zabumba 分享您的代码(python)可能是 pastebin 链接,以便我可以检查有什么不同,请注意,我使用的是不同的 socketio 客户端,而不是您提到的那个 这与您的建议非常相似,仅使用https。我留下评论了我尝试过的一些东西。检查这个pastebin【参考方案2】:

我一直无法解决我的问题,因此我通过创建一个中继服务器将消息从 Sails.io 连接转发到 socket.io 连接,反之亦然。 p>

我使用Flask SocketIO 在我的 Python 模块中创建了一个 socket.io 服务器,然后让我的中继连接 Python 服务器 (socket.io) 和 Sails 服务器 (Sails.io)。

当从 SailsJS (Sails.io) 收到消息时,将其发送/转发到 python 服务器 (socket.io)。

在我的示例中,Sails.io 客户端首先对 Sails 进行身份验证,但我没有为 python 服务器实现身份验证。

// 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);
);

【讨论】:

【参考方案3】:

您需要在 url 中添加__sails_io_sdk_version=0.11.0,如下所示:

var socketIO = require('socket.io-client')('https://localhost:7000/?__sails_io_sdk_version=0.11.0'');

【讨论】:

以上是关于如何在 Python 中创建 Socket.io 客户端以与 Sails 服务器通信的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Socket.io 实时更新大量数据

如何在环回 4 的 RestApplication 中使用 socket.io?

如何在 Nodejs 中创建服务器?

如何在节点 js 服务器中创建通道 websocket

如何使用 socket.io 实现长轮询?

在 socket.io 中搜索对手