iOS websocket without handshake - ios

I want to establish a simple local Socket communication on iOS. After looking into other libraries, I found the websocket lib that is now native to Swift since last year works quite well, but comes with a nasty handshake that I would like to disable (otherwise I would have to adjust the server response etc.). My code looks like this:
let webSocketDelegate = WebSocket()
let session = URLSession(configuration: .default, delegate: webSocketDelegate, delegateQueue: OperationQueue())
let url = URL(string: "ws://192.168.43.1:6000")!
let webSocketTask = session.webSocketTask(with: url)
func send() {
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
send()
webSocketTask.send(.string("New Message")) { error in
if let error = error {
print("Error when sending a message \(error)")
}
}
}
}
webSocketTask.receive { result in
switch result {
case .success(let message):
switch message {
case .data(let data):
print("Data received \(data)")
case .string(let text):
print("Text received \(text)")
}
case .failure(let error):
print("Error when receiving \(error)")
}
}
webSocketTask.resume()
I googled and found nw_ws_options_set_skip_handshake, but I could not find an example how to use it. Any ideas?

As #SteffenUllrich suggested, I looked more into TCP socket communication and found a simple way to communicate. Here my solution, hope it will help someone else:
let queue = DispatchQueue(label: "TCP Client Queue")
let connection = NWConnection(to: NWEndpoint.hostPort(host: "192.168.99.1", port: 7000), using: NWParameters.init(tls: nil))
func connect() {
connection.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print("Socket: ready")
sendString(text: "Hello server!")
receiveMsg()
default: print("Socket: \(newState)")
}
}
connection.start(queue: queue)
}
func receiveMsg() {
connection.receive(minimumIncompleteLength: 1, maximumLength: 1000, completion: { (data, context, isComplete, error) in
if((data) != nil){
let text:String = String(decoding: data!, as: UTF8.self)
//send string to handler function, trim linebreak for single line commands
handleMsg(msg: text.trimmingCharacters(in: .newlines))
}
else{receiveMsg()//go back to receiving}
})
}
func receiveFile() {
print("Receiving file...")
//fileSize was sent before submission
connection.receive(minimumIncompleteLength: Int(fileSize), maximumLength: Int(fileSize)+1000, completion: { (data, context, isComplete, error) in
saveFile(data: data!)//save data to file
})
}
func sendString(text:String) {
let data:Data? = "\(text)\n".data(using: .utf8)//add linebreak to single line string, otherwise it does not release transmission
connection.send(content: data, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to TCP destination ")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
You just need to call connect() to start the connection, use sendString() to send some text. Receiving should run constantly. I didn't share the command handler, but it re-runs receiveMsg() on a regular command, and receiveFile() when a file transmission is initiated by the server (the init also contains the fileSize required to set the expected bytesize in the receive() function!).

Related

How to split data in packets and send it using UDP connection

I am trying to send an image via an UDP connection.
class Connect: NSObject {
var connection: NWConnection?
func connectToUDP(host: String, port: UInt16) {
let host = NWEndpoint.Host(host)
guard let port = NWEndpoint.Port(rawValue: port) else { return }
let messageToUDP = "Bonjour"
connection = NWConnection(host: host, port: port, using: .udp)
connection?.stateUpdateHandler = { (newState) in
print("This is stateUpdateHandler:")
switch (newState) {
case .ready:
print("State: Ready\n")
self.sendUDP(messageToUDP)
self.receiveUDP()
default:
print("ERROR! State not defined!\n")
}
}
connection?.start(queue: .global())
}
func sendUDP(_ content: Data) {
connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
}
I converted the image into data and when I send it :
guard let image = UIGraphicsGetImageFromCurrentImageContext(),
let data = image.pngData() else { return }
connect.connectToUDP(host: "192.168.XX.XX", port: 10000)
connect.sendUDP(data)
I got this error
ERROR! Error when data (Type: Data) sending. NWError: POSIXErrorCode: Message too long
How can I split the data in packets and in order the data get fully reconstructed by the receiver ?

NSPOSIXErrorDomain Code=57 "Socket is not connected"

I'm creating a chat app using websocket.
I'm connecting to server with URLSessionWebSocketTask.
url = ws://"appname".herokuapp.com/chats/listen/
func connect(url: String) {
self.socket = session.webSocketTask(with: URL(string: url)!)
self.listen()
self.socket.resume()
}
func listen() {
self.socket.receive { [weak self] (result) in
guard let self = self else { return }
switch result {
case .failure(let error):
print(error)
return
case .success(let message):
switch message {
case .data(let data):
self.handle(data)
case .string(let str):
guard let data = str.data(using: .utf8) else { return }
self.handle(data)
#unknown default:
break
}
}
self.listen()
}
}
It's running okay on localhost, but after I deploy the server to Heroku, I get this message:
NSPOSIXErrorDomain Code=57 "Socket is not connected
I guest it was auto disconnect after a time out.
So, I add ping function to ping every 10s. Then, it works:
func sendPing() {
self.socket.sendPing { (error) in
if let error = error {
print("Sending PING failed: \(error)")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
self.sendPing()
}
}
}

Swift UDP Connection Issue

protocol UDPListener {
func handleResponse(_ client: UDPConnection, data: Data)
}
class UDPConnection{
var connection: NWConnection?
var listner: NWListener?
var delegate: UDPListener?
let parameters = NWParameters.udp
// for connect with device
func connect(withHost: NWEndpoint.Host, port: NWEndpoint.Port) {
parameters.allowLocalEndpointReuse = true
self.connection = NWConnection(host: withHost, port: port, using: parameters)
self.connection?.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print("connection ready")
case .setup:
print("connectionsetup")
case .cancelled:
print("connectioncancelled")
case .preparing:
print("connection Preparing")
default:
print("connection waiting or failed")
}
}
self.connection?.start(queue: .global())
}
// for sending UDP string
func sendUDP(_ content: String) {
let contentToSendUDP = content.data(using: String.Encoding.utf8)
self.connection?.send(content: contentToSendUDP, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR SEND! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
//for sending UDP data
func sendUDP(_ content: Data) {
self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR Send! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
//////////////// UDP LISTNER /////////////////////
func listenUDP(port: NWEndpoint.Port) {
do {
parameters.allowLocalEndpointReuse = true
self.listner = try NWListener(using: parameters, on: port)
self.listner?.stateUpdateHandler = {(newState) in
switch newState {
case .ready:
print("Listner ready")
case .failed:
print("Listner failed")
case .cancelled:
print("Listner cancelled")
default:
break
}
}
self.listner?.newConnectionHandler = {(newConnection) in
newConnection.stateUpdateHandler = {newState in
switch newState {
case .ready:
print("new connection establish")
self.receive(on: newConnection)
case .failed:
print("new connection failed")
case .cancelled:
print("new connection cancelled")
default:
break
}
}
newConnection.start(queue: DispatchQueue(label: "new client"))
}
} catch {
print("unable to create listener")
}
self.listner?.start(queue: .global())
}
func receive(on connection: NWConnection) {
connection.receiveMessage { (data, context, isComplete, error) in
if !isComplete {
print("Not Complete")
}
if let error = error {
print("Error in REceive: - ",error)
return
}
if let data = data, !data.isEmpty {
// let backToString = String(decoding: data, as: UTF8.self)
// print("Received Data: ",backToString)
guard self.delegate != nil else {
print("Error Receive: UDPClient response handler is nil")
return
}
print("Data receive in UDPConenction;")
self.delegate?.handleResponse(self, data: data)
}
}
}
}
Here I attached my UDP file that contains connections and listener of UDP.
When I send some data for the first time, it will send the data and receive the data.
But when I send data again, then data will be sent, but I am not getting data second time and onwards. Listner is not listning anymore. Data will be sent but listener is not listening.
If I close the application and run it again, same issue occurs. First time load the data (listener lister the data) but second time listener is not working.
What is the issue in my code ? I was trying so much but not resolving the issue. Please anyone that can solve my issue would be so grateful to me.
Thank you in advance.
In your function receive you are using the NWConnection.receiveMessage if you check the documentation here:
https://developer.apple.com/documentation/network/nwconnection/3020638-receivemessage
You'll see that it schedules a single receive completion handler. That means that you'll have to do something to trigger it again. What I normally do is have a function like:
private func setupReceive() {
connection.receive(minimumIncompleteLength: 1, maximumLength: MTU) { (data, _, isComplete, error) in
if let data = data, !data.isEmpty {
let message = String(data: data, encoding: .utf8)
print("connection \(self.id) did receive, data: \(data as NSData) string: \(message ?? "-")")
self.send(data: data)
}
if isComplete {
self.connectionDidEnd()
} else if let error = error {
self.connectionDidFail(error: error)
} else {
self.setupReceive() // HERE I SET THE RECEIVE AGAIN
}
}
}
That way, after processing the read, you end up setting up another single receive completion handler.
If you want to see a full example, you can check my article on using Network.framework here:
https://rderik.com/blog/building-a-server-client-aplication-using-apple-s-network-framework/
The example uses TCP instead of UDP, but it should give a general idea.

NWConnection SSDP Discovery not receiving data

I'm trying to do an SSDP Discovery broadcast and unable to get reply data from NWConnection.receive.
Network.framework is relatively new and there is not a lot of info out there. What I'm missing here?
SSDP Discovery broadcast was sent and a UPnP device replied. (Wireshark screenshot below)
import Foundation
import Network
let connection = NWConnection(host: "239.255.255.250", port: 1_900, using: .udp)
func sendBroadcast() {
let message = """
M-SEARCH * HTTP/1.1
ST: ssdp:all
HOST: 239.255.255.250:1900
MAN: ssdp:discover
MX: 1
""".data(using: .utf8)
connection.send(content: message, completion: .contentProcessed { error in
if let error = error {
print("Send Error: \(error)")
} else {
print("Broadcast sent")
}
}
)
}
connection.stateUpdateHandler = { newState in
switch newState {
case .setup:
print("Connection: Setup")
case .preparing:
print("Connection: Preparing")
case .waiting:
print("Connection: Waiting")
case .ready:
print("Connection: Ready")
sendBroadcast()
case .failed:
print("Connection: Failed")
case .cancelled:
print("Connection: Cancelled")
}
}
connection.receive(minimumIncompleteLength: 2, maximumLength: 4_096) { data, context, isComplete, error in
/// This is never executed
///
print(data ?? "", context ?? "", isComplete, error ?? "")
}
connection.viabilityUpdateHandler = { update in
print(update)
}
connection.betterPathUpdateHandler = { path in
print(path)
}
connection.start(queue: .main)
RunLoop.main.run()
Turns out Network.framework does not support UDP Broadcasts yet (Feb 2019)
https://forums.developer.apple.com/message/316357#316357
With UDP try this method instead:
connection.receiveMessage { (data, context, isComplete, error) in
print(data ?? "", context ?? "", isComplete, error ?? "")
}
Here is a good example of it in use here
I had the opposite problem with TCP and was using connection.receiveMessage(...) and the same thing was happening -the callback was never entered. I posted a question in Apple Forums. It turns out with TCP you can only use:
connection.receive(minimumIncompleteLength: 1, maximumLength: 65535) { data, context, isComplete, error in
print(data ?? "", context ?? "", isComplete, error ?? "")
}
An Apple Developer Technical Support Specialist named eskimo answered it here:
. TCP is not a message-oriented protocol, and thus
receiveMessage(…)
doesn’t make any sense. What you want is
receive(minimumIncompleteLength:maximumLength:completion:)
That being said, with UDP try connection.receiveMessage(…)

How to work with UDP sockets in iOS, swift?

I'm trying connect to local esp8266 UDP server. SwiftSocket haven't documentation. CocoaAsyncSocket doesn't work.
How to connect and send data to udp server? What i should do?
I wrote sample UDP python server and tried connect to them via SwiftSocket and CocoaAsyncSocket. I'm don't get feedback from app. Server don't receive connections.
For example- one of the most attempts:
var connection = NWConnection(host: "255.255.255.255", port: 9093, using: .udp)
connection.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print("ready")
case .setup:
print("setup")
case .cancelled:
print("cancelled")
case .preparing:
print("Preparing")
default:
print("waiting or failed")
break
}
}
connection.start(queue: .global())
connection.send(content: "Xyu".data(using: String.Encoding.utf8), completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
print(NWError)
})))
connection.receiveMessage { (data, context, isComplete, error) in
print("Got it")
print(data)
}
Can't connect to the server
This solution work for me! Thanks #Paulw11
Swift 4, XCode 10.1, iOS 12.0
Simple connect to the public UDP server (This is NOT optimal version but works):
import UIKit
import Network
class ViewController: UIViewController {
var connection: NWConnection?
var hostUDP: NWEndpoint.Host = "iperf.volia.net"
var portUDP: NWEndpoint.Port = 5201
override func viewDidLoad() {
super.viewDidLoad()
// Hack to wait until everything is set up
var x = 0
while(x<1000000000) {
x+=1
}
connectToUDP(hostUDP,portUDP)
}
func connectToUDP(_ hostUDP: NWEndpoint.Host, _ portUDP: NWEndpoint.Port) {
// Transmited message:
let messageToUDP = "Test message"
self.connection = NWConnection(host: hostUDP, port: portUDP, using: .udp)
self.connection?.stateUpdateHandler = { (newState) in
print("This is stateUpdateHandler:")
switch (newState) {
case .ready:
print("State: Ready\n")
self.sendUDP(messageToUDP)
self.receiveUDP()
case .setup:
print("State: Setup\n")
case .cancelled:
print("State: Cancelled\n")
case .preparing:
print("State: Preparing\n")
default:
print("ERROR! State not defined!\n")
}
}
self.connection?.start(queue: .global())
}
func sendUDP(_ content: Data) {
self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
func sendUDP(_ content: String) {
let contentToSendUDP = content.data(using: String.Encoding.utf8)
self.connection?.send(content: contentToSendUDP, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
func receiveUDP() {
self.connection?.receiveMessage { (data, context, isComplete, error) in
if (isComplete) {
print("Receive is complete")
if (data != nil) {
let backToString = String(decoding: data!, as: UTF8.self)
print("Received message: \(backToString)")
} else {
print("Data == nil")
}
}
}
}
}
You need to wait until your connection is in the ready state before you try and send or receive any data. You will also need to hold a strong reference to your connection in a property to prevent it from being released as soon as the function exits.
var connection: NWConnection?
func someFunc() {
self.connection = NWConnection(host: "255.255.255.255", port: 9093, using: .udp)
self.connection?.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print("ready")
self.send()
self.receive()
case .setup:
print("setup")
case .cancelled:
print("cancelled")
case .preparing:
print("Preparing")
default:
print("waiting or failed")
}
}
self.connection?.start(queue: .global())
}
func send() {
self.connection?.send(content: "Test message".data(using: String.Encoding.utf8), completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
print(NWError)
})))
}
func receive() {
self.connection?.receiveMessage { (data, context, isComplete, error) in
print("Got it")
print(data)
}
}

Resources