Client Server program using Network Framework

Hi,

I am trying to build and run a sample client server using NWFramework for UDP.

I have been successfully able to send and receive the packet using NWConnection and NWListner but the issue I am facing is regarding the member of NWListner which is newConnectionHandler which is used to handle receives inbound connections.

But UDP is connectionless protocol and I don't want my server to receive inbound connections as in UDP the server treat each packet as independent packet without maintaining any connection or connection info of client.

Please Find the refernce to the sample code below.

Can it be done without newConnectionHandler or is there any way to do communication on all the Apple Family without using BSD sockets as BSD sockets are not suported on watchOS

import Foundation
import Network

class Server {
    private var listener: NWListener?
    private let dispatchGroup = DispatchGroup()
    private var receivedPackets: [String] = []
  
    
    func startListening() {
        do {
            let parameters = NWParameters.udp
            let port = NWEndpoint.Port(integerLiteral: 12345)
           
            listener = try NWListener(using: parameters, on: port)
            
            listener?.stateUpdateHandler = { state in
                switch state {
                case .ready:
                    print("Server is ready to accept connections.")
                case .failed(let error):
                    print("Server failure, error: \(error.localizedDescription)")
                default:
                    break
                }
            }
            
            listener?.newConnectionHandler = { [weak self] newConnection in
                self?.handleNewConnection(newConnection)
            }
        
            listener?.start(queue: .main)
            //RunLoop.main.run()
            dispatchGroup.wait()
         
         
        } catch {
            print("Failed to start the server, error: \(error.localizedDescription)")
        }
        
       
    }
    
    private func handleNewConnection(_ connection: NWConnection) {
        connection.stateUpdateHandler = { state in
            switch state {
            case .ready:
                print("Client connected.")
                self.receiveData(on: connection)
            case .failed(let error):
                print("Connection failure, error: \(error.localizedDescription)")
            default:
                break
            }
        }
        
        connection.start(queue: .main)
    }
    
    private func receiveData(on connection: NWConnection) {
        connection.receiveMessage { (data, context, isComplete, error) in
            if let data = data, !data.isEmpty {
                let message = String(data: data, encoding: .utf8)
                print("Received message: \(message ?? "")")
                
                // Process the received message
                // Process the received message
                if let message = message {
                    self.receivedPackets.append(message)
                }
                
                if self.receivedPackets.count == 50 {
                    // All expected packets received
                    print("Received all packets:")
                    for packet in self.receivedPackets {
                        print(packet)
                    }
                    
                    // Close the connection after receiving all packets
                   // connection.cancel()
                } else {
                    // Continue receiving more packets
                    self.receiveData(on: connection)
                }
            }
        
            if let error = error {
                print("Failed to receive data, error: \(error.localizedDescription)")
            }
           
        }
    }

[I want] the server treat each packet as independent packet without maintaining any connection or connection info of client.

Can you explain more about the protocol you’re implementing here?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[Can you explain more about the protocol you’re implementing here?]

Apologies for delayed reply

Yes so I am trying to use UDP as a protocol for implementing server using NWListner where multiple packets will get received. But here is a part of code which gets invoked whenever packets from different client is send and creates a connection object.

listener?.newConnectionHandler = { [weak self] newConnection in self?.handleNewConnection(newConnection) }

But as far as I know UDP is connectionless protocol which does not maintain any connection with client.

But in Network framework for UDP also it is establishing a connection which I don't want.

I want to know if at all there is any way where I can implement server using Network Framework for UDP where I don't need to establish the connection as it possible with BSD sockets where for UDP if I block a thread on receiver it just receives a packet irrespective of establishing any connection with client.

But UDP is connectionless protocol and I don't want my server to receive inbound connections as in UDP the server treat each packet as independent packet without maintaining any connection or connection info of client.

and then:

But as far as I know UDP is connectionless protocol which does not maintain any connection with client. But in Network framework for UDP also it is establishing a connection which I don't want.

These two statements confuse me a bit because I am not sure what you are looking for? At any rate, if you have a server running with NWListener and two different clients are connecting to your listener they will have two different remote endpoints and so they will use two different connections with Network Framework.

// Sri Ganeshji : Sri Balaji : Sri Pitreshwarji : Sri Durgaji : Sri Venkateshwara

#if         TW_KERNEL_APPLE

#if         !TW_SUBKERNEL_APPLE


import Foundation
import Network


 @objc
class Server : NSObject {
    private var listener: NWListener?
    private var isRunning = false
    private var serverQueue: DispatchQueue?
 
    // Creating a private dispatch queue to handle the receiving of packets

    // Note : Global dispatch should be used instead of private dispatch queue invokes/creates personal thread
    // because the apps can consume too many threads is by creating too many private concurrent dispatch queues. 
    // Because each dispatch queue consumes thread resources, creating additional concurrent dispatch queues
    // exacerbates the thread consumption problem. Instead of creating private concurrent queues, 
    // submit tasks to one of the global concurrent dispatch queues. For serial tasks, set the target of your 
    // serial queue to one of the global concurrent queues.
    // That way, you can maintain the serialized behavior of the queue while minimizing the number of separate queues
    // creating threads.

