SwiftyZeroMQ Publisher does not recognise subscriber after first message is published - ios

Problem: I fail subscribing to my publisher implemented in swift as soon as the publisher has published its first message.
Goal: Publish a data stream over ZeroMQ from my swift app. Then connect and disconnect a few subscribers and get messages through.
Background: I use swift5 and SwiftyZeroMQ5 (I have tested SwiftyZeroMQ too), I deploy on iPhone. I try to subscribe from both swift and python3. It only works if I connect my subscriber prior to publishing the first message. It also works if I first connect my subscriber then start the publisher app, then publishes. Corresponding publish and subscribe code on python3 does not require launching in any specific order and represents the behaviour I want.
Since I get the sub/pub to work if I start i specific order, I know that IP-numbers, ports and topic, formatting etc are correct.
Note that the behaviour is the same when I subscribe from both python3 and swift - it is not compatibility issues between swift and python.
Error messages: No error messages, the poller simply does not trigger if the processes are not started in the described order. The poller do trigger and messages and received if the processes are started in the described order.
What I tried: I've tried different combinations of publishers and subscribers regarding code base and devices.
Swift pub to swift sub on same device [only works in described order]
Swift pub to swift sub on different devices [only works in described order]
Swift pub to python3 sub [only works in described order]
Python3 pub to swift sub [works independent of start order]
Python3 pub to python3 sub [works independent of start order]
My conclusion: There is a problem in the swift publisher socket: it fails to recognise new subscribers after it has published its first message.
Swift code for publisher, initPublisher is called in viewDidLoad(). ZeroMQ library version is 4.2.2:
import SwiftyZeroMQ5
var context: SwiftyZeroMQ.Context = try! SwiftyZeroMQ.Context()
var gpsPublisher: SwiftyZeroMQ.Socket?
let gpsPublishEndPoint = "tcp://*:5560"
// Init the publisher socket
func initPublisher()->Bool{
do{
self.gpsPublisher = try context.socket(.publish)
try self.gpsPublisher?.setSendBufferSize(4096)
try self.gpsPublisher?.setLinger(0) // Dont buffer messages
try self.gpsPublisher?.bind(self.gpsPublishEndPoint)
return true
}
catch{
print("Publish setup failed!")
return false
}
}
// ZMQ publish. Publishes string and serialized json-object
func publish(socket: SwiftyZeroMQ.Socket?, topic: String, json: JSON)->Bool{
// Create string with topic and json representation
let publishStr = getJsonStringAndTopic(topic: topic, json: json)
do{
try socket?.send(string: publishStr)
print("publish: Published: " + publishStr)
return true
}
catch{
print("publish: Error, tried to publish, but failed: " + publishStr)
return false
}
}
//The function is repeatedly called in a thread. Only function call shown here below.
_ = self.publish(socket: self.gpsPublisher, topic: "myTopic", json: json)
The python3 subscriber code,
zmq.zmq_version() -> '4.3.2':
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://192.168.1.2:5560")
socket.setsockopt_string(zmq.SUBSCRIBE, 'myTopic')
socket.RCVTIMEO = 1000 # in milliseconds
while socket:
try:
msg = str(socket.recv(), 'utf-8')
(topic, msg) = auxiliaries.zmq.demogrify(msg)
_print((topic, msg))
except zmq.error.Again as error:
_print(str(error))
except KeyboardInterrupt:
auxiliaries.zmq.close_socket_gracefully(socket)
socket = None
Any help, interesting test setups etc is very much appreciated.

