Sending UDP packet with CocoaAsyncSocket - ios

I'm trying to send a UDP packet with CocoaAsyncSocket. I've implemented the socket instance creation and the send method as follows:
let IP = "192.168.2.255"
let PORT:UInt16 = 6454
var socket:GCDAsyncUdpSocket!
func sendPacket(){
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
let message = "test"
let data = message.dataUsingEncoding(NSUTF8StringEncoding)
socket.sendData(data, toHost: IP, port: PORT, withTimeout: 2, tag: 0)
}
I'm testing it with an iPad connected on my local Wi-Fi network. On a computer connected on the same network, I've installed Wireshark and I'm listening to the UDP packets received. No packet corresponding to mine is received.
Any idea why? :/

Related

How to create NWConnection

Premise
I'm using Apple's Network framework to create a peer-to-peer app. What I'm currently trying to do is:
Device A establishes a connection with Device B and sends data.
Device B saves the NWConnection information of Device A.
Device B establishes a connection with Device A again using the saved MWConnection information and sends data.
I modelled off Apple's demo, which is essentially this source code here. Unlike Apple's demo, which establishes a single two-way connection and maintains that connection at all times, my app connects to multiple devices that can drop in and out of the connections at any time. This is why I want to be able to a) distinguish a specific device and b) initiate a new connection.
Problem
The problem I'm having is being able to reconstruct the NWConnection object using the information provided when a connection has been established. There are two ways of instantiating this object.
init(host: NWEndpoint.Host, port: NWEndpoint.Port, using: NWParameters)
init(to: NWEndpoint, using: NWParameters)
My attempts have been trying to gather the endpoint information like the host and the port while the connection with the desired device has been established and instantiate NWEndpoint to be used in NWConnection. But, I haven't been able to re-establish the connection thus far.
Following is the portion of the object that is used to initiate a connection. My full code is modified, but Apple's counterpart can be seen here.
class PeerConnection {
weak var delegate: PeerConnectionDelegate?
weak var statusDelegate: StatusDelegate?
var connection: NWConnection?
let initiatedConnection: Bool
init(endpoint: NWEndpoint, interface: NWInterface?, passcode: String, delegate: PeerConnectionDelegate) {
self.delegate = delegate
self.initiatedConnection = true
let connection = NWConnection(to: endpoint, using: NWParameters(passcode: passcode))
self.connection = connection
startConnection()
}
init(connection: NWConnection, delegate: PeerConnectionDelegate) {
self.delegate = delegate
self.connection = connection
self.initiatedConnection = false
startConnection()
}
// Handle starting the peer-to-peer connection for both inbound and outbound connections.
func startConnection() {
guard let connection = connection else {
return
}
connection.stateUpdateHandler = { newState in
switch newState {
case .ready:
print("\(connection) established")
// When the connection is ready, start receiving messages.
self.receiveNextMessage()
// Notify your delegate that the connection is ready.
if let delegate = self.statusDelegate {
delegate.showConnectionStatus(.connectionSuccess("Connection Success"))
}
case .failed(let error):
print("\(connection) failed with \(error)")
// Cancel the connection upon a failure.
connection.cancel()
// Notify your delegate that the connection failed.
if let delegate = self.statusDelegate {
delegate.showConnectionStatus(.connectionFail("Connection Fail"))
}
default:
break
}
}
// Start the connection establishment.
connection.start(queue: .main)
}
}
This is how I receive data when another device sends it.
func receiveNextMessage() {
guard let connection = connection else {
return
}
/// Has to call itself again within the closure because the maximum message is once.
connection.receiveMessage { (content, context, isComplete, error) in
// Extract your message type from the received context.
if let message = context?.protocolMetadata(definition: GameProtocol.definition) as? NWProtocolFramer.Message {
self.delegate?.receivedMessage(content: content, message: message, connection: connection)
}
if error == nil {
// Continue to receive more messages until you receive and error.
self.receiveNextMessage()
}
}
}
Finally, following is how I attempt to reconstruct MWConnection:
func receivedMessage(content: Data?, message: NWProtocolFramer.Message, connection: NWConnection) {
switch(connection.endpoint) {
case .hostPort(let host, let port):
/// first attempt
let endpoint = NWEndpoint.hostPort(host: host, port: port)
let newConnection = PeerConnection(endpoint: endpoint, interface: nil, passcode: passcode, delegate: self)
/// second attempt
let connection = NWConnection(host: host, port: port, using: NWParameters(passcode: passcode))
let newConnection = PeerConnection(connection: connection, delegate: self)
default:
break
}
}
NWConnection is instantiated inside initializer of PeerConnection.

