Echo server: Difference between revisions

Line 48:
end loop;
end Echo_Server;</lang>
 
Multi-threaded, multiple clients served. On OS X 10.10.5 with gcc 4.9.1, serves a maximum of about 2000 threads (communication tasks) per process.
<lang Ada>
with Ada.Text_IO;
with Ada.IO_Exceptions;
with GNAT.Sockets;
procedure Echo_Server_Multi is
-- Multiple socket connections example based on Rosetta Code Echo_Server.
 
Tasks_To_Create : constant := 3; -- simultaneous socket connections.
 
-------------------------------------------------------------------------------
-- Use stack to pop the next free task index. When a task finishes its
-- asynchronous (no rendezvous) phase, it pushes the index back on the stack.
type Integer_List is array (1..Tasks_To_Create) of integer;
subtype Counter is integer range 0 .. Tasks_To_Create;
subtype Index is integer range 1 .. Tasks_To_Create;
protected type Info is
procedure Push_Stack (Return_Task_Index : in integer);
procedure Initialize_Stack;
entry Pop_Stack (Get_Task_Index : out integer);
private
Task_Stack : Integer_List; -- Stack of free-to-use tasks.
Stack_Pointer: Counter := 0;
Task_Index : Index;
end Info;
 
protected body Info is
procedure Push_Stack (Return_Task_Index : in integer) is
begin -- Performed by tasks that were popped, so won't overflow.
Stack_Pointer := Stack_Pointer + 1;
Task_Stack(Stack_Pointer) := Return_Task_Index;
end;
 
entry Pop_Stack (Get_Task_Index : out integer) when Stack_Pointer /= 0 is
begin -- guarded against underflow.
Get_Task_Index := Task_Stack(Stack_Pointer);
Stack_Pointer := Stack_Pointer - 1;
end;
 
procedure Initialize_Stack is
begin
for I in Task_Stack'Range loop
Push_Stack (I);
end loop;
end;
end Info;
 
Task_Info : Info;
-------------------------------------------------------------------------------
task type SocketTask is
-- Rendezvous the setup, which sets the parameters for entry Echo.
entry Setup (Connection : GNAT.Sockets.Socket_Type;
Client : GNAT.Sockets.Sock_Addr_Type;
Channel : GNAT.Sockets.Stream_Access;
Task_Index : integer);
-- Echo accepts the asynchronous phase, i.e. no rendezvous. When the
-- communication is over, push the task number back on the stack.
entry Echo;
end SocketTask;
 
task body SocketTask is
my_Connection : GNAT.Sockets.Socket_Type;
my_Client : GNAT.Sockets.Sock_Addr_Type;
my_Channel : GNAT.Sockets.Stream_Access;
my_Index : integer;
begin
loop -- Infinitely reusable
accept Setup (Connection : GNAT.Sockets.Socket_Type;
Client : GNAT.Sockets.Sock_Addr_Type;
Channel : GNAT.Sockets.Stream_Access;
Task_Index : integer) do
-- Store parameters and mark task busy.
my_Connection := Connection;
my_Client := Client;
my_Channel := Channel;
my_Index := Task_Index;
end;
accept Echo; -- Do the echo communications.
begin
Ada.Text_IO.Put_Line ("Task " & integer'image(my_Index));
loop
Character'Output (my_Channel, Character'Input(my_Channel));
end loop;
exception
when Ada.IO_Exceptions.End_Error =>
Ada.Text_IO.Put_Line ("Echo " & integer'image(my_Index) & " end");
when others =>
Ada.Text_IO.Put_Line ("Echo " & integer'image(my_Index) & " err");
end;
GNAT.Sockets.Close_Socket (my_Connection);
Task_Info.Push_Stack (my_Index); -- Return to stack of unused tasks.
end loop;
end SocketTask;
 
-------------------------------------------------------------------------------
-- Setup the socket receiver, initialize the task stack, and then loop,
-- blocking on Accept_Socket, using Get_Task for the next free task from the
-- stack, waiting if necessary.
task type SocketServer (my_Port : GNAT.Sockets.Port_Type) is
entry Listen;
end SocketServer;
 
task body SocketServer is
Receiver : GNAT.Sockets.Socket_Type;
Connection : GNAT.Sockets.Socket_Type;
Client : GNAT.Sockets.Sock_Addr_Type;
Channel : GNAT.Sockets.Stream_Access;
Worker : array (1..Tasks_To_Create) of SocketTask;
Use_Task : Index;
 
begin
accept Listen;
GNAT.Sockets.Create_Socket (Socket => Receiver);
GNAT.Sockets.Set_Socket_Option
(Socket => Receiver,
Option => (Name => GNAT.Sockets.Reuse_Address, Enabled => True));
GNAT.Sockets.Bind_Socket
(Socket => Receiver,
Address => (Family => GNAT.Sockets.Family_Inet,
Addr => GNAT.Sockets.Inet_Addr ("127.0.0.1"),
Port => my_Port));
GNAT.Sockets.Listen_Socket (Socket => Receiver);
Task_Info.Initialize_Stack;
Find: loop -- Block for connection and take next free task.
GNAT.Sockets.Accept_Socket
(Server => Receiver,
Socket => Connection,
Address => Client);
Ada.Text_IO.Put_Line ("Connect " & GNAT.Sockets.Image(Client));
Channel := GNAT.Sockets.Stream (Connection);
Task_Info.Pop_Stack(Use_Task); -- Protected guard waits if full house.
-- Setup the socket in this task in rendezvous.
Worker(Use_Task).Setup(Connection,Client, Channel,Use_Task);
-- Run the asynchronous task for the socket communications.
Worker(Use_Task).Echo; -- Start echo loop.
end loop Find;
end SocketServer;
 
Echo_Server : SocketServer(my_Port => 12321);
 
-------------------------------------------------------------------------------
begin
Echo_Server.Listen;
end Echo_Server_Multi;
</lang>
 
=={{header|AutoHotkey}}==
Anonymous user