      override init(){
          serverQueue =  DispatchQueue(label:"com.sampleapp.server")
          super.init()
      }

      //This function is invoked through objective c++ function 
     @objc
     func startListening() ->Void {
        print("swift function is invoked")
        do {
            let parameters = NWParameters.udp
            let port = NWEndpoint.Port(integerLiteral: 13249)

            //create a listner object which will be listnining on particular ports

            listener = try? NWListener(using: parameters, on: port)

            print("listner object is created")

            listener?.stateUpdateHandler = { state in
                switch state {
                case .ready:
                    print("Server is ready to accept connections.")
                case .failed(let error):
                    print("Server failure, error: \(error.localizedDescription)")
                default:
                    break
                }
            }
          
            //Register listner object to invoked whenever new connection is established

            listener?.newConnectionHandler = { [weak self] newConnection in
                self?.handleNewConnection(newConnection)
               
            }
            
        // Queues on which listner will executes its events and callbacks
        listener?.start(queue: serverQueue ?? DispatchQueue.global())

        } catch {
            print("Failed to start the server, error: \(error.localizedDescription)")
        }
        
       
    }
    
    private func handleNewConnection(_ connection: NWConnection) {
        connection.stateUpdateHandler = { state in
            switch state {
            case .ready:
                print("Client connected.")
                //invoking receiver to receive the message
                self.receiveData(on: connection)
            case .failed(let error):
                print("Connection failure, error: \(error.localizedDescription)")
            default:
                break
            }
        }

        connection.start(queue: serverQueue ?? DispatchQueue.global())
      
    }
    
    private func receiveData(on connection: NWConnection) {
        connection.receiveMessage { (data, context, isComplete, error) in
            if let data = data, !data.isEmpty {
                let message = String(data: data, encoding: .utf8)
                print("Received message: \(message ?? "")")
                
                // Process the received message
                
                // Echo back the message
                //self.sendData(data, on: connection)

                //Continue receiving the packets on the connection
                 self.receiveData(on: connection)
            }
            
            if let error = error {
                print("Failed to receive data, error: \(error.localizedDescription)")
            }
           
        }
    }
    
    private func sendData(_ data: Data, on connection: NWConnection) {
        connection.send(content: data, completion: .contentProcessed { error in
            if let error = error {
                print("Failed to send data, error: \(error.localizedDescription)")
            }
            
            // Close the connection after sending the response
            connection.cancel()
        })
    }
    @objc
    func runServer() {
        startListening()
        print("------------Here4-----------")
        while true {
            RunLoop.current.run()
        }
        }
      
     @objc
    func stopListening() {
        listener?.cancel()
        
    }
   

}


#endif

#endif      // TW_KERNEL_APPLE

As you can see above code I am implementing a server where multiple clients will send the packet and receivemessage will recieve the packet

But as you can see below code:

 private func receiveData(on connection: NWConnection) {
        connection.receiveMessage { (data, context, isComplete, error) in
            if let data = data, !data.isEmpty {
                let message = String(data: data, encoding: .utf8)
                print("Received message: \(message ?? "")")
                
                // Process the received message
                
                // Echo back the message
                //self.sendData(data, on: connection)
**
                //Continue receiving the packets on the connection
                 self.receiveData(on: connection)**
            }

receiveData is getting called recursively and if I am implementing a server and it is maintaining multiple connections then this recursive call will block the thread and slow it down as if there are 1000 of client is connected to it then 1000 different threads will be blocked due to this recursive call which should not be the appropriate behaviour for server.

The two options which I can think of

  1. Either there is way to implement server as connectionless
  2. Or There might be a way to invoke receivemessage when a new packet comes through some registering it on some event then identify the connection and receive the packet which will unblock the threads.

I am not able to find any solution regarding this please help me out if there is any possibilities from one of options mentioned above.

Thanks

Or There might be a way to invoke receivemessage when a new packet comes through some registering it on some event then identify the connection and receive the packet which will unblock the threads.

I think you'll need to do some version of this option; however you'll need to keep track of all of the different client connections that are being used so I would recommend using the Server as just a functional class that is setting up reads and writes on all of the different client connections. Then when a connection does have a message ready to read your receiveMessage call will be fired for a connection.

I would also park the main by using dispatchMain() instead of spinning the runloop. The reason for this is that most of the frameworks on the system use GCD now so it makes sense to also incorporate it into your project.

Out of curiosity, I suspect this is a macOS daemon? What type of server is this that it will have thousands of clients?

QUOTE Then when a connection does have a message ready to read your receiveMessage call will be fired for a connection. UNQUOTE

With refernce to the code shared by me earlier(in the above post) Is there a way that once I have processed the first receiveMessage I return the thread. Now when the next message comes from the same source will the recieveMessage again gets called. Basically I don't want to block any thread if there is nothing to process.

Thanks.

For those reading along at home, I’ll be helping HimanshuSingh98 in a different context.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Client Server program using Network Framework
 
 
Q