Unable to connect to remote RabbitMQ server using Swift

I am using RabbitMQ in my app for chat module. It works fine with local server but somehow I am not able to connect to remote RabbitMQ server. I keep getting this error when I try to send a message.
Received connection: <RMQConnection: 0x6000022e2eb0>
disconnectedWithError: Error Domain=GCDAsyncSocketErrorDomain Code=7
"Socket closed by remote peer" UserInfo={NSLocalizedDescription=Socket
closed by remote peer}
My swift code looks like this:
func getRabbitMQUrl() -> String{
var components = URLComponents()
components.scheme = "amqps"
components.host = "[broker-id].mq.[region].amazon.aws.com"
components.user = "[username]"
components.password = "[passowrd]"
components.port = 5671
let url = components.url?.absoluteString ?? "-"
print("RabbitMQ URL", url)
return url
}
let uri = getRabbitMQUrl()
let conn = RMQConnection(uri: uri, delegate: RMQConnectionDelegateLogger())
conn.start()
let ch = conn.createChannel()
let q = ch.queue(UUID().uuidString, options: .durable)
let exc = ch.direct("my-exchange-name-here")
q.bind(exc, routingKey: "my-routing-key")
q.subscribe({(_ message: RMQMessage) -> Void in
print("Message received")
})
While using the local server, I set the uri "amqp://[username]:[password]#localhost:5672" and this works as expected.
PS: when I set this subscriber I do not get any error message regarding connection or anything. so I think it is connecting to the server without any issue.
But, when I send a message from the iOS app, the backend publish it and so the iOS app should receive it back. Exactly at this time, it gives me the above error!
EDIT: Though the C# backend is able to publish and subscribe messages successfully with RabbitMQ remote server. It is just the iOS client who fails!
Any help would be appreciated!
After going through a lots of links, slack channels and Github issues, finally the issue has been resolved! The solution was unexpected.
The problem was, my C# backend has set the vhost to a slash / and in my Swift code I was passing an empty string instead. I got hint from here
I made these 2 changes in my code:
In server uri I added %2f(a slash /) as the vhost at the end.
I set the options of the exchange also to .durable just like the queue
Here is the complete working code:
func getRabbitMQUrl() -> String{
var components = URLComponents()
components.scheme = "amqps"
components.host = "[broker-id].mq.[region].amazon.aws.com"
components.user = "[username]"
components.password = "[passowrd]"
components.port = 5671
components.path = "/%2f" //1st change
let url = components.url?.absoluteString ?? "-"
print("RabbitMQ URL", url)
return url
}
let uri = getRabbitMQUrl()
let conn = RMQConnection(uri: uri, delegate: RMQConnectionDelegateLogger())
conn.start()
let ch = conn.createChannel()
let q = ch.queue(UUID().uuidString, options: .durable)
let exc = ch.direct("my-exchange-name-here", options: .durable) // 2nd change
q.bind(exc, routingKey: "my-routing-key")
q.subscribe({(_ message: RMQMessage) -> Void in
print("Message received")
})
Your URI is AMQP on the local host but AMQP is the example code.
You should connect to port 5671 if you are using AMQPS (And 5672 if you are on AMQP) Try that!

SwiftNIO: Send and receive UDP broadcast

