简体   繁体   English

使用Python Twisted和Autobahn从Matlab通过WebSocket发送JSON数据

[英]Sending JSON data over WebSocket from Matlab using Python Twisted and Autobahn

I'm trying to create a connection from Matlab to stream JSON frames over a WebSocket. 我正在尝试从Matlab创建一个连接,以通过WebSocket流式传输JSON帧。 I've tested my python installation of autobahn and twisted using the following. 我已经测试了我的高速公路安装的高速公路并使用以下方法进行了扭曲。

Working Example 工作实例

Matlab Code Matlab代码

Sample driver code that uses the JSONlab toolbox to convert Matlab data to JSON form and then I compress and Base64 encode the data. 使用JSONlab工具箱将Matlab数据转换为JSON格式然后压缩Base64编码数据的示例驱动程序代码。 Since I haven't gotten RPC to work I'm using the command-line where I need compression and Base64 encoding to avoid line-length and shell escaping issues. 由于我还没有使用RPC工作,我正在使用命令行,我需要压缩和Base64编码,以避免行长和shell转义问题。

clear all
close all

python = '/usr/local/bin/python'
bc = '/Users/palmerc/broadcast_client.py'
i = uint32(1)

encoder = org.apache.commons.codec.binary.Base64
while true
    tic;
    packet = rand(100, 100);
    json_packet = uint8(savejson('', packet));
    compressed = CompressLib.compress(json_packet);
    b64 = char(encoder.encode(compressed));
    message = sprintf('%s %s %s', python, bc, b64);
    status = system(message);

    i = i + 1;
    toc;
end

Broadcast Client Code 广播客户端代码

The client code has two ways of being called. 客户端代码有两种被调用方式。 You can pass your message through the command-line or create an instance of BroadcastClient and call sendMessage. 您可以通过命令行传递消息,也可以创建BroadcastClient实例并调用sendMessage。

#!/usr/bin/env python

import sys
from twisted.internet import reactor
from txjsonrpc.web.jsonrpc import Proxy


class BroadcastClient():

    def __init__(self, server=None):
        self.proxy = Proxy(server)

    def errorMessage(self, value):
        print 'Error ', value

    def sendMessage(self, message):
        rc = self.proxy.callRemote('broadcastMessage', message).addCallback(lambda _: reactor.stop())
        rc.addErrback(self.errorMessage)


def main(cli_arguments):
    if len(cli_arguments) > 1:
        message = cli_arguments[1]
        broadcastClient = BroadcastClient('http://127.0.0.1:7080/')
        broadcastClient.sendMessage(message)
        reactor.run()

if __name__ == '__main__':
    main(sys.argv)

Broadcast Server Code 广播服务器代码

The server provides an RPC client on 7080, a web client on 8080, and a WebSocket on 9080 using TXJSONRPC, Twisted, and Autobahn. 服务器在7080上提供RPC客户端,在8080上提供Web客户端,在9080上使用TXJSONRPC,Twisted和Autobahn提供WebSocket。 The Autobahn Web Client is useful for debugging and should be placed in the same directory as the server code. Autobahn Web Client对于调试很有用,应该与服务器代码放在同一目录中。

#!/usr/bin/env python

import sys

from twisted.internet import reactor
from twisted.python import log
from twisted.web.server import Site
from twisted.web.static import File
from txjsonrpc.web import jsonrpc

from autobahn.twisted.websocket import WebSocketServerFactory, \
    WebSocketServerProtocol, \
    listenWS


class BroadcastServerProtocol(WebSocketServerProtocol):

    def onOpen(self):
        self.factory.registerClient(self)

    def onMessage(self, payload, isBinary):
        if not isBinary:
            message = "{} from {}".format(payload.decode('utf8'), self.peer)
            self.factory.broadcastMessage(message)

    def connectionLost(self, reason):
        WebSocketServerProtocol.connectionLost(self, reason)
        self.factory.unregisterClient(self)


