Dmitry
Dmitry

Reputation: 2168

swift NIO : udp client to send and receive without closing channel

I want to have a udp socket channel to send and receive data. Here is my implementation :

import NIOCore
import NIOPosix

public class UdpClient: @unchecked Sendable {
    private var udpChannel: NIOAsyncChannel<AddressedEnvelope<ByteBuffer>, AddressedEnvelope<ByteBuffer>>?

    init(host: String, port: Int) async {
        udpChannel = try? await DatagramBootstrap(group: NIOSingletons.posixEventLoopGroup)
            .bind(host: host, port: port)
            .flatMapThrowing { channel in
                return try NIOAsyncChannel(
                    wrappingChannelSynchronously: channel,
                    configuration: NIOAsyncChannel.Configuration(
                        inboundType: AddressedEnvelope<ByteBuffer>.self,
                        outboundType: AddressedEnvelope<ByteBuffer>.self
                    )
                )
            }
        .get()
    }
    
    /* Wildcard init */
    convenience init() async {
        await self.init(host: "0.0.0.0", port: 0)
    }

    public func send(message: String, sendToAddress: String, sendToPort: Int) async throws {
        try await udpChannel?.outbound.write(
            AddressedEnvelope(
                remoteAddress: SocketAddress(ipAddress: sendToAddress, port: sendToPort),
                data: ByteBuffer(string: message)
            )
        )
    }
    
    public func read() async throws -> ByteBuffer? {
        guard let channel = udpChannel else { return nil }
        
        for try await var packet in channel.inbound {
            return packet.data
        }
        
        return nil
    }
}

And here is my use-case :

let clientTask = Task.detached {
    let udpClient = await UdpClient()

    for iter in 1..<5 {
        print("Iteration \(iter): send a packet")
        try? await udpClient.send(message: "hello", sendToAddress: "0.0.0.0", sendToPort: 20_000)
        try? await Task.sleep(for: .seconds(1))
    }

    print("Udp send task finished")
}

print("Press enter to cancel task")
 _ = readLine()
clientTask.cancel()
_ = readLine()
print("Ciao")

Several quetions still to clarify :

  1. inbound and outbound properties are marked as deprecated and it is suggested to use executeThenClose. But I don't want to close the channel after send or receive - what am I supposed to do ?

  2. I don't like the way read is implemented : I just want to read only 1 packet a time, so how should I get a single packet from NIOAsyncChannelInboundStream ?

Upvotes: 0

Views: 33

Answers (0)

Related Questions