I'm trying to build a TCP server with SwiftNIO. The server starts in the net, but the clients don't know the ip address. Therefore I want to start an UDP server as well and if the clients comes up, he sends a broadcast message to the net. The server will receive and answer, so that the client now knows the IP address.
Is it possible to build something like this with SwiftNIO?
Yes, that's possible also there's not much support in SwiftNIO to make this easy.
See below for a commented example which will send HELLO WORLD once a second to en0's broadcast address and port 37020.
import NIO
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
try! group.syncShutdownGracefully()
}
let matchingInterfaces = try System.enumerateInterfaces().filter {
// find an IPv4 interface named en0 that has a broadcast address.
$0.name == "en0" && $0.broadcastAddress != nil
}
guard let en0Interface = matchingInterfaces.first, let broadcastAddress = en0Interface.broadcastAddress else {
print("ERROR: No suitable interface found. en0 matches \(matchingInterfaces)")
exit(1)
}
// let's bind the server socket
let server = try! DatagramBootstrap(group: group)
// enable broadast
.channelOption(ChannelOptions.socket(SOL_SOCKET, SO_BROADCAST), value: 1)
.bind(to: en0Interface.address)
.wait()
print("bound to \(server.localAddress!)")
var buffer = server.allocator.buffer(capacity: 32)
buffer.writeString("HELLO WORLD!")
var destAddr = broadcastAddress
destAddr.port = 37020 // we're sending to port 37020
// now let's just send the buffer once a second.
group.next().scheduleRepeatedTask(initialDelay: .seconds(1),
delay: .seconds(1),
notifying: nil) { task in
server.writeAndFlush(AddressedEnvelope(remoteAddress: destAddr,data: buffer)).map {
print("message sent to \(destAddr)")
}.whenFailure { error in
print("ERROR: \(error)")
// and stop if there's an error.
task.cancel()
server.close(promise: nil)
}
}
try server.closeFuture.wait()
In case you want to bind to 0.0.0.0 and send to 255.255.255.255 you can use this
import NIO
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
try! group.syncShutdownGracefully()
}
// let's bind the server socket
let server = try! DatagramBootstrap(group: group)
// enable broadast
.channelOption(ChannelOptions.socket(SOL_SOCKET, SO_BROADCAST), value: 1)
.bind(to: .init(ipAddress: "0.0.0.0", port: 0))
.wait()
print("bound to \(server.localAddress!)")
var buffer = server.allocator.buffer(capacity: 32)
buffer.writeString("HELLO WORLD!")
// we're sending to port 37020
let destPort = 37020
let destAddress = try SocketAddress(ipAddress: "255.255.255.255", port: destPort)
// now let's just send the buffer once a second.
group.next().scheduleRepeatedTask(initialDelay: .seconds(1),
delay: .seconds(1),
notifying: nil) { task in
server.writeAndFlush(AddressedEnvelope(remoteAddress: destAddress, data: buffer)).map {
print("message sent to \(destAddress)")
}.whenFailure { error in
print("ERROR: \(error)")
// and stop if there's an error.
task.cancel()
server.close(promise: nil)
}
}
try server.closeFuture.wait()

unable to download Tello SDK for IOS