class BroadcastServerFactory(WebSocketServerFactory):

    """
    Simple broadcast server broadcasting any message it receives to all
    currently connected clients.
    """

    def __init__(self, url, debug=False, debugCodePaths=False):
        WebSocketServerFactory.__init__(self, url, debug=debug, debugCodePaths=debugCodePaths)
        self.clients = []

    def registerClient(self, client):
        if client not in self.clients:
            print("registered client {}".format(client.peer))
            self.clients.append(client)

    def unregisterClient(self, client):
        if client in self.clients:
            print("unregistered client {}".format(client.peer))
            self.clients.remove(client)

    def broadcastMessage(self, message):
        print("broadcasting message '{}' ..".format(message))
        for client in self.clients:
            client.sendMessage(message.encode('utf8'))
            print("message sent to {}".format(client.peer))


class BroadcastPreparedServerFactory(BroadcastServerFactory):

    """
    Functionally same as above, but optimized broadcast using
    prepareMessage and sendPreparedMessage.
    """

    def broadcastMessage(self, message):
        print("broadcasting prepared message '{}' ..".format(message))
        preparedMessage = self.prepareMessage(message.encode('utf8'), isBinary=False)
        for client in self.clients:
            client.sendPreparedMessage(preparedMessage)
            print("prepared message sent to {}".format(client.peer))


class MatlabClient(jsonrpc.JSONRPC):

    factory = None

    def jsonrpc_broadcastMessage(self, message):
        if self.factory is not None:
            print self.factory.broadcastMessage(message)


if __name__ == '__main__':

    if len(sys.argv) > 1 and sys.argv[1] == 'debug':
        log.startLogging(sys.stdout)
        debug = True
    else:
        debug = False
    factory = BroadcastPreparedServerFactory(u"ws://127.0.0.1:9000",
                                             debug=debug,
                                             debugCodePaths=debug)

    factory.protocol = BroadcastServerProtocol
    listenWS(factory)

    matlab = MatlabClient()
    matlab.factory = factory
    reactor.listenTCP(7080, Site(matlab))

    webdir = File(".")
    web = Site(webdir)
    reactor.listenTCP(8080, web)

    reactor.run()

The Problem - Failed Attempts 问题 - 尝试失败

First a note, If you have trouble getting python working from Matlab you need to make sure you're pointing at the correct version of Python on your system using the pyversion command and you can correct it using pyversion('/path/to/python') 首先注意,如果你在使用matlab从python上运行时遇到麻烦,你需要确保使用pyversion命令在系统上指向正确版本的Python,你可以使用pyversion('/path/to/python')来修正它pyversion('/path/to/python')

Matlab can't run reactor Matlab无法运行reactor

clear all
close all

i = uint32(1)

