Hi. I'm trying to write simple async network client and server via AsyncSocket (by sample from manual). But client and server don't read any data. What I'm doing wrong? server.nim:
import asyncnet, asyncdispatch
var clients {.threadvar.}: seq[AsyncSocket]
proc sendToClients (msg: string) {.async.} =
for c in clients:
await c.send(msg & "\c\L")
proc processClient(client: AsyncSocket) {.async.} =
while true:
let msg = await client.recvLine()
if msg.len == 0: break
echo "-> " & msg
asyncCheck sendToClients(msg)
proc serve() {.async.} =
clients = @[]
var server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(Port(12345))
server.listen()
while true:
let client = await server.accept()
echo "client accepted"
clients.add client
asyncCheck processClient(client)
proc sendServerMsg () {.async.} =
while true:
var msg = stdin.readLine()
echo "<- " & msg
asyncCheck sendToClients(msg)
asyncCheck serve()
asyncCheck sendServerMsg()
runForever()
client.nim
import asyncnet, asyncdispatch
var client {.threadvar.}: AsyncSocket
client = newAsyncSocket()
proc readServer() {.async.} =
while client != nil:
let msg = await client.recvLine()
if msg.len == 0: break
echo "-> " & msg
proc sendClientMsg () {.async.} =
while client != nil:
var msg = stdin.readLine()
if msg == "exit":
client.close()
client = nil
else:
echo "<- " & msg
await client.send(msg & "\c\L")
asyncCheck client.connect("127.0.0.1", Port(12345))
asyncCheck readServer()
asyncCheck sendClientMsg()
runForever()
well, I changed code. server.nim:
import asyncnet, asyncdispatch, nativesockets
var clients {.threadvar.}: seq[AsyncSocket]
proc processClient(client: AsyncSocket) {.async.} =
while true:
let msg = await client.recvLine()
for c in clients:
let (address, port) = c.getFd.getPeerAddr(Domain.AF_INET)
echo "send to: " & address & ":" & $port
await c.send(msg & "\c\L")
proc serve() {.async.} =
clients = @[]
var server = newAsyncSocket()
server.bindAddr(Port(12345))
server.listen()
while true:
let client = await server.accept()
let (address, port) = client.getFd.getPeerAddr(Domain.AF_INET)
echo "client connected: " & address & ":" & $port
clients.add client
asyncCheck processClient(client)
asyncCheck serve()
runForever()
client.nim
import asyncnet, asyncdispatch
proc readServer(c: AsyncSocket) {.async.} =
while true:
let msg = await c.recvLine()
echo "-> " & msg
proc sendClientMsg (c: AsyncSocket) {.async.} =
while true:
let msg = stdin.readLine()
echo "<- " & msg
await c.send(msg & "\c\L")
var client {.threadvar.}: AsyncSocket
client = newAsyncSocket()
asyncCheck client.connect("127.0.0.1", Port(12345))
asyncCheck readServer(client)
asyncCheck sendClientMsg(client)
runForever()
server_output
Running executable
client connected: 127.0.0.1:36138
send to: 127.0.0.1:36138
but client don't receive any data :-| I don't understand why line msg = await c.recvLine()
don't receive data from serverthis code works fine:
proc sendClientMsg (c: AsyncSocket) {.async.} =
#while true:
let msg = stdin.readLine()
echo "<- " & msg
await c.send(msg & "\c\L")
why code with loop doesn't work? how to organize parallel/async read/write?why code with loop doesn't work?
While I don't know the exact reason too, I think you need separate thread which handle the readline like this https://github.com/mashingan/nim-etc/blob/master/cond_loop.nim#L58-L67
something wrong when I tried to read data from stdin! this code works fine: client.nim
proc readServer(c: AsyncSocket) {.async.} =
while true:
let msg = await c.recvLine()
echo "-> " & msg
proc sendClientMsg (c: AsyncSocket) {.async.} =
while true:
waitFor sleepAsync(5000)
let msg = "blah..."
echo "<- " & msg
but here function readServer doesn't read anything from server! (but server already got data from client): client.nim
proc readServer(c: AsyncSocket) {.async.} =
while true:
let msg = await c.recvLine()
echo "-> " & msg
proc sendClientMsg (c: AsyncSocket) {.async.} =
while true:
let msg = stdin.readLine()
echo "<- " & msg
ok, problem solved. just need to wait before sending data. I don't know why:
proc sendClientMsg (c: AsyncSocket) {.async.} =
while true:
waitFor sleepAsync(1000)
let msg = stdin.readLine()
echo "<- " & msg
await c.send(msg & "\c\L")
Probably because stdin.readLine() is a blocking call, the event loop is blocked and other tasks can not be executed.
wait well in a separate thread, or such a library will be useful asynctools/asyncpty
r3d9u11
a few unsorted things:
https://nim-lang.org/docs/asyncnet.html#acceptAddr,AsyncSocket
It might be still the case (it was a few month ago when i tried it) that reading from stdin is not working in an async manner! So you could try to read from stdin in a thread:
https://github.com/dom96/nim-in-action-code/blob/master/Chapter3/ChatApp/src/client.nim#L38
Indeed, I was going to mention the ChatApp example from Nim in Action. Pretty sure it implements precisely what you're trying to implement.
See both server and client here: https://github.com/dom96/nim-in-action-code/blob/master/Chapter3/ChatApp
In regards to some other things written here:
I'm "pretty sure" that I observed in Windows that asyncdispatch would use multiple different threads to run the code.
It doesn't, and it cannot do that unless you pass --threads:on. This is assuming that Windows doesn't do something weird internally, like create threads to handle IO (which wouldn't surprise me), but Nim's asyncdispatch loop is definitely single threaded.
@r3d9u11 The issue with your code is, as others mentioned, that you're calling stdin.readLine which is a blocking call. If you're performing IO and it doesn't return a Future then assume it is blocking.
The ChatApp we linked above shows how to handle this by creating a new thread to read from stdin without blocking your program.