summary refs log tree commit diff stats
path: root/tests/niminaction/Chapter3/ChatApp/src/server.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tests/niminaction/Chapter3/ChatApp/src/server.nim')
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/server.nim84
1 files changed, 84 insertions, 0 deletions
diff --git a/tests/niminaction/Chapter3/ChatApp/src/server.nim b/tests/niminaction/Chapter3/ChatApp/src/server.nim
new file mode 100644
index 000000000..8c572aeb0
--- /dev/null
+++ b/tests/niminaction/Chapter3/ChatApp/src/server.nim
@@ -0,0 +1,84 @@
+import asyncdispatch, asyncnet
+
+type
+  Client = ref object
+    socket: AsyncSocket
+    netAddr: string
+    id: int
+    connected: bool
+
+  Server = ref object
+    socket: AsyncSocket
+    clients: seq[Client]
+
+proc newServer(): Server =
+  ## Constructor for creating a new ``Server``.
+  Server(socket: newAsyncSocket(), clients: @[])
+
+proc `$`(client: Client): string =
+  ## Converts a ``Client``'s information into a string.
+  $client.id & "(" & client.netAddr & ")"
+
+proc processMessages(server: Server, client: Client) {.async.} =
+  ## Loops while ``client`` is connected to this server, and checks
+  ## whether as message has been received from ``client``.
+  while true:
+    # Pause execution of this procedure until a line of data is received from
+    # ``client``.
+    let line = await client.socket.recvLine()
+
+    # The ``recvLine`` procedure returns ``""`` (i.e. a string of length 0)
+    # when ``client`` has disconnected.
+    if line.len == 0:
+      echo(client, " disconnected!")
+      client.connected = false
+      # When a socket disconnects it must be closed.
+      client.socket.close()
+      return
+
+    # Display the message that was sent by the client.
+    echo(client, " sent: ", line)
+
+    # Send the message to other clients.
+    for c in server.clients:
+      # Don't send it to the client that sent this or to a client that is
+      # disconnected.
+      if c.id != client.id and c.connected:
+        await c.socket.send(line & "\c\l")
+
+proc loop(server: Server, port = 7687) {.async.} =
+  ## Loops forever and checks for new connections.
+
+  # Bind the port number specified by ``port``.
+  server.socket.bindAddr(port.Port)
+  # Ready the server socket for new connections.
+  server.socket.listen()
+  echo("Listening on localhost:", port)
+
+  while true:
+    # Pause execution of this procedure until a new connection is accepted.
+    let (netAddr, clientSocket) = await server.socket.acceptAddr()
+    echo("Accepted connection from ", netAddr)
+
+    # Create a new instance of Client.
+    let client = Client(
+      socket: clientSocket,
+      netAddr: netAddr,
+      id: server.clients.len,
+      connected: true
+    )
+    # Add this new instance to the server's list of clients.
+    server.clients.add(client)
+    # Run the ``processMessages`` procedure asynchronously in the background,
+    # this procedure will continuously check for new messages from the client.
+    asyncCheck processMessages(server, client)
+
+# Check whether this module has been imported as a dependency to another
+# module, or whether this module is the main module.
+when isMainModule:
+  # Initialise a new server.
+  var server = newServer()
+  echo("Server initialised!")
+  # Execute the ``loop`` procedure. The ``waitFor`` procedure will run the
+  # asyncdispatch event loop until the ``loop`` procedure finishes executing.
+  waitFor loop(server)
\ No newline at end of file