while true
    tic;
    packet = rand(100, 100);
    json_packet = uint8(savejson('', packet));
    compressed = CompressLib.compress(json_packet);
    b64 = char(encoder.encode(compressed));
    bc.sendMessage(py.str(b64.'));
    py.twisted.internet.reactor.run % This won't work.

    i = i + 1;
    toc;
end

Matlab POST Matlab POST

Another attempt involved using Matlab's webwrite to POST to the server. 另一个尝试涉及使用Matlab的webwrite POST到服务器。 Turns out webwrite will convert data to JSON simply by passing the correct weboptions . 原来, webwrite只需传递正确的weboptions即可将数据转换为JSON。

options = weboptions('MediaType', 'application/json');
data = struct('Matrix', rand(100, 100));
webwrite(server, data, options);

This worked, but turns out to be slow (~0.1 seconds) per message. 这有效,但每封邮件的速度很慢(~0.1秒)。 I should mention that the matrix is not the real data I'm sending, the real data serializes to about 280000 bytes per message, but this provides a reasonable approximation. 我应该提到矩阵不是我发送的真实数据,真实数据序列化为每个消息约280000字节,但这提供了合理的近似值。

How can I call bc.sendMessage so that it correctly manages to get reactor to run or solve this issue in another, faster way? 如何调用bc.sendMessage以便正确设法让reactor运行或以另一种更快的方式解决此问题?

Setting up a WebSocket using Python and Matlab 使用Python和Matlab设置WebSocket

Check Matlab is pointing at the correct version of python 检查Matlab指向正确版本的python

First, you need to make sure you're using the correct python binary. 首先,您需要确保使用正确的python二进制文件。 On Mac you might be using the system standard version instead of the one that Homebrew installed for example. 在Mac上,您可能正在使用系统标准版本而不是Homebrew安装的版本。 Check the location of your python install using: 使用以下命令检查python安装的位置:

pyversion

You can point Matlab to the correct version using: 您可以使用以下方法将Matlab指向正确的版本:

pyversion('path/to/python')

this may require you restart python. 这可能需要你重启python。

As stated above I'm using Twisted to multiplex my Matlab data to the WebSocket clients. 如上所述,我使用Twisted将我的Matlab数据多路复用到WebSocket客户端。 The best way I have found to solve this problem has been simply to create a server that handles POSTS and then passes that along to the WebSocket clients. 我找到解决这个问题的最好方法就是创建一个处理POSTS的服务器,然后将其传递给WebSocket客户端。 Compression just slowed things down so I send 280 kBytes of JSON per request which is taking roughly 0.05 seconds per message. 压缩只会减慢速度,所以每个请求发送280 kB的JSON,每条消息大约需要0.05秒。 I would like this to be faster, .01 seconds, but this is a good start. 我希望这会更快,.01秒,但这是一个好的开始。

Matlab Code Matlab代码

server = 'http://127.0.0.1:7080/update.json';
headers = py.dict(pyargs('Charset','UTF-8','Content-Type','application/json'));
while true
    tic;
    packet = rand(100, 100);
    json_packet = savejson('', packet);
    r = py.requests.post(server, pyargs('data', json_packet, 'headers', headers));
    toc;
end

I could have used the Matlab webwrite function, but generally I find calling out to python to be more flexible. 我可以使用Matlab webwrite函数,但通常我发现调用python更灵活。

Python WebSocket-WebClient Server Python WebSocket-WebClient服务器

import sys

from twisted.internet import reactor
from twisted.python import log
from twisted.web.resource import Resource
from twisted.web.server import Site
from twisted.web.static import File

from autobahn.twisted.websocket import WebSocketServerFactory, \
    WebSocketServerProtocol, \
    listenWS


class BroadcastServerProtocol(WebSocketServerProtocol):

    def onOpen(self):
        self.factory.registerClient(self)

    def onMessage(self, payload, isBinary):
        if not isBinary:
            message = "{} from {}".format(payload.decode('utf8'), self.peer)
            self.factory.broadcastMessage(message)

    def connectionLost(self, reason):
        WebSocketServerProtocol.connectionLost(self, reason)
        self.factory.unregisterClient(self)


class BroadcastServerFactory(WebSocketServerFactory):

    def __init__(self, url, debug=False, debugCodePaths=False):
        WebSocketServerFactory.__init__(self, url, debug=debug, debugCodePaths=debugCodePaths)
        self.clients = []

    def registerClient(self, client):
        if client not in self.clients:
            print("registered client {}".format(client.peer))
            self.clients.append(client)

    def unregisterClient(self, client):
        if client in self.clients:
            print("unregistered client {}".format(client.peer))
            self.clients.remove(client)

    def broadcastMessage(self, message):
        for client in self.clients:
            client.sendMessage(message.encode('utf8'))


class BroadcastPreparedServerFactory(BroadcastServerFactory):

    def broadcastMessage(self, message, isBinary=False):
        if isBinary is True:
            message = message.encode('utf8')
        preparedMessage = self.prepareMessage(message, isBinary=isBinary)
        for client in self.clients:
            client.sendPreparedMessage(preparedMessage)


class WebClient(Resource):

    webSocket = None

    def render_POST(self, request):
        self.webSocket.broadcastMessage(request.content.read())

        return 'OK'


if __name__ == '__main__':

    if len(sys.argv) > 1 and sys.argv[1] == 'debug':
        log.startLogging(sys.stdout)
        debug = True
    else:
        debug = False
    factory = BroadcastPreparedServerFactory(u"ws://127.0.0.1:9000",
                                             debug=debug,
                                             debugCodePaths=debug)

    factory.protocol = BroadcastServerProtocol
    listenWS(factory)

    root = Resource()
    webClient = WebClient()
    webClient.webSocket = factory
    root.putChild('update.json', webClient)
    webFactory = Site(root)
    reactor.listenTCP(7080, webFactory)

    webdir = File(".")
    web = Site(webdir)
    reactor.listenTCP(8080, web)

    reactor.run()

I got rid of the RPC attempt and just went with a straight POST. 我摆脱了RPC尝试,只是直接POST。 Still lots of opportunity for performance improvement. 仍有很多提升绩效的机会。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM