4

I am trying to make a simple echo UDP server that sends back all incoming datagrams prefixed with a UTF8 string.

In my attempts to reach this goal, I succeeded in sending back the incoming data, but when I try to prefix this data with the string: "You sent: ", I get an error writeDataUnsupported

This is my code:

I made a ChannelInboundHandler called Echo all it does is: For each incoming datagram, it sends the string "You sent: " and then the data of the incoming datagram.

final class Echo: ChannelInboundHandler {
    typealias   InboundIn = ByteBuffer
    typealias OutboundOut = ByteBuffer

    var wroteResponse = false
    static let response = "You sent: ".data(using: .utf8)!

    func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
        if !wroteResponse {
            var buffer = ctx.channel.allocator.buffer(capacity: Echo.response.count)
            buffer.write(bytes: Echo.response)
            ctx.write(self.wrapOutboundOut(buffer), promise: nil)
            wroteResponse = true
        }
        ctx.write(data, promise: nil)
    }

    func channelReadComplete(ctx: ChannelHandlerContext) {
        ctx.flush()
        wroteResponse = false
    }
}

Then I made a single threaded event loop group and assigned a datagram bootsrap to it. Then I bound the bootstrap to port 4065.

let  = MultiThreadedEventLoopGroup(numThreads: 1)
let bootstrap = DatagramBootstrap(group: )
    .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
    .channelInitializer { $0.pipeline.add(handler: Echo()) }
defer {
    try! .syncShutdownGracefully()
}


try bootstrap
    .bind(host: "127.0.0.1", port: 4065)
    .wait()
    .closeFuture
    .wait()

Why do I always get this writeDataUnsupported while trying to send the string: "You sent: "?

Caleb Kleveter
  • 11,170
  • 8
  • 62
  • 92
Damiaan Dufaux
  • 4,427
  • 1
  • 22
  • 33

2 Answers2

2

For DatagramChannel you need to wrap your ByteBuffer into an AddressEnvelope. Which also means your ChannelInboundHandler should operate on AddressedEnvelope<ByteBuffer>.

Norman Maurer
  • 23,104
  • 2
  • 33
  • 31
2

To make the ChannelInboundHandler operate on AddressedEnvelope<ByteBuffer>, as Norman Maurer suggests, you can rewrite Echo so it looks more like:

final class Echo: ChannelInboundHandler {
    typealias  InboundIn  = AddressedEnvelope<ByteBuffer>
    typealias OutboundOut = AddressedEnvelope<ByteBuffer>

    static let response = "You sent: ".data(using: .utf8)!

    func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
        var incomingEnvelope = unwrapInboundIn(data)
        var buffer = ctx.channel.allocator.buffer(capacity: Echo.response.count + incomingEnvelope.data.readableBytes)
        buffer.write(bytes: Echo.response)
        buffer.write(buffer: &incomingEnvelope.data)

        let envelope = AddressedEnvelope(remoteAddress: incomingEnvelope.remoteAddress, data: buffer)
        ctx.write(wrapOutboundOut(envelope), promise: nil)
    }

    func channelReadComplete(ctx: ChannelHandlerContext) {
        ctx.flush()
    }
}
Damiaan Dufaux
  • 4,427
  • 1
  • 22
  • 33