Reading a string from the terminal is easy.

Receiving a UDP packet is easy and there are ready examples.

Sending a UDP packet is really easy.

So SURELY doing all three at once is also easy.... I just spent many days on 
working out different ways to do that.

Fortunately since UDP is connectionless, it turns out a simple pair of threads 
can do it; but this is very non-obvious at first. At least it wasn't to me 
anyway.

I also got a version to sort of work with many layers asynchronous blah blah 
blah working. It is about 300 lines line and isn't even slightly readable.

So, for any future UDP client and server writers, here is my very simplified 
bidirectionial UDP client. Feel free to use as a simple starting point:
    
    
    import net, threadpool, strutils
    
    const MAX_PACKET_SIZE = 508
    
    const clientAddr = "127.0.0.1"
    const clientPort = 8800
    
    const serverAddr = "127.0.0.1"
    const serverPort = 9900
    
    
    proc sendMessage(socket: Socket, message: string) =
      socket.sendTo(serverAddr, Port(serverPort),  message)
      echo "sent: ", message
    
    proc recvMessage(message: string) =
      echo "received: ", message
    
    
    proc onPacket(socket: Socket): string =
      var incomingMessage = ""
      var srcAddress = ""
      var srcPort = Port(0)
      
      let msgLen = socket.recvFrom(incomingMessage, MAX_PACKET_SIZE, 
srcAddress, srcPort)
      echo "packet of size $1 seen from $2:$3".format(msgLen, srcAddress, 
srcPort)
      return incomingMessage
    
    
    let socket: Socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
    socket.bindAddr(Port(clientPort), clientAddr)
    
    var messageFlowVar = spawn stdin.readLine()
    var packetFlowVar = spawn onPacket(socket)
    
    while true:
      if messageFlowVar.isReady():
        sendMessage(socket, ^messageFlowVar)
        messageFlowVar = spawn stdin.readLine()
      if packetFlowVar.isReady():
        recvMessage(^packetFlowVar)
        packetFlowVar = spawn onPacket(socket)
    
    
    Run

Feel free to convert the `if {}.isReady()` into while loops and then slip in a 
`os.sleep(50)` to make it a tad easier on the CPU.

Next, I'll be trying to avoid pre-assigning the client address and port; but 
instead capturing the OS-assigned outbound src port on first `sendTo` so that 
the other server can consistently see the same client source port over time. If 
nothing else, that will help with tracking firewall NAT passage. If anyone 
knows an clean and easy way to do that, let me know. 

Reply via email to