I did some more testing and found out a few things and a workaround.
The code works as intended when running on an iPhone simulator (simulator is x86_64 architecture, iPhone8 is armv7)
I don't think it is related, but I did find it interesting. Certain multicast and broadcast protocols require an approval from Apple. You can run without the approval on simulators, but not in devices. Apple networking multicast entitlement. Since it partially works I did rule this out.
The workaround is to bind the socket again prior to each publish. This throws the error "Address already in use", butt seem to not do much harm.
Without unreasonable delays in between publish messages, the pub-sub fails after 500-1000 messages if the publish socket is not binded again when running from the iPhone.
I made a minimal working example app that you can play around with if you want to, or need to dig deeper. It has buttons for "init", "bind", "publish" and "bind and publish". You can send a batch of messages and check timings etc.
I run the app towards a python3 script.
I included SwiftyZeroMQ5 via cocoapods.
Podfile:
platform :ios, '13.0'
target 'ZMQtest' do
use_frameworks!
pod 'SwiftyZeroMQ5'
end
SwiftCode (set up the buttons your self..)
import UIKit
import SwiftyZeroMQ5
class ViewController: UIViewController {
let context = try! SwiftyZeroMQ.Context()
var publisher: SwiftyZeroMQ.Socket?
let publishEndPoint = "tcp://*:5560"
var cnt = 0
let quota = 1200
#IBAction func publishButtonPressed(_ sender: Any) {
while cnt < quota {
publish()
//usleep(10000)
}
cnt = 1
}
#IBAction func bindAndPublishButtonPressed(_ sender: Any) {
while cnt < quota {
bindPublisher()
publish()
}
cnt = 1
}
#IBAction func initPubButtonPressed(_ sender: Any) {
initPublisher()
}
#IBAction func bindPublisherButtonPressed(_ sender: Any) {
bindPublisher()
}
#IBOutlet weak var statusLabel: UILabel!
// **************
// Init publisher
func initPublisher(){
do {
self.publisher = try context.socket(.publish)
print("Publisher socket created")
}
catch {
print("initPublisher error: ", error)
}
}
// **************
// Bind publisher
func bindPublisher(){
do {
try self.publisher?.bind(publishEndPoint)
print("Publisher socket binded to :", publishEndPoint)
}
catch {
print("bindPublisher error: ", error)
}
}
// *****************
// Publish a message
func publish(){
// Publish dummy string
do{
cnt += 1
let str = "topic {\"key\": \"" + String(cnt) + "\"}"
try self.publisher?.send(string: str)
statusLabel.text = str
print("Publish message no: ", String(cnt))
}
catch{
print("publisher error: ", error)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
And the python3 code:
#!/usr/bin/env python3
'''Minimal running example of a ZMQ SUB socket.'''
import json
import zmq
def demogrify(msg: str):
'''inverse of mogrify()'''
try:
(topic, message) = msg.split(maxsplit=1)
except ValueError:
(topic, message) = (msg, '{}')
return topic, json.loads(message)
def close_socket_gracefully(socket):
'''graceful termination'''
socket.setsockopt(zmq.LINGER, 0) # to avoid hanging infinitely
socket.close()
if __name__ == "__main__":
context = zmq.Context()
socket = context.socket(zmq.SUB) #pylint: disable=no-member
socket.connect("tcp://192.168.1.2:5560")
socket.setsockopt_string(zmq.SUBSCRIBE, '') #pylint: disable=no-member
socket.RCVTIMEO = 1000 # in milliseconds
while socket:
try:
msg = str(socket.recv(), 'utf-8')
(topic, msg) = demogrify(msg)
print((topic, msg))
except zmq.error.Again as error:
print(str(error))
except KeyboardInterrupt:
close_socket_gracefully(socket)
socket = None
Should I mark this issue as solved or not?

Related

Unable to run vapor as server into an iOS app

I am trying to add a REST API handling function to my existing iOS App. What I am trying to achieve is to make my iOS app work as local server network. I just started to learn about Vapor.
Face a problem when I want to start the server. It throws error
Unknown command -NSDocumentRevisionsDebugMode
Here is my class
import Vapor
import Leaf
class BackendManager {
var app = Application(.development)
init() {
app.http.server.configuration.hostname = "0.0.0.0"
app.http.server.configuration.port = 8080
app.views.use(.leaf)
app.leaf.cache.isEnabled = app.environment.isRelease
app.leaf.configuration.rootDirectory = Bundle.main.bundlePath
app.routes.defaultMaxBodySize = "50MB"
}
func start() {
Task(priority: .background) {
do {
try app.start()
} catch {
fatalError(String(describing: error))
}
}
}
}
And here is how I call it
let server = BackendManager()
server.start()
Here is my configuration
What am I missing here? Thanks ...
After followed the solution suggested in (Xcode and Python) error: unrecognized arguments: -NSDocumentRevisionsDebugMode
It will throw another error -AppleLanguages
Someone in the Discord group helped me to solve this by passing this configuration before start the server
app.environment.arguments = [app.environment.arguments[0]] and it is done
Hope this will help anyone affected in the future

Test using StepVerifier blocks when using Spring WebClient with retry

EDIT: here https://github.com/wujek-srujek/reactor-retry-test is a repository with all the code.
I have the following Spring WebClient code to POST to a remote server (Kotlin code without imports for brevity):
private val logger = KotlinLogging.logger {}
#Component
class Client(private val webClient: WebClient) {
companion object {
const val maxRetries = 2L
val firstBackOff = Duration.ofSeconds(5L)
val maxBackOff = Duration.ofSeconds(20L)
}
fun send(uri: URI, data: Data): Mono<Void> {
return webClient
.post()
.uri(uri)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(data)
.retrieve()
.toBodilessEntity()
.doOnSubscribe {
logger.info { "Calling backend, uri: $uri" }
}
.retryExponentialBackoff(maxRetries, firstBackOff, maxBackOff, jitter = false) {
logger.debug { "Call to $uri failed, will retry (#${it.iteration()} of max $maxRetries)" }
}
.doOnError {
logger.error { "Call to $uri with $maxRetries retries failed with $it" }
}
.doOnSuccess {
logger.info { "Call to $uri succeeded" }
}
.then()
}
}
(It returns an empty Mono as we don't expect an answer, nor do we care about it.)
I would like to test 2 cases, and one of them is giving me headaches, namely the one in which I want to test that all the retries have been fired. We are using MockWebServer (https://github.com/square/okhttp/tree/master/mockwebserver) and the StepVerifier from reactor-test. (The test for success is easy and doesn't need any virtual time scheduler magic, and works just fine.) Here is the code for the failing one:
#JsonTest
#ContextConfiguration(classes = [Client::class, ClientConfiguration::class])
class ClientITest #Autowired constructor(
private val client: Client
) {
lateinit var server: MockWebServer
#BeforeEach
fun `init mock server`() {
server = MockWebServer()
server.start()
}
#AfterEach
fun `shutdown server`() {
server.shutdown()
}
#Test
fun `server call is retried and eventually fails`() {
val data = Data()
val uri = server.url("/server").uri()
val responseStatus = HttpStatus.INTERNAL_SERVER_ERROR
repeat((0..Client.maxRetries).count()) {
server.enqueue(MockResponse().setResponseCode(responseStatus.value()))
}
StepVerifier.withVirtualTime { client.send(uri, data) }
.expectSubscription()
.thenAwait(Duration.ofSeconds(10)) // wait for the first retry
.expectNextCount(0)
.thenAwait(Duration.ofSeconds(20)) // wait for the second retry
.expectNextCount(0)
.expectErrorMatches {
val cause = it.cause
it is RetryExhaustedException &&
cause is WebClientResponseException &&
cause.statusCode == responseStatus
}
.verify()
// assertions
}
}
I am using withVirtualTime because I don't want the test to take nearly seconds.
The problem is that the test blocks indefinitely. Here is the (simplified) log output:
okhttp3.mockwebserver.MockWebServer : MockWebServer[51058] starting to accept connections
Calling backend, uri: http://localhost:51058/server
MockWebServer[51058] received request: POST /server HTTP/1.1 and responded: HTTP/1.1 500 Server Error
Call to http://localhost:51058/server failed, will retry (#1 of max 2)
Calling backend, uri: http://localhost:51058/server
MockWebServer[51058] received request: POST /server HTTP/1.1 and responded: HTTP/1.1 500 Server Error
Call to http://localhost:51058/server failed, will retry (#2 of max 2)
As you can see, the first retry works, but the second one blocks. I don't know how to write the test so that it doesn't happen. To make matters worse, the client will actually use jitter, which will make the timing hard to anticipate.
The following test using StepVerifier but without WebClient works fine, even with more retries:
#Test
fun test() {
StepVerifier.withVirtualTime {
Mono
.error<RuntimeException>(RuntimeException())
.retryExponentialBackoff(5,
Duration.ofSeconds(5),
Duration.ofMinutes(2),
jitter = true) {
println("Retrying")
}
.then()
}
.expectSubscription()
.thenAwait(Duration.ofDays(1)) // doesn't matter
.expectNextCount(0)
.expectError()
.verify()
}
Could anybody help me fix the test, and ideally, explain what is wrong?
This is a limitation of virtual time and the way the clock is manipulated in StepVerifier. The thenAwait methods are not synchronized with the underlying scheduling (that happens for example as part of the retryBackoff operation). This means that the operator submits retry tasks at a point where the clock has already been advanced by one day. So the second retry is scheduled for + 1 day and 10 seconds, since the clock is at +1 day. After that, the clock is never advanced so the additional request is never made to MockWebServer.
Your case is made even more complicated in the sense that there is an additional component involved, the MockWebServer, that still works "in real time".
Though advancing the virtual clock is a very quick operation, the response from the MockWebServer still goes through a socket and thus has some amount of latency to the retry scheduling, which makes things more complicated from the test writing perspective.
One possible solution to explore would be to externalize the creation of the VirtualTimeScheduler and tie advanceTimeBy calls to the mockServer.takeRequest(), in a parallel thread.

Parse Live Query: Not receiving Events on iOS Client. Our Parse server hosted on Azure App Services

Issue Description
We have hosted Parse server on Azure App Services. We are able to connect to live Query and able to Subscribe to it. We are getting below messages.
2019-01-01 19:03:26.917094+0530 LiveQueryTest[59625:972922] ParseLiveQuery: Sending message:
{"clientKey":"xxxxxx","op":"connect","sessionToken":"","applicationId":"xxxxxx"}
2019-01-01 19:03:27.285251+0530 LiveQueryTest[59625:972922] ParseLiveQuery: Received message: {"op":"connected","clientId":5}
2019-01-01 19:03:27.388337+0530 LiveQueryTest[59625:972922] ParseLiveQuery: Sending message: {"query":{"className":"PostQuestionMessage","where":{"type":2}},"requestId":1,"op":"subscribe"}
2019-01-01 19:03:27.600455+0530 LiveQueryTest[59625:972813] ParseLiveQuery: Received message: {"op":"subscribed","clientId":5,"requestId":1}
And we are subscribed to Update Event but when we updated any records we are not getting event Back.
On the server:
We tried to listen to Specific port "1337" as below: Every time we do that our Parse Dashboard and API stops working. So Is it necessary to listen to specific port i.e "1337" or var port = process.env.PORT || 1337; will also work for Live Query.
var httpServer = require('http').createServer(app);
var port = 1337;
httpServer.listen(port, function() {
console.log('Parse Server running at ${port}');
console.log("The value of port is " + port);
});
ParseServer.createLiveQueryServer(httpServer);
Expected Results
We should get the event update as per our subscription.
Actual Outcome
Not receiving event updates as per the Query Subscribed,
Environment Setup
Server
parse-server version (Be specific! Don't say 'latest'.) : [2.3.8]
Operating System: [Linux]
[Remote Server - Azure]
Database
MongoDB version: [3.4]
[Remote Server - Azure]
Logs/Trace
Server:
[32minfo�[39m: Parse LiveQuery Server starts running
�[32minfo�[39m: Create new client: 1
iOS client Code:
import UIKit
import Parse
import ParseLiveQuery
class LiveQueryManager {
static let shared = LiveQueryManager()
var liveQueryClient = ParseLiveQuery.Client()
var subscription: Subscription<PostQuestionMessage>?
var messagesQuery: PFQuery<PostQuestionMessage> {
return (PostQuestionMessage.query()?
.whereKey("type", equalTo: 2)) as! PFQuery<PostQuestionMessage>
}
init(){
liveQueryClient.shouldPrintWebSocketLog = true
liveQueryClient.shouldPrintWebSocketTrace = true
}
fileprivate func printMessage(_ message: PostQuestionMessage) {
let createdAt = message.createdAt ?? Date()
print("\(createdAt) : \(message.message ?? "")")
}
func subscribeToUpdates() {
//https://stackoverflow.com/questions/44273455/parse-cloud-livequeries-ios-client-doesnt-work
self.subscription = self.liveQueryClient.subscribe(messagesQuery)
self.subscription = self.subscription?.handleEvent({ (_, event) in
switch event {
case .created(let object):
print("Event Created")
self.printMessage(object)
// do stuff
case .updated(let object):
print("Event Updated")
self.printMessage(object)
default:
break // do other stuff or do nothing
}
})
}
func disconnectFromSubscription() {
if let objSubcription = self.subscription {
self.liveQueryClient.unsubscribe(messagesQuery, handler: objSubcription)
}
}
func printSubscription () {
if let objSubscription = self.subscription {
print ("Subscription:\(objSubscription)")
print ("Client:\(self.liveQueryClient)")
print ("Client userDisconnected:\(self.liveQueryClient.userDisconnected)")
}
}
}

React-Native Websocket Event data property is missing

I am trying to connect to the Watson TTS API over a Websocket connection in React-Native. The connection is established and I can send a message to the server, however the data that I get back from the server somehow always is empty.
It seems as if the event.data property is completely missing. If I log it to the console in react-native I get 'undefined' as a result. If i use the same code in the browser everything works perfectly.
I am using react-native 0.33 and here's my code:
function connectTTS(token) {
var voice = "de-DE_BirgitVoice";
var format = 'audio/basic';
var token = token;
var wsURI = "wss://stream.watsonplatform.net/text-to-speech/api/v1/synthesize?voice=" + voice + "&watson-token=" + token;
function onOpen(evt) {
var message = {
text: "Hello world.",
accept: format
};
// note: the Text to Speech service currently only accepts a single message per WebSocket connection
websocket.send(JSON.stringify(message));
}
var audioParts = [];
var finalAudio;
function onMessage(evt) {
console.log(evt.data);
if (typeof evt.data === 'string') {
console.log('Received string message: ', evt.data)
} else {
console.log('Received ' + evt.data.size + ' binary bytes', evt.data.type);
audioParts.push(evt.data);
}
}
function onClose(evt) {
console.log('WebSocket closed', evt.code, evt.reason);
console.log(audioParts);
console.log(format);
finalAudio = new Blob(audioParts, {type: format});
console.log('final audio: ', finalAudio);
}
function onError(evt) {
console.log('WebSocket error', evt);
}
var websocket = new WebSocket(wsURI);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage;
websocket.onerror = onError;
}
It would be great if somebody with more react-native / websocket experience could help me find the solution. Thanks.
In react-native up to 0.53 (latest version at the moment), react-native WebSocket event processing relies on event-target-shim 1.1.1 lib which wraps an event and does not include data to the wrapped event, so in order to get WebSocket event data you may use one of two approaches:
Get data from __proto__;
Rewrite event-target-shim 1.1.1;
The first approach:
use <your event>.__proto__.__proto__.data
The second approach:
fork event-target-shim and reset to event-target-shim 1.1.1;
fork react-native;
Add the code listed below to the event-target-shim/lib/event-wrapper.js;
rewrite react-native package.json to use forked version of the event-target-shim;
rewrite package.json of your project;
Code to add in exports.createEventWrapper after var propertyDefinition = {...}:
if (event.type === "message"){
propertyDefinition.data = {value: event.data, enumerable: true};
}

Spray.IO Stop http server with Http.Unbind

I have an app built on spray 1.3.3 and I'm trying to stop http server by sending message.
Official documentation says:
Stopping. To explicitly stop the server, send an Http.Unbind command to
the HttpListener instance (the ActorRef for this instance is available
as the sender of the Http.Bound confirmation event from when the
server was started).
Here is how i the start server:
(IO(Http) ? Http.Bind(httpListener, interface = iface, port = port)).map {
case m: Http.Bound => println(s"Success $iface:$port")
case fail : Http.CommandFailed => println(s"Bad try :(")
}
I tried to send message Http.Unbind to httpListener but with no success. Seems to be it's not that actor
May be i need to extract somehow sender ref of Http.Bound message? But how?
Here is my httpListener's head:
class MyHttpListener extends HttpServiceActor {
import context.dispatcher
def receive = runRoute(
path("shutdown") {
get {
actors.httpListener ! Http.Unbind
complete("Stopping server")
}
}
Anyway, i just want to send http request to /shutdown and have my application be down
The sender of the Bound message should be the Spray listener, so when receiving that message you can capture it.
You did not post your full actor so this is a little different but I hope this helps.
class MyStarter extends Actor {
// this will be the listener
var myListener: ActorRef = _
override def preStart(): Unit = {
implicit val system = context.system
IO(Http) ! Http.Bind(...)
}
override def receive: Receive = {
case msg: Bound =>
// when you receive the Bound message it was sent by the listener
myListener = sender()
...
case ... => ...
}
}

Resources