How to work with UDP sockets in iOS, swift? - ios

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)
}
}

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 ?

Type of expression is ambiguous without more context - AWS Amplify

I am learning iOS and amplify and struggling my way through implementing a custom auth flow. I have run into an issue I can't resolve. Here is my code:
//
// SessionManager.swift
// Mapwork
//
// Created by James Nebeker on 2/27/21.
//
import Foundation
import Combine
import Amplify
import AmplifyPlugins
enum AuthState {
case signUp
case login
case confirmCode(username: String)
case session (user: AuthUser)
case firstTime
case confirmSignIn
}
final class SessionManager: ObservableObject {
#Published var authState: AuthState = .firstTime
func getCurrentAuthUser() {
if let user = Amplify.Auth.getCurrentUser() {
authState = .session(user: user)
} else {
authState = .login
}
}
func showSignUpView()
{
authState = .signUp
}
func showLoginView()
{
authState = .login
}
func showConfirmationSignInView()
{
authState = .confirmSignIn
}
func signUp(username: String, email: String) {
let userAttributes = [AuthUserAttribute(.email, value: email)]
let options = AuthSignUpRequest.Options(userAttributes: userAttributes)
_ = Amplify.Auth.signUp(username: username, password: UUID().uuidString, options: options) { [weak self] result in
switch result {
case .success(let signUpResult):
if case let .confirmUser(deliveryDetails, _) = signUpResult.nextStep {
print("Delivery details \(String(describing: deliveryDetails))")
DispatchQueue.main.async {
self?.authState = .confirmCode(username: username)
}
} else {
print("Signup Complete")
}
case .failure(let error):
print("An error occurred while registering a user \(error)")
}
}
}
func confirmSignUp(for username: String, with confirmationCode: String) {
Amplify.Auth.confirmSignUp(for: username, confirmationCode: confirmationCode) { [weak self] result in
switch result {
case .success:
print("Confirm signUp succeeded")
DispatchQueue.main.async {
self?.showLoginView()
}
case .failure(let error):
print("An error occurred while confirming sign up \(error)")
}
}
}
func signIn(username: String) {
Amplify.Auth.signIn(username: username) { [weak self] result in
switch result {
case .success:
if case .confirmSignInWithCustomChallenge(_) = result.nextStep {
DispatchQueue.main.async {
self?.showConfirmationSignInView()
}
} else {
print("Sign in succeeded")
}
case .failure(let error):
print("Sign in failed \(error)")
}
}
}
func customChallenge(response: String) {
Amplify.Auth.confirmSignIn(challengeResponse: response) {[weak self] result in
switch result {
case .success:
DispatchQueue.main.async {
self?.getCurrentAuthUser()
}
print("Confirm sign in succeeded")
case .failure(let error):
print("Confirm sign in failed \(error)")
}
}
}
}
In the above code, I have copied this function from the AWS Amplify docs exactly:
func signIn(username: String) {
Amplify.Auth.signIn(username: username) { result in
switch result {
case .success:
if case .confirmSignInWithCustomChallenge(_) = result.nextStep {
// Ask the user to enter the custom challenge.
} else {
print("Sign in succeeded")
}
case .failure(let error):
print("Sign in failed \(error)")
}
}
}
But I am receiving this error:
Type of expression is ambiguous without more context
Within the switch statement. I really don't understand this because I have not changed this code, I have copied it directly from the documentation. Any help would be greatly appreciated.
Edit: Specifically, the error is appearing here:

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()
}
}
}

iOS websocket without handshake

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!).

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.

Resources