Task: Chat server








VB: Imports System.Net.Sockets
Imports System.Text
Imports System.Threading

Module Module1

    Class State
        Private ReadOnly client As TcpClient
        Private ReadOnly sb As New StringBuilder

        Public Sub New(name As String, client As TcpClient)
            Me.Name = name
            Me.client = client
        End Sub

        Public ReadOnly Property Name As String

        Public Sub Send(text As String)
            Dim bytes = Encoding.ASCII.GetBytes(String.Format("{0}" & vbCrLf, text))
            client.GetStream().Write(bytes, 0, bytes.Length)
        End Sub
    End Class

    ReadOnly connections As New Dictionary(Of Integer, State)
    Dim listen As TcpListener
    Dim serverThread As Thread

    Sub Main()
        listen = New TcpListener(Net.IPAddress.Parse("127.0.0.1"), 4004)
        serverThread = New Thread(New ThreadStart(AddressOf DoListen))
        serverThread.Start()
    End Sub

    Private Sub DoListen()
        listen.Start()
        Console.WriteLine("Server: Started server")

        Do
            Console.Write("Server: Waiting...")
            Dim client = listen.AcceptTcpClient()
            Console.WriteLine(" Connected")

            
            Dim clientThread As New Thread(New ParameterizedThreadStart(AddressOf DoClient))

            clientThread.Start(client)
        Loop
    End Sub

    Private Sub DoClient(client As TcpClient)
        Console.WriteLine("Client (Thread: {0}): Connected!", Thread.CurrentThread.ManagedThreadId)
        Dim bytes = Encoding.ASCII.GetBytes("Enter name: ")
        client.GetStream().Write(bytes, 0, bytes.Length)

        Dim done As Boolean
        Dim name As String
        Do
            If Not client.Connected Then
                Console.WriteLine("Client (Thread: {0}): Terminated!", Thread.CurrentThread.ManagedThreadId)
                client.Close()
                Thread.CurrentThread.Abort() 
            End If

            name = Receive(client)
            done = True

            For Each cl In connections
                Dim state = cl.Value
                If state.Name = name Then
                    bytes = Encoding.ASCII.GetBytes("Name already registered. Please enter your name: ")
                    client.GetStream().Write(bytes, 0, bytes.Length)
                    done = False
                End If
            Next
        Loop While Not done

        connections.Add(Thread.CurrentThread.ManagedThreadId, New State(name, client))
        Console.WriteLine(vbTab & "Total connections: {0}", connections.Count)
        Broadcast(String.Format("+++ {0} arrived +++", name))

        Do
            Dim text = Receive(client)
            If text = "/quit" Then
                Broadcast(String.Format("Connection from {0} closed.", name))
                connections.Remove(Thread.CurrentThread.ManagedThreadId)
                Console.WriteLine(vbTab & "Total connections: {0}", connections.Count)
                Exit Do
            End If

            If Not client.Connected Then
                Exit Do
            End If

            Broadcast(String.Format("{0}> {1}", name, text))
        Loop

        Console.WriteLine("Client (Thread: {0}): Terminated!", Thread.CurrentThread.ManagedThreadId)
        client.Close()
        Thread.CurrentThread.Abort()
    End Sub

    Private Function Receive(client As TcpClient) As String
        Dim sb As New StringBuilder
        Do
            If client.Available > 0 Then
                While client.Available > 0
                    Dim ch = Chr(client.GetStream.ReadByte())
                    If ch = vbCr Then
                        
                        Continue While
                    End If
                    If ch = vbLf Then
                        Return sb.ToString()
                    End If
                    sb.Append(ch)
                End While

                
                Thread.Sleep(100)
            End If
        Loop
    End Function

    Private Sub Broadcast(text As String)
        Console.WriteLine(text)
        For Each client In connections
            If client.Key <> Thread.CurrentThread.ManagedThreadId Then
                Dim state = client.Value
                state.Send(text)
            End If
        Next
    End Sub

End Module




Ada: with Ada.Containers.Vectors;
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Exceptions;   use Ada.Exceptions;
with Ada.Text_IO;      use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Sockets;          use Sockets;

procedure Chat_Server is

   package Client_Vectors is new Ada.Containers.Vectors
     (Element_Type => Socket_FD, Index_Type => Positive);
   All_Clients : Client_Vectors.Vector;

   procedure Write (S : String) is
      procedure Output (Position : Client_Vectors.Cursor) is
         Sock : Socket_FD := Client_Vectors.Element (Position);
      begin
         Put_Line (Sock, S);
      end Output;
   begin
      All_Clients.Iterate (Output'Access);
   end Write;

   task type Client_Task is
      entry Start (FD : Socket_FD);
   end Client_Task;

   task body Client_Task is
      Sock    : Socket_FD;
      Sock_ID : Positive;
      Name    : Unbounded_String;
   begin
      select
         accept Start (FD : Socket_FD) do
            Sock := FD;
         end Start;
      or
         terminate;
      end select;

      while Name = Null_Unbounded_String loop
         Put (Sock, "Enter Name:");
         Name := To_Unbounded_String (Get_Line (Sock));
      end loop;
      Write (To_String (Name) & " joined.");
      All_Clients.Append (Sock);
      Sock_ID := All_Clients.Find_Index (Sock);
      loop
         declare
            Input : String := Get_Line (Sock);
         begin
            Write (To_String (Name) & ": " & Input);
         end;
      end loop;
   exception
      when Connection_Closed =>
         Put_Line ("Connection closed");
         Shutdown (Sock, Both);
         All_Clients.Delete (Sock_ID);
         Write (To_String (Name) & " left.");
   end Client_Task;

   Accepting_Socket : Socket_FD;
   Incoming_Socket  : Socket_FD;

   type Client_Access is access Client_Task;
   Dummy : Client_Access;
begin
   if Argument_Count /= 1 then
      Raise_Exception (Constraint_Error'Identity,
                       "Usage: " & Command_Name & " port");
   end if;
   Socket (Accepting_Socket, PF_INET, SOCK_STREAM);
   Setsockopt (Accepting_Socket, SOL_SOCKET, SO_REUSEADDR, 1);
   Bind (Accepting_Socket, Positive'Value (Argument (1)));
   Listen (Accepting_Socket);
   loop
      Put_Line ("Waiting for new connection");
      Accept_Socket (Accepting_Socket, Incoming_Socket);
      Put_Line ("New connection acknowledged");

      Dummy := new Client_Task;
      Dummy.Start (Incoming_Socket);
   end loop;
end Chat_Server;