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

2022-03-09 00:00:00 python socket.io node.js sails.js

问题描述

我正在运行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

所以我注释掉了套接字连接请求和身份验证检查,以使其更简单,希望能弄清楚.

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

解决方案

似乎需要一段时间才能围绕给定的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代码段转换为thisclientAS

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)

相关文章