[英]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.
我已经测试了我的高速公路安装的高速公路并使用以下方法进行了扭曲。
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
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)
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()
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')
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
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运行或以另一种更快的方式解决此问题?
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秒,但这是一个好的开始。
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更灵活。
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.