How to Reply to a Message Received by Multicast Receiver and Extract Connection for Communication

Hello everyone,

I’m currently working on a Swift project using the Network framework to create a multicast-based communication system. Specifically, I’m implementing both a multicast receiver and a sender that join the same multicast group for communication. However, I’ve run into some challenges with the connection management, replying to multicast messages, and handling state updates for both connections and connection groups.

Below is a breakdown of my setup and the specific issues I’ve encountered.

I have two main parts in the implementation: the multicast receiver and the multicast sender. The goal is for the receiver to join the multicast group, receive messages from the sender, and send a reply back to the sender using a direct connection.

Multicast Receiver Code:

import Network
import Foundation

func setupMulticastGroup() -> NWConnectionGroup? {
    let multicastEndpoint1 = NWEndpoint.hostPort(host: NWEndpoint.Host("224.0.0.1"), port: NWEndpoint.Port(rawValue: 45000)!)
    let multicastParameters = NWParameters.udp
    multicastParameters.multipathServiceType = .aggregate

    do {
        let multicastGroup = try NWMulticastGroup(for: [multicastEndpoint1], from: nil, disableUnicast: false)
        let multicastConnections = NWConnectionGroup(with: multicastGroup, using: multicastParameters)
        multicastConnections.stateUpdateHandler = InternalConnectionStateUpdateHandler
        multicastConnections.setReceiveHandler(maximumMessageSize: 16384, rejectOversizedMessages: false, handler: receiveHandler)
        multicastConnections.newConnectionHandler = newConnectionHandler
        multicastConnections.start(queue: .global())
        
        return multicastConnections
    } catch {
        return nil
    }
}

func receiveHandler(message: NWConnectionGroup.Message, content: Data?, isComplete: Bool) {
    print("Received message from \(String(describing: message.remoteEndpoint))")

    if let content = content, let messageString = String(data: content, encoding: .utf8) {
        print("Received Message: \(messageString)")
    }

    let remoteEndpoint = message.remoteEndpoint
    message.reply(content: "Multicast group on 144 machine ACK from recv handler".data(using: .utf8))

    if let connection = multicastConnections?.extract(connectionTo: remoteEndpoint) {
        connection.stateUpdateHandler = InternalConnectionRecvStateUpdateHandler
        connection.start(queue: .global())
        connection.send(content: "Multicast group on 144 machine ACK from recv handler".data(using: .utf8), completion: NWConnection.SendCompletion.contentProcessed({ error in
            print("Error code: \(error?.errorCode ?? 0)")
            print("Ack sent to \(connection.endpoint)")
        }))
    }
}

func newConnectionHandler(connection: NWConnection) {
    connection.start(queue: .global())
    connection.send(content: "Multicast group on 144 machine ACK".data(using: .utf8), completion: NWConnection.SendCompletion.contentProcessed({ error in
        print("Error code: \(error?.errorCode ?? 0)")
        print("Ack sent to \(connection.endpoint)")
    }))
}

func InternalConnectionRecvStateUpdateHandler(_ pState: NWConnection.State) {
    switch pState {
    case .setup:
        NSLog("The connection has been initialized but not started")
    case .preparing:
        NSLog("The connection is preparing")
    case .waiting(let error):
        NSLog("The connection is waiting for a network path change. Error: \(error)")
    case .ready:
        NSLog("The connection is established and ready to send and receive data.")
    case .failed(let error):
        NSLog("The connection has disconnected or encountered an error. Error: \(error)")
    case .cancelled:
        NSLog("The connection has been canceled.")
    default:
        NSLog("Unknown NWConnection.State.")
    }
}

func InternalConnectionStateUpdateHandler(_ pState: NWConnectionGroup.State) {
    switch pState {
    case .setup:
        NSLog("The connection has been initialized but not started")
    case .waiting(let error):
        NSLog("The connection is waiting for a network path change. Error: \(error)")
    case .ready:
        NSLog("The connection is established and ready to send and receive data.")
    case .failed(let error):
        NSLog("The connection has disconnected or encountered an error. Error: \(error)")
    case .cancelled:
        NSLog("The connection has been canceled.")
    default:
        NSLog("Unknown NWConnection.State.")
    }
}
let multicastConnections = setupMulticastGroup()
RunLoop.main.run()

Multicast Sender Code:

import Foundation
import Network
func setupConnection() -> NWConnection {
    let params = NWParameters.udp
    params.allowLocalEndpointReuse = true
    return NWConnection(to: NWEndpoint.hostPort(host: NWEndpoint.Host("224.0.0.1"), port: NWEndpoint.Port(rawValue: 45000)!), using: params)
}

func sendData(using connection: NWConnection, data: Data) {
    connection.send(content: data, completion: .contentProcessed { nwError in
        if let error = nwError {
            print("Failed to send message with error: \(error)")
        } else {
            print("Message sent successfully")
        }
    })
}
func setupReceiveHandler(for connection: NWConnection) {
    connection.receive(minimumIncompleteLength: 1, maximumLength: 65000) { content, contentContext, isComplete, error in
        print("Received data:")
        print(content as Any)
        print(contentContext as Any)
        print(error as Any)
     
        setupReceiveHandler(for: connection)
    }
}

let connectionSender = setupConnection()
connectionSender.stateUpdateHandler = internalConnectionStateUpdateHandler
connectionSender.start(queue: .global())

let sendingData = "Hello, this is a multicast message from the process on mac machine 144".data(using: .utf8)!

sendData(using: connectionSender, data: sendingData)
setupReceiveHandler(for: connectionSender)
RunLoop.main.run()

Issues Encountered:

  1. Error Code 0 Even When Connection Refused:

On the receiver side, I encountered this log:

nw_socket_get_input_frames [C1.1.1:1] recvmsg(fd 8, 9216 bytes) [61: Connection refused]
Error code: 0
Ack sent to 10.20.16.144:62707

Questions:

  1. how do I reply to the message if above usage pattern is wrong?
  2. how do I get a NWConnection from the received message to create a separate connection for communication with the sender.

Any insights or suggestions are appretiated :slight_smile:

Posted the same on developer forums also, here's the link: How to Reply to a Message Received… | Apple Developer Forums