how to register listener to `NWConnectionGroup` for QUIC

I am trying to make http3 client with Network.framework on Apple platforms.

Codes that implement NWConnectionGroup.start with NWListener don't always work with warning below. I assume NWConnectionGroup.newConnectionHandler or NWListener.newConnectionHandler will be called to start connection from the server if it works.

nw_protocol_instance_add_new_flow [C1.1.1:2] No listener registered, cannot accept new flow
quic_stream_add_new_flow [C1.1.1:2] [-fde1594b83caa9b7] failed to create new stream for received stream id 3

so I tried:

  • create the NWListener -> not work
  • check whether NWConnectionGroup has a member to register or not NWListener -> not work (it doesn't have).
  • use NWConnection instead of NWConnectionGroup -> not work

Is my understanding correct?

How should I do to set or associate listener with NWConnection/Group for newConnectionHandler is called and to delete wanings?

What is the best practice in the case?

Sample codes are below.

Thanks in advance.

// http3 needs unidirectional stream by the server and client. 

// listener
private let _listener: NWListener

let option: NWProtocolQUIC.Options = .init(alpn:["h3"])
let param: NWParameters = .init(quic: option)

_listener = try! .init(using: param)

_listener.stateUpdateHandler = { state in
    print("listener state: \(state)")
}
_listener.newConnectionHandler = { newConnection in
    print("new connection added")
}
_listener.serviceRegistrationUpdateHandler = { registrationState in
    print("connection registrationstate")
}

// create connection
private let _group: NWConnectionGroup

let options: NWProtocolQUIC.Options = .init(alpn: ["h3"])
options.direction = .unidirectional
options.isDatagram = false
options.maxDatagramFrameSize = 65535

sec_protocol_options_set_verify_block(options.securityProtocolOptions, {(_: sec_protocol_metadata_t, _: sec_trust_t, completion: @escaping sec_protocol_verify_complete_t) in
    print("cert completion.")
    completion(true)
}, .global())

let params: NWParameters = .init(quic: options)

let group: NWMultiplexGroup = .init(
    to: .hostPort(host: NWEndpoint.Host("google.com"),
                  port: NWEndpoint.Port(String(443))!))

_group = .init(with: group, using: params)

_group.setReceiveHandler {message,content,isComplete in
    print("receive: \(message)")
}
_group.newConnectionHandler = {newConnection in
    print("newConnectionHandler: \(newConnection.state)")
}
_group.stateUpdateHandler = { state in
    print("state: \(state)")
}
_group.start(queue: .global())

_listener.start(queue: .global())

if let conn = _group.extract() {
    let data: Data = .init()
    let _ = _group.reinsert(connection: conn)
    conn.send(content: data, completion: .idempotent)
}
Answered by DTS Engineer in 833837022

I think the key point you’re missing here is newConnectionGroupHandler. If you working with QUIC, you accept connection groups rather than connections, and for that you need to set this callback rather than newConnectionHandler.

Share and Enjoy

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

I think the key point you’re missing here is newConnectionGroupHandler. If you working with QUIC, you accept connection groups rather than connections, and for that you need to set this callback rather than newConnectionHandler.

Share and Enjoy

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

@DTS Engineer

Thanks for your response.

I tried to implement NWListener.newConnectionGroupHandler, but not called...

So I guess another relates to problem.

I found weird log.

The operation couldn’t be completed. (Network.NWError error 50 - Network is down)

It seems that NWConnectionGroup.state turns .waiting with the NWError above just after .ready.

_group.stateUpdateHandler = { state in
    switch state {
        case .waiting(let e):
           // `waiting` is called 100ms after `.ready`
           // The operation couldn’t be completed. (Network.NWError error 50 - Network is down)
           print("\(e.localizedDescription)")
        case .ready:
           print("ready")
        default:
        break
    }
}

Of course, Network is not down. I can use everything on the Internet on debug device.

How should I do to fix? or Do you have any idea for NWListener.newConnectionGroupHandler is called?

Device Info

  • iOS Simulator/iOS18.2
  • Xcode 16.3

@DTS Engineer

forget to write.

logs below still displayed although I implemented NWListener.newConnectionGroupHandler

	nw_protocol_instance_add_new_flow [C1.1.1:2] No listener registered, cannot accept new flow
	quic_stream_add_new_flow [C1.1.1:2] [-fde1594b83caa9b7] failed to create new stream for received stream id 3

@DTS Engineer

I tried NWConnection instead of NWConnectionGroup, but still NO listener registered is displayed.

Any ideas to solve? I am completely cornered.

I will do to solve whatever it takes.

how to register listener to `NWConnectionGroup` for QUIC
 
 
Q