Anyone knows how to download the Tello sdk for iOS. I have visited the link https://www.ryzerobotics.com/tello, but it is not useful
There isn't an official SDK to download for the Tello. You can find the instructions at this link which provides details of sending UDP packets over a network connection to the drone. You can use CocoaAsyncSocket found on GitHub to help you create the connection to the socket and then pass the commands as ASCII data to the Tello.
Here is some sample code to get you going (which I wrote earlier today on my own website):
var socket = GCDAsyncUdpSocket()
let sendHost = "192.168.10.1"
let sendPort: UInt16 = 8889
let statePort: UInt16 = 8890
First, create the socket, a String with the Tello IP, and a couple of constants holding the port numbers (8889 is used to send commands to, and also receives responses back such as "OK" if the command was successful. 8890 is used for receiving telemetry from the drone multiple times per second.
This will send the initial "command" command to the Tello:
// Set the delegate and dispatch queue
socket.setDelegate(self)
socket.setDelegateQueue(DispatchQueue.main)
// Send the "command" command to the socket.
do {
try socket.bind(toPort: sendPort)
try socket.enableBroadcast(true)
try socket.beginReceiving()
socket.send("command".data(using: String.Encoding.utf8)!,
toHost: sendHost,
port: sendPort,
withTimeout: 0,
tag: 0)
} catch {
print("Command command sent.")
}
You need to make sure that the class conforms to GCDAsyncUdpSocketDelegate.
Implement the delegate method which receives data from the Tello.
func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
let dataString = String(data: data, encoding: String.Encoding.utf8)
if (sock.localPort() == sendPort) {
print(dataString)
}
if (sock.localPort() == statePort) {
var telloStateDictionary = [String:Double]()
let stateArray = dataString?.components(separatedBy: ";")
for itemState in stateArray! {
let keyValueArray = itemState.components(separatedBy: ":")
if (keyValueArray.count == 2) {
telloStateDictionary[keyValueArray[0]] = Double(keyValueArray[1])
}
}
print(telloStateDictionary)
}
}
Set up port 8890 for listening to the telemetry:
let receiveSocket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
do {
try receiveSocket.bind(toPort: statePort)
} catch {
print("Bind Problem")
}
do {
try receiveSocket.beginReceiving()
} catch {
print("Receiving Problem")
}
Run the app. Assuming you have manually connected to the Tello (like you would if you were controlling it with the Tello app), you should begin getting information back from the Tello.
To send more commands to it, such as "takeoff" or "cw 360", you can do something such as:
func sendCommand(command: String) {
let message = command.data(using: String.Encoding.utf8)
socket.send(message!, toHost: sendHost, port: sendPort, withTimeout: 2, tag: 0)
}
Pass in the command as a string, and this will send it to the Tello.

Swift Socket, listening to incoming message

I am trying to implement swift socket in my application. I am pretty new to socket programming. I was able to send messages through the socket. But I am unable to listen to any messages from server. I have implemented the following code:
let host = Urls.GetServerIP()
let port = portNo
client = TCPClient(address: host, port: Int32(port))
switch client?.connect(timeout: 10) {
case .success?:
print((readResponse(from: client!)))
case .failure(let error)?:
print( String(describing: error))
break
case .none: break
}
In the above code, I am able to read an incoming message which comes as soon as I connect. But, I am unable to find a way to know that there is a message from the server, after couple of minutes.
Are there any specific call backs to know when a new message arrives from the server?
I have implemented StreamDelegate to my class. But no call backs are being triggered.
Thanks
It seems that your code lacks continuous reading, which is the reason that you only get a message once. I used to use SwiftSocket myself some time ago. The problem with this framework is that it is too simple and lacks a lot of functionality, for example encryption.
I switched to CocoaAsyncSocket, which is the Swiss army knife to handle socket connections in Swift.
Here is a short Example:
import CocoaAsyncSocket
class TcpSocketConnection: GCDAsyncSocketDelegate {
let tcpSocket: GCDAsyncSocket?
init(host: String, port: UInt16) {
self.tcpSocket = GCDAsyncSocket(delegate: self)
do {
try tcpSocket?.connect(toHost: host, onPort: port, withTimeout: 5.0)
} catch let error {
print("Cannot open socket to \(host):\(port): \(error)")
self.tcpSocket = nil
}
}
func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) {
self.tcpSocket?.readData(toLength: 1024, withTimeout: 60.0)
}
func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) {
// Process data
self.tcpSocket?.readData(toLength: 1024, withTimeout: 60.0, tag: 0)
}
}
The code highly depends from how your server behaves and what you exactly want to do. Socket handling is not an easy task, especially when you add encryption (which you should do in modern applications). You can use the documentation as a starting point but you will have to read and learn a lot.
You can use SocketIOClient for web sockets. It can be used to send or receive messages through sockets.
Link:
https://github.com/nuclearace/Socket.IO-Client-Swift

Resources