Echo server
You are encouraged to solve this task according to the task description, using any language you may know.
Create a network service that sits on TCP port 12321, which accepts connections on that port, and which echoes complete lines (using a carriage-return/line-feed sequence as line separator) back to clients. No error handling is required. For the purposes of testing, it is only necessary to support connections from localhost (127.0.0.1 or perhaps ::1). Logging of connection information to standard output is recommended.
The implementation must be able to handle simultaneous connections from multiple clients. A multi-threaded or multi-process solution may be used. Each connection must be able to echo more than a single line.
The implementation must not stop responding to other clients if one client sends a partial line or stops reading responses.
Ada
single-threaded, one client served at a time.
<lang Ada>with Ada.Text_IO; with Ada.IO_Exceptions; with GNAT.Sockets; procedure Echo_Server is
Receiver : GNAT.Sockets.Socket_Type; Connection : GNAT.Sockets.Socket_Type; Client : GNAT.Sockets.Sock_Addr_Type; Channel : GNAT.Sockets.Stream_Access;
begin
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 => 12321)); GNAT.Sockets.Listen_Socket (Socket => Receiver); loop GNAT.Sockets.Accept_Socket (Server => Receiver, Socket => Connection, Address => Client); Ada.Text_IO.Put_Line ("Client connected from " & GNAT.Sockets.Image (Client)); Channel := GNAT.Sockets.Stream (Connection); begin loop Character'Output (Channel, Character'Input (Channel)); end loop; exception when Ada.IO_Exceptions.End_Error => null; end; GNAT.Sockets.Close_Socket (Connection); end loop;
end Echo_Server;</lang>
AutoHotkey
echoserver.ahk, modified from script by zed gecko. <lang AutoHotkey>#SingleInstance Force Network_Port = 12321 Network_Address = 127.0.0.1
NewData := false DataReceived = Gosub Connection_Init return
Connection_Init: OnExit, ExitSub socket := PrepareForIncomingConnection(Network_Address, Network_Port) if socket = -1
ExitApp
Process, Exist DetectHiddenWindows On ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel) DetectHiddenWindows Off
NotificationMsg = 0x5555 OnMessage(NotificationMsg, "ReceiveData")
ExitMsg = 0x6666 OnMessage(ExitMsg, "ExitData")
FD_READ = 1 FD_CLOSE = 32 FD_CONNECT = 20
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket,
"UInt", ScriptMainWindowId, "UInt", ExitMsg, "Int", FD_CLOSE)
{
msgbox, closed
}
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket,
"UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CONNECT)
{
MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", ExitMsg, "Int", FD_CLOSE) ExitApp
}
SetTimer, NewConnectionCheck, 500 return
PrepareForIncomingConnection(IPAddress, Port) {
VarSetCapacity(wsaData, 32) result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) if ErrorLevel { MsgBox % "WSAStartup() could not be called due to error %ErrorLevel%. " . "Winsock 2.0 or higher is required." return -1 } if result { MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") return -1 } AF_INET = 2 SOCK_STREAM = 1 IPPROTO_TCP = 6 socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP) if socket = -1 { MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") return -1 } SizeOfSocketAddress = 16 VarSetCapacity(SocketAddress, SizeOfSocketAddress) InsertInteger(2, SocketAddress, 0, AF_INET) InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2) InsertInteger(DllCall("Ws2_32\inet_addr", "Str", IPAddress), SocketAddress, 4, 4) if DllCall("Ws2_32\bind", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress) { MsgBox % "bind() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?" return -1 } if DllCall("Ws2_32\listen", "UInt", socket, "UInt", "SOMAXCONN") { MsgBox % "LISTEN() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?" return -1 } return socket
}
ReceiveData(wParam, lParam) {
global DataReceived global NewData global mydata global ConnectionList socket := wParam ReceivedDataSize = 4096 Loop { VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
if ReceivedDataLength = 0
{ StringReplace, ConnectionList, ConnectionList, %socket%`n DllCall("Ws2_32\closesocket", "UInt", socket) } if ReceivedDataLength = -1 { WinsockError := DllCall("Ws2_32\WSAGetLastError") if WinsockError = 10035 { DataReceived = %TempDataReceived% NewData := true return 1 } if WinsockError <> 10054 { MsgBox % "recv() indicated Winsock error " . WinsockError StringReplace, ConnectionList, ConnectionList, %socket%`n DllCall("Ws2_32\closesocket", "UInt", socket) } } mydata := ReceivedData gosub myreceive
if (A_Index = 1)
TempDataReceived = TempDataReceived = %TempDataReceived%%ReceivedData% } return 1
}
ExitData(wParam, lParam) {
global ConnectionList socket := wParam ReceivedDataSize = 16 VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0) StringReplace, ConnectionList, ConnectionList, %socket%`n DllCall("Ws2_32\closesocket", "UInt", socket) return 1
}
SendData(wParam,SendData) {
SendDataSize := VarSetCapacity(SendData) SendDataSize += 1 Loop, parse, wParam, `n { If A_LoopField = Continue socket := A_LoopField sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendData, "Int", SendDatasize, "Int", 0) }
}
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
{
Loop %pSize% DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}
NewConnectionCheck: ConnectionCheck := DllCall("Ws2_32\accept", "UInt", socket,
"UInt", &SocketAddress, "Int", SizeOfSocketAddress)
if ConnectionCheck > 1
ConnectionList = %ConnectionList%%ConnectionCheck%`n
Return
SendProcedure: If ConnectionList <> {
SendText = %A_Hour%:%A_Min%:%A_Sec% SendData(ConnectionList,SendText)
} Return
myreceive:
TrayTip, server, %mydata%, ,16 return
GuiClose: ExitSub: DllCall("Ws2_32\WSACleanup") ExitApp</lang> A client is also available for testing this code.
C
This is a rather standard code (details apart); the reference guide for such a code is the Beej's Guide to Network programming. The dependency from POSIX is mainly in the use of the read and write functions, (using the socket as a file descriptor sometimes make things simpler).
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <string.h>
- include <errno.h>
- include <sys/types.h>
- include <sys/socket.h>
- include <netdb.h>
- include <unistd.h>
- include <sys/wait.h>
- include <signal.h>
- define MAX_ENQUEUED 20
- define BUF_LEN 256
- define PORT_STR "12321"
/* ------------------------------------------------------------ */ /* How to clean up after dead child processes */ /* ------------------------------------------------------------ */
void wait_for_zombie(int s) {
while(waitpid(-1, NULL, WNOHANG) > 0) ;
}
/* ------------------------------------------------------------ */ /* Core of implementation of a child process */ /* ------------------------------------------------------------ */
void echo_lines(int csock) {
char buf[BUF_LEN]; int r;
while( (r = read(csock, buf, BUF_LEN)) > 0 ) { (void)write(csock, buf, r); } exit(EXIT_SUCCESS);
}
/* ------------------------------------------------------------ */ /* Core of implementation of the parent process */ /* ------------------------------------------------------------ */
void take_connections_forever(int ssock) {
for(;;) { struct sockaddr addr; socklen_t addr_size = sizeof(addr); int csock;
/* Block until we take one connection to the server socket */ csock = accept(ssock, &addr, &addr_size);
/* If it was a successful connection, spawn a worker process to service it */ if ( csock == -1 ) { perror("accept"); } else if ( fork() == 0 ) { close(ssock); echo_lines(csock); } else { close(csock); } }
}
/* ------------------------------------------------------------ */ /* The server process's one-off setup code */ /* ------------------------------------------------------------ */
int main() {
struct addrinfo hints, *res; struct sigaction sa; int sock;
/* Look up the address to bind to */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if ( getaddrinfo(NULL, PORT_STR, &hints, &res) != 0 ) { perror("getaddrinfo"); exit(EXIT_FAILURE); }
/* Make a socket */ if ( (sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1 ) { perror("socket"); exit(EXIT_FAILURE); }
/* Arrange to clean up child processes (the workers) */ sa.sa_handler = wait_for_zombie; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if ( sigaction(SIGCHLD, &sa, NULL) == -1 ) { perror("sigaction"); exit(EXIT_FAILURE); }
/* Associate the socket with its address */ if ( bind(sock, res->ai_addr, res->ai_addrlen) != 0 ) { perror("bind"); exit(EXIT_FAILURE); }
freeaddrinfo(res);
/* State that we've opened a server socket and are listening for connections */ if ( listen(sock, MAX_ENQUEUED) != 0 ) { perror("listen"); exit(EXIT_FAILURE); }
/* Serve the listening socket until killed */ take_connections_forever(sock); return EXIT_SUCCESS;
}</lang>
C#
<lang csharp>using System.Net.Sockets; using System.Threading;
namespace ConsoleApplication1 {
class Program { static TcpListener listen; static Thread serverthread;
static void Main(string[] args) { listen = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 12321); serverthread = new Thread(new ThreadStart(DoListen)); serverthread.Start(); }
private static void DoListen() { // Listen listen.Start(); Console.WriteLine("Server: Started server");
while (true) { Console.WriteLine("Server: Waiting..."); TcpClient client = listen.AcceptTcpClient(); Console.WriteLine("Server: Waited");
// New thread with client Thread clientThread = new Thread(new ParameterizedThreadStart(DoClient)); clientThread.Start(client); } }
private static void DoClient(object client) { // Read data TcpClient tClient = (TcpClient)client;
Console.WriteLine("Client (Thread: {0}): Connected!", Thread.CurrentThread.ManagedThreadId); do { if (!tClient.Connected) { tClient.Close(); Thread.CurrentThread.Abort(); // Kill thread. }
if (tClient.Available > 0) { // Resend byte pByte = (byte)tClient.GetStream().ReadByte(); Console.WriteLine("Client (Thread: {0}): Data {1}", Thread.CurrentThread.ManagedThreadId, pByte); tClient.GetStream().WriteByte(pByte); }
// Pause Thread.Sleep(100); } while (true); } }
}</lang>
Clojure
<lang lisp>(use '[clojure.contrib.server-socket :only (create-server)]) (use '[clojure.contrib.duck-streams :only (read-lines write-lines)])
(defn echo [input output]
(write-lines (java.io.PrintWriter. output true) (read-lines input)))
(create-server 12321 echo)</lang>
Note here that an auto-flushing PrintWriter needs to be created, otherwise 'output' could simply be passed to write-lines.
CoffeeScript
<lang coffeescript> net = require("net") server = net.createServer (conn) ->
console.log "Connection from " + conn.remoteAddress + " on port " + conn.remotePort conn.setEncoding "utf8" buffer = "" conn.on "data", (data) -> i = 0
while i <= data.length char = data.charAt(i) buffer += char if char is "\n" conn.write buffer buffer = "" i++
server.listen 12321, "localhost" </lang>
Common Lisp
Sockets is not a standard part of Common Lisp but many implementations have support for this. The following example
<lang lisp>(defvar *clients* '()
"This is a list of (socket :input status) which is used with
`socket:socket-status' to test for data ready on a socket.")
(defun echo-server (port)
"Listen on `port' for new client connections and for data arriving on
any existing client connections"
(let ((server (socket:socket-server port))) (format t "Echo service listening on port ~a:~d~%" (socket:socket-server-host server) (socket:socket-server-port server)) (unwind-protect (loop (when (socket:socket-status server 0 1) (echo-accept-client (socket:socket-accept server :external-format :dos :buffered t))) (when *clients* (socket:socket-status *clients* 0 1) (mapcar #'(lambda (client) (when (eq :input (cddr client)) (echo-service-client (car client))) (when (eq :eof (cddr client)) (echo-close-client (car client)))) *clients*))) (socket-server-close server))))
(defun echo-accept-client (socket)
"Accept a new client connection and add it to the watch list." (multiple-value-bind (host port) (socket:socket-stream-peer socket) (format t "Connect from ~a:~d~%" host port)) (push (list socket :input nil) *clients*))
(defun echo-service-client (socket)
(let ((line (read-line socket nil nil))) (princ line socket) (finish-output socket)))
(defun echo-close-client (socket)
"Close a client connection and remove it from the watch list." (multiple-value-bind (host port) (socket:socket-stream-peer socket) (format t "Closing connection from ~a:~d~%" host port)) (close socket) (setq *clients* (remove socket *clients* :key #'car)))
(echo-server 12321)</lang>
D
This is a very basic server that processes the buffers one character at a time. In a real-world application, the buffers would be larger. More seriously, it processes one listener at a time. If the currSock.receive()
blocks, the loop will not process other clients. This opens the door for a trivial denial-of-service attack. A realistic echo service must multiplex clients.
<lang d>// Tested using DMD 2.048
import std.socket;
import std.array;
void main() {
Socket listener = new TcpSocket; assert(listener.isAlive); listener.bind(new InternetAddress(12321)); listener.listen(10);
Socket currSock; uint bytesRead; ubyte buff[1];
while(1) { currSock = listener.accept(); while ((bytesRead = currSock.receive(buff)) > 0) { currSock.send(buff); } currSock.close(); buff.clear(); }
}</lang>
Delphi
<lang Delphi>program EchoServer;
{$APPTYPE CONSOLE}
uses SysUtils, IdContext, IdTCPServer;
type
TEchoServer = class private FTCPServer: TIdTCPServer; public constructor Create; destructor Destroy; override; procedure TCPServerExecute(AContext: TIdContext); end;
constructor TEchoServer.Create; begin
FTCPServer := TIdTCPServer.Create(nil); FTCPServer.DefaultPort := 12321; FTCPServer.OnExecute := TCPServerExecute; FTCPServer.Active := True;
end;
destructor TEchoServer.Destroy; begin
FTCPServer.Active := False; FTCPServer.Free; inherited Destroy;
end;
procedure TEchoServer.TCPServerExecute(AContext: TIdContext); var
lCmdLine: string;
begin
lCmdLine := AContext.Connection.IOHandler.ReadLn; Writeln('>' + lCmdLine); AContext.Connection.IOHandler.Writeln('>' + lCmdLine);
if SameText(lCmdLine, 'QUIT') then begin AContext.Connection.IOHandler.Writeln('Disconnecting'); AContext.Connection.Disconnect; end;
end;
var
lEchoServer: TEchoServer;
begin
lEchoServer := TEchoServer.Create; try Writeln('Delphi Echo Server'); Writeln('Press Enter to quit'); Readln; finally lEchoServer.Free; end;
end.</lang>
Erlang
<lang erlang>-module(echo). -export([start/0]).
start() ->
spawn(fun () -> {ok, Sock} = gen_tcp:listen(12321, [{packet, line}]), echo_loop(Sock) end).
echo_loop(Sock) ->
{ok, Conn} = gen_tcp:accept(Sock), io:format("Got connection: ~p~n", [Conn]), Handler = spawn(fun () -> handle(Conn) end), gen_tcp:controlling_process(Conn, Handler), echo_loop(Sock).
handle(Conn) ->
receive {tcp, Conn, Data} -> gen_tcp:send(Conn, Data), handle(Conn); {tcp_closed, Conn} -> io:format("Connection closed: ~p~n", [Conn]) end.</lang>
F#
<lang fsharp>open System.IO open System.Net open System.Net.Sockets
let service (client:TcpClient) =
use stream = client.GetStream() use out = new StreamWriter(stream, AutoFlush = true) use inp = new StreamReader(stream) while not inp.EndOfStream do match inp.ReadLine() with | line -> printfn "< %s" line out.WriteLine(line) printfn "closed %A" client.Client.RemoteEndPoint client.Close |> ignore
let EchoService =
let socket = new TcpListener(IPAddress.Loopback, 12321) do socket.Start() printfn "echo service listening on %A" socket.Server.LocalEndPoint while true do let client = socket.AcceptTcpClient() printfn "connect from %A" client.Client.RemoteEndPoint let job = async { use c = client in try service client with _ -> () } Async.Start job
[<EntryPoint>] let main _ =
EchoService 0</lang>
Factor
Connections get logged to /place-where-factor-is/logs/echo-server
.
<lang factor>USING: accessors io io.encodings.utf8 io.servers.connection
threads ;
IN: rosetta.echo
CONSTANT: echo-port 12321
- handle-client ( -- )
[ write "\r\n" write flush ] each-line ;
- <echo-server> ( -- threaded-server )
utf8 <threaded-server> "echo-server" >>name echo-port >>insecure [ handle-client ] >>handler ;
- start-echo-server ( -- threaded-server )
<echo-server> [ start-server ] in-thread ;</lang>
Forth
<lang forth>include unix/socket.fs
128 constant size
- (echo) ( sock buf -- sock buf )
begin cr ." waiting..." 2dup 2dup size read-socket nip dup 0> while ." got: " 2dup type rot write-socket repeat drop drop drop ;
create buf size allot
- echo-server ( port -- )
cr ." Listening on " dup . create-server dup 4 listen begin dup accept-socket cr ." Connection!" buf ['] (echo) catch cr ." Disconnected (" . ." )" drop close-socket again ;
12321 echo-server</lang> TODO: use tasker.fs and non-blocking semantics to handle mutliple connections
Go
<lang go>package main
import ( "fmt" "net" "bufio" )
func echo(s net.Conn, i int) { fmt.Printf("%d: %v <-> %v\n", i, s.LocalAddr(), s.RemoteAddr()) b := bufio.NewReader(s) for { line, e := b.ReadBytes('\n') if e != nil { break } s.Write(line) } fmt.Printf("%d: closed\n", i) }
func main() { l, e := net.Listen("tcp", ":12321") for i := 0; e == nil; i++ { var s net.Conn s, e = l.Accept() go echo(s, i) } }</lang>
Haskell
<lang haskell>module Main where import Network (withSocketsDo, accept, listenOn, sClose, PortID(PortNumber)) import Control.Monad (forever) import System.IO (hGetLine, hPutStrLn, hFlush, hClose) import System.IO.Error (isEOFError) import Control.Concurrent (forkIO) import Control.Exception (bracket)
-- For convenience in testing, ensure that the listen socket is closed if the main loop is aborted withListenOn port body = bracket (listenOn port) sClose body
echo (handle, host, port) = catch (forever doOneLine) stop where
doOneLine = do line <- hGetLine handle print (host, port, init line) hPutStrLn handle line hFlush handle stop error = do putStrLn $ "Closed connection from " ++ show (host, port) ++ " due to " ++ show error hClose handle
main = withSocketsDo $
withListenOn (PortNumber 12321) $ \listener -> forever $ do acc@(_, host, port) <- accept listener putStrLn $ "Accepted connection from " ++ show (host, port) forkIO (echo acc)</lang>
Java
<lang java>import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket;
public class EchoServer { ServerSocket serverSocket; public EchoServer(){ }
public void start() { try { serverSocket = new ServerSocket(12321); while(true){ Thread clientThread = new Thread(new ClientHandler(serverSocket.accept())); clientThread.start(); } } catch (IOException e) { e.printStackTrace(); } finally { try { System.out.println("closing server socket"); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } }
}
public static void main(String[] args) { EchoServer es = new EchoServer(); es.start(); } }
class ClientHandler implements Runnable { private static int numConnections; private int connectionId = 0; Socket clientSocket;
public ClientHandler(Socket s) { connectionId = numConnections++; System.out.println("handling connection, #" + connectionId); clientSocket = s; }
public void run() { PrintWriter out = null; BufferedReader in = null; try { out = new PrintWriter(clientSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String inputLine, outputLine; while((inputLine = in.readLine()) != null){ outputLine = inputLine; System.out.println("recieved: " + outputLine); out.write(outputLine+"\n"); out.flush(); if (outputLine.equals("exit")) break; } } catch(Exception e) { e.printStackTrace(); } finally { out.close(); try { in.close(); clientSocket.close(); System.out.println("closing connection, #" + connectionId); } catch (IOException e) { e.printStackTrace(); } } } }</lang>
JavaScript
<lang javascript>var net = require('net');
var server = net.createServer(function(conn) {
console.log("Connection from " + conn.remoteAddress + " on port " + conn.remotePort); conn.setEncoding("utf8"); var buffer = "";
conn.on("data", function(data) { for(var i = 0; i <= data.length; i++) { var char = data.charAt(i); buffer += char; if(char == "\n") { conn.write(buffer); buffer = ""; } } });
});
server.listen(12321, "localhost");</lang>
Oz
<lang oz>declare
ServerSocket = {New Open.socket init}
proc {Echo Socket} case {Socket getS($)} of false then skip [] Line then {System.showInfo "Received line: "#Line} {Socket write(vs:Line#"\n")} {Echo Socket} end end
class TextSocket from Open.socket Open.text end
in
{ServerSocket bind(takePort:12321)} {System.showInfo "Socket bound."}
{ServerSocket listen} {System.showInfo "Started listening."}
for do ClientHost ClientPort ClientSocket = {ServerSocket accept(accepted:$
acceptClass:TextSocket host:?ClientHost port:?ClientPort )}
in {System.showInfo "Connection accepted from "#ClientHost#":"#ClientPort#"."} thread {Echo ClientSocket}
{System.showInfo "Connection lost: "#ClientHost#":"#ClientPort#"."}
{ClientSocket close} end end</lang>
Client test code: <lang oz>declare
Socket = {New class $ from Open.socket Open.text end init}
in
{Socket connect(port:12321)} {Socket write(vs:"Hello\n")} {System.showInfo "Client received: "#{Socket getS($)}} {Socket close}</lang>
Example session:
Socket bound. Started listening. Connection accepted from localhost:2048. Received line: Hello Client received: Hello Connection lost: localhost:2048.
Perl
This server will run indefinitely listening in the port 12321 and forking every time a client connects, the childs listen to the client and write back.
This is an example using the IO::Socket module: <lang perl>use IO::Socket; my $use_fork = 1;
my $sock = new IO::Socket::INET (
LocalHost => '127.0.0.1', LocalPort => '12321', Proto => 'tcp', Listen => 1, # maximum queued connections Reuse => 1, )
or die "socket: $!"; # no newline, so perl appends stuff
$SIG{CHLD} = 'IGNORE' if $use_fork; # let perl deal with zombies
print "listening...\n"; while (1) { # declare $con 'my' so it's closed by parent every loop
my $con = $sock->accept()
or die "accept: $!"; fork and next if $use_fork; # following are for child only
print "incoming..\n"; print $con $_ while(<$con>); # read each line and write back print "done\n";
last if $use_fork; # if not forking, loop }
- child will reach here and close its copy of $sock before exit</lang>
This is an equivalent program using the Net::Server module: <lang perl>package Echo; use base 'Net::Server::Fork'; sub process_request {
print while <STDIN>;
} Echo->run(port => 12321, log_level => 3);</lang> It also prints the IP address and port number of every connection.
This is a more complicated example using preforking: <lang perl>package Echo; use base 'Net::Server::PreFork'; sub process_request {
print while <STDIN>;
} Echo->run(port => 12321, log_level => 3);</lang> By default it spawns 5 child processes at startup, makes sure there are always at least 2 and at most 10 spare children available for new requests, each of which will be killed after processing 1000 requests and new ones will take their place.
PHP
<lang PHP>$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); socket_bind($socket, '127.0.0.1', 12321); socket_listen($socket);
$client_count = 0; while (true){
if (($client = socket_accept($socket)) === false) continue; $client_count++;
$client_name = 'Unknown'; socket_getpeername($client, $client_name); echo "Client {$client_count} ({$client_name}) connected\n"; $pid = pcntl_fork(); if($pid == -1) die('Could not fork'); if($pid){ pcntl_waitpid(-1, $status, WNOHANG); continue; }
//In a child process while(true){ if($input = socket_read($client, 1024)){ socket_write($client, $input); } else { socket_shutdown($client); socket_close($client); echo "Client {$client_count} ({$client_name}) disconnected\n"; exit(); } }
}</lang>
PicoLisp
<lang PicoLisp>(setq Port (port 12321))
(loop
(setq Sock (listen Port)) # Listen (NIL (fork) (close Port)) # Accepted (close Sock) ) # Parent: Close socket and continue
- Child:
(prinl (stamp) " -- (Pid " *Pid ") Client connected from " *Adr)
(in Sock
(until (eof) # Echo lines (out Sock (prinl (line))) ) )
(prinl (stamp) " -- (Pid " *Pid ") Client disconnected") (bye) # Terminate child</lang>
PureBasic
<lang Purebasic>NewMap RecData.s() OpenWindow(0, 100, 200, 200, 100, "Echo Server", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget ) InitNetwork() CreateNetworkServer(1, 12321)
Repeat
Event = NetworkServerEvent() ClientID = EventClient() If Event = #PB_NetworkEvent_Connect ; When a new client has been connected... AddMapElement(RecData(), Str(ClientID)) ElseIf Event = #PB_NetworkEvent_Data *Buffer = AllocateMemory(20000) count = ReceiveNetworkData(ClientID, *Buffer, 20000) For i = 1 To count RecData(Str(ClientID)) + Mid( PeekS(*Buffer, count), i , 1) If Right( RecData(Str(ClientID)), 2) = #CRLF$ SendNetworkString (ClientID, RecData(Str(ClientID))) Debug IPString(GetClientIP(ClientID)) + ":" + Str(GetClientPort(ClientID)) + " " + RecData(Str(ClientID)) RecData(Str(ClientID)) = "" EndIf Next FreeMemory(*Buffer) ElseIf Event = #PB_NetworkEvent_Disconnect ; When a client has closed the connection... DeleteMapElement(RecData(), Str(ClientID)) EndIf
Event = WaitWindowEvent(10)
Until Event = #PB_Event_CloseWindow</lang>
Python
<lang python>import SocketServer
HOST = "localhost" PORT = 12321
- this server uses ThreadingMixIn - one thread per connection
- replace with ForkMixIn to spawn a new process per connection
class EchoServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
# no need to override anything - default behavior is just fine pass
class EchoRequestHandler(SocketServer.StreamRequestHandler):
""" Handles one connection to the client. """ def handle(self): print "connection from %s" % self.client_address[0] while True: line = self.rfile.readline() if not line: break print "%s wrote: %s" % (self.client_address[0], line.rstrip()) self.wfile.write(line) print "%s disconnected" % self.client_address[0]
- Create the server
server = EchoServer((HOST, PORT), EchoRequestHandler)
- Activate the server; this will keep running until you
- interrupt the program with Ctrl-C
print "server listening on %s:%s" % server.server_address server.serve_forever()</lang>
REBOL
<lang rebol>server-port: open/lines tcp://:12321 forever [
connection-port: first server-port until [ wait connection-port error? try [insert connection-port first connection-port] ] close connection-port
] close server-port</lang>
Ruby
<lang ruby>require 'socket' server = TCPServer.new(12321)
while (connection = server.accept)
Thread.new(connection) do |conn| port, host = conn.peeraddr[1,2] client = "#{host}:#{port}" puts "#{client} is connected" begin loop do line = conn.readline puts "#{client} says: #{line}" conn.puts(line) end rescue EOFError conn.close puts "#{client} has disconnected" end end
end</lang>
Ruby 1.9.2 introduced an alternate method to create TCP server sockets. The Socket.tcp_server_loop
method encapsulates the guts of the server into a block.
<lang ruby>require 'socket'
Socket.tcp_server_loop(12321) do |conn, addr|
Thread.new do client = "#{addr.ip_address}:#{addr.ip_port}" puts "#{client} is connected" begin loop do line = conn.readline puts "#{client} says: #{line}" conn.puts(line) end rescue EOFError conn.close puts "#{client} has disconnected" end end
end</lang>
Scheme
Based on the Guile Internet Socket Server Example. <lang scheme>; Needed in Guile for read-line (use-modules (ice-9 rdelim))
- Variable used to hold child PID returned from forking
(define child #f)
- Start listening on port 12321 for connections from any address
(let ((s (socket PF_INET SOCK_STREAM 0)))
(setsockopt s SOL_SOCKET SO_REUSEADDR 1) (bind s AF_INET INADDR_ANY 12321) (listen s 5) ; Queue size of 5
(simple-format #t "Listening for clients in pid: ~S" (getpid)) (newline)
- Wait for connections forever
(while #t (let* ((client-connection (accept s)) (client-details (cdr client-connection)) (client (car client-connection)))
- Once something connects fork
(set! child (primitive-fork)) (if (zero? child) (begin
- Then have child fork to avoid zombie children (grandchildren aren't our responsibility)
(set! child (primitive-fork)) (if (zero? child) (begin
- Display some connection details
(simple-format #t "Got new client connection: ~S" client-details) (newline) (simple-format #t "Client address: ~S" (gethostbyaddr (sockaddr:addr client-details))) (newline)
- Wait for input from client and then echo the input back forever (or until client quits)
(do ((line (read-line client)(read-line client))) ((zero? 1)) (display line client)(newline client))))
- Child exits after spawning grandchild.
(primitive-exit))
- Parent waits for child to finish spawning grandchild
(waitpid child)))))</lang>
Tcl
This code is single-threaded. It uses non-blocking I/O to perform the transfers, sitting on top of the event multiplexer system call (e.g., select()
on Unix) to decide when to take new connections or service a particular socket. This makes this into a very lightweight echo server in terms of overall system resources.
<lang tcl># How to handle an incoming new connection proc acceptEcho {chan host port} {
puts "opened connection from $host:$port" fconfigure $chan -blocking 0 -buffering line -translation crlf fileevent $chan readable [list echo $chan $host $port]
}
- How to handle an incoming message on a connection
proc echo {chan host port} {
if {[gets $chan line] >= 0} { puts $chan $line } elseif {[eof $chan]} { close $chan puts "closed connection from $host:$port" } # Other conditions causing a short read need no action
}
- Make the server socket and wait for connections
socket -server acceptEcho -myaddr localhost 12321 vwait forever</lang>
Alternative version
A more succinct version (though one harder to adapt to other kinds of services, but closer to the standard unix echo daemon since it has no line-buffering) is to use an asynchronous binary copy. <lang tcl># How to handle an incoming new connection proc acceptEcho {chan host port} {
puts "opened connection from $host:$port" fconfigure $chan -translation binary -buffering none fcopy $chan $chan -command [list done $chan $host $port]
}
- Called to finalize the connection
proc done {chan host port args} {
puts "closed connection from $host:$port" close $chan
}
- Make the server socket and wait for connections
socket -server acceptEcho -myaddr localhost 12321 vwait forever</lang>
- Programming Tasks
- Networking and Web Interaction
- Ada
- AutoHotkey
- C
- C sharp
- Clojure
- CoffeeScript
- Common Lisp
- Common Lisp examples needing attention
- Examples needing attention
- D
- Delphi
- Erlang
- F Sharp
- Factor
- Forth
- Go
- Haskell
- Java
- JavaScript
- Oz
- Perl
- PHP
- PicoLisp
- PureBasic
- Python
- REBOL
- Ruby
- Scheme
- Tcl
- Lotus 123 Macro Scripting/Omit
- TI-83 BASIC/Omit
- TI-89 BASIC/Omit
- M4/Omit
- ML/I/Omit
- PARI/GP/Omit
- Retro/Omit
- SNUSP/Omit
- Unlambda/Omit