Sockets 1 - plain sockets binary protocol

Content:

  1. Introduction
  2. TCP and socket API
  3. Example protocol
  4. Client
  5. Singlethreaded server
  6. Multithreaded server
  7. Multiplexed server
  8. Asynchroneous server

For other articles in series see:

Introduction:

TCP sockets is the foundation for most network communication today. And even though applications often use higher level API's, then understanding the foundation is beneficial. Both to be able to understand why higher level API's are as they are and to be able to use TCP sockets directly if necessary.

TCP and socket API:

One very important aspect to understand is that the TCP protocol and the socket API are stream oriented.

This mean that sender write chunks of bytes to the stream and receiver read chunks of bytes from the stream, but the receivers chunks can and often are different from the senders chunks.

Example:

This means that receiver can not use chunk sizes read to determine any type of data lengths.

Instead more explict management of data length has to be used:

The current socket API is very old. It goes back to Berkely sockets in BSD Unix in 1983. Since then it has been implemented on most platforms including other Unix, Linux, VMS etc.. Even Windows has a 99% compatible implementation Winsocket.

Example protocol

The binary protocol used in all examples are very simple:

  1. client connect to server
  2. client send 4 byte integer in network order (big endian)
  3. client send 4 byte integer in network order (big endian)
  4. server send 4 byte integer with sum of clients two integers in network order (big endian)
  5. client send 1 byte with string length in bytes
  6. client send bytes of string
  7. server send 1 byte with duplicated string length in bytes
  8. server send bytes of duplicated string
  9. client and server closes connection

Client:

Client logic is:

connect
send/write
receive/read
close

Socket and ServerSocket classes has been in Java since version 1.0. Java also have DataInputStream and DataOutputStream classes for convenient reading and writing binary data.

Note that DataInputStream and DataOutputStream always use big endian.

package socket.plain.binary.traditional;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class Client {
    private static final String HOST = "localhost";
    private static final int PORT = 12345;
    public static void main(String[] args) {
        try {
            // open connection
            Socket s = new Socket(HOST, PORT);
            // get streams
            DataInputStream dis = new DataInputStream(s.getInputStream());
            DataOutputStream dos = new DataOutputStream(s.getOutputStream());
            System.out.println("(connected)");
            // send a and b
            int a = 123;
            int b = 456;
            dos.writeInt(a);
            dos.writeInt(b);
            dos.flush();
            // receive c and print
            int c = dis.readInt();
            System.out.println(c);
            // send v
            String v = "ABC";
            byte[] vb = v.getBytes("UTF-8");
            dos.writeByte(vb.length);
            dos.write(vb);
            dos.flush();
            // receive v2 and print
            int buflen = dis.readByte();
            byte[] buf = new byte[buflen];
            int ix = 0;
            while(ix < buf.length) {
                ix += dis.read(buf, ix, buf.length - ix);
            }
            String v2 = new String(buf, "UTF-8");
            System.out.println(v2);
            // close connection
            dis.close();
            dos.close();
            s.close();
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
}

Java 1.4 added several new IO features called NIO including a SocketChannel. It is not that much of an improvement for client except that is supports both little and big endian.

package socket.plain.binary.modern;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SocketChannel;

public class Client {
    private static final String HOST = "localhost";
    private static final int PORT = 12345;
    public static void main(String[] args) {
        try {
            // open connection
            SocketChannel sc = SocketChannel.open();
            sc.connect(new InetSocketAddress(HOST, PORT));
            System.out.println("(connected)");
            ByteBuffer bb;
            // send a and b
            int a = 123;
            int b = 456;
            bb = ByteBuffer.allocate(8);
            bb.order(ByteOrder.BIG_ENDIAN);
            bb.putInt(a);
            bb.putInt(b);
            bb.rewind();
            sc.write(bb);
            // receive c and print
            bb = ByteBuffer.allocate(4);
            while(bb.hasRemaining() && sc.read(bb) > 0);
            bb.rewind();
            bb.order(ByteOrder.BIG_ENDIAN);
            int c = bb.getInt();
            System.out.println(c);
            // send v
            String v = "ABC";
            byte[] vb = v.getBytes("UTF-8");
            bb = ByteBuffer.allocate(1 + vb.length);
            bb.put((byte)vb.length);
            bb.put(vb);
            bb.rewind();
            sc.write(bb);
            // receive v2 and print
            bb = ByteBuffer.allocate(1);
            sc.read(bb);
            bb.rewind();
            int buflen = bb.get();
            bb = ByteBuffer.allocate(buflen);
            while(bb.hasRemaining() && sc.read(bb) > 0);
            bb.rewind();
            byte[] buf = new byte[buflen];
            bb.get(buf);
            String v2 = new String(buf, "UTF-8");
            System.out.println(v2);
            // close connection
            sc.close();
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
}

.NET provide two levels of socket API:

This is the low level API.

using System;
using System.Net.Sockets;
using System.Text;

namespace SocketDemo.Binary.Low.Client
{
    public class Program
    {
        private static byte[] IntToBytes(int v)
        {
            byte[] buf = BitConverter.GetBytes(v);
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return buf;
        }
        private static int BytesToInt(byte[] buf)
        {
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return BitConverter.ToInt32(buf, 0);
        }
        private const string HOST = "localhost";
        private const int PORT = 12345;
        public static void Main(string[] args)
        {
            // open connection
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            s.Connect(HOST, PORT);
            Console.WriteLine("(connected)");
            byte[] buf;
            int ix;
            // send a and b
            int a = 123;
            int b = 456;
            s.Send(IntToBytes(a));
            s.Send(IntToBytes(b));
            // receive c and print
            buf = new Byte[4];
            ix = 0;
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            int c = BytesToInt(buf);
            Console.WriteLine(c);
            // send v
            string v = "ABC";
            byte[] vb = Encoding.UTF8.GetBytes(v);
            s.Send(new byte[] { (byte)vb.Length });
            s.Send(vb);
            // receive v2 and print
            buf = new Byte[1];
            s.Receive(buf);
            int buflen = buf[0];
            buf = new byte[buflen];
            ix = 0;
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            string v2 = Encoding.UTF8.GetString(buf);
            Console.WriteLine(v2);
            // close connection
            s.Close();
        }
    }
}

.NET provide two levels of socket API:

This is the high level API.

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;

namespace SocketDemo.Binary.High.Client
{
    public class Program
    {
        private static int HTON(int v)
        {
            if(BitConverter.IsLittleEndian)
            {
                byte[] buf = BitConverter.GetBytes(v);
                Array.Reverse(buf);
                return BitConverter.ToInt32(buf, 0);
            }
            else
            {
                return v;
            }
        }
        private static int NTOH(int v)
        {
            if(BitConverter.IsLittleEndian)
            {
                byte[] buf = BitConverter.GetBytes(v);
                Array.Reverse(buf);
                return BitConverter.ToInt32(buf, 0);
            }
            else
            {
                return v;
            }
        }
        private const string HOST = "localhost";
        private const int PORT = 12345;
        public static void Main(string[] args)
        {
            // open connection
            TcpClient cli = new TcpClient(HOST, PORT);
            // get reader and writer
            NetworkStream stm = cli.GetStream();
            BinaryReader br = new BinaryReader(stm);
            BinaryWriter bw = new BinaryWriter(stm);
            Console.WriteLine("(connected)");
            // send a and b
            int a = 123;
            int b = 456;
            bw.Write(HTON(a));
            bw.Write(HTON(b));
            bw.Flush();
            // receive c and print
            int c = NTOH(br.ReadInt32());
            Console.WriteLine(c);
            // send v
            string v = "ABC";
            byte[] vb = Encoding.UTF8.GetBytes(v);
            bw.Write((byte)vb.Length);
            bw.Write(vb);
            bw.Flush();
            // receive v2 and print
            int buflen = br.ReadByte();
            byte[] buf = new byte[buflen];
            int ix = 0;
            while(ix < buf.Length)
            {
                ix += br.Read(buf, ix, buf.Length - ix);
            }
            string v2 = Encoding.UTF8.GetString(buf);
            Console.WriteLine(v2);
            // close connection
            br.Close();
            bw.Close();
            cli.Close();
        }
    }
}

.NET provide two levels of socket API:

This is the low level API.

Imports System
Imports System.Net.Sockets
Imports System.Text

Namespace SocketDemo.Binary.Low.Client
    Public Class Program
        Private Shared Function IntToBytes(v As Integer) As Byte()
            Dim buf As Byte() = BitConverter.GetBytes(v)
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return buf
        End Function
        Private Shared Function BytesToInt(buf As Byte()) As Integer
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return BitConverter.ToInt32(buf, 0)
        End Function
        Private Const HOST As String = "localhost"
        Private Const PORT As Integer = 12345
        Public Shared Sub Main(args As String())
            ' open connection
            Dim s As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            s.Connect(HOST, PORT)
            Console.WriteLine("(connected)")
            Dim buf As Byte()
            Dim ix As Integer
            ' send a and b
            Dim a As Integer = 123
            Dim b As Integer = 456
            s.Send(IntToBytes(a))
            s.Send(IntToBytes(b))
            ' receive c and print
            buf = New [Byte](3) {}
            ix = 0
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim c As Integer = BytesToInt(buf)
            Console.WriteLine(c)
            ' send v
            Dim v As String = "ABC"
            Dim vb As Byte() = Encoding.UTF8.GetBytes(v)
            s.Send(New Byte() {CByte(vb.Length)})
            s.Send(vb)
            ' receive v2 and print
            buf = New [Byte](0) {}
            s.Receive(buf)
            Dim buflen As Integer = buf(0)
            buf = New Byte(buflen - 1) {}
            ix = 0
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim v2 As String = Encoding.UTF8.GetString(buf)
            Console.WriteLine(v2)
            ' close connection
            s.Close()
        End Sub
    End Class
End Namespace

.NET provide two levels of socket API:

This is the high level API.

Imports System
Imports System.IO
Imports System.Net.Sockets
Imports System.Text

Namespace SocketDemo.Binary.High.Client
    Public Class Program
        Private Shared Function HTON(v As Integer) As Integer
            If BitConverter.IsLittleEndian Then
                Dim buf As Byte() = BitConverter.GetBytes(v)
                Array.Reverse(buf)
                Return BitConverter.ToInt32(buf, 0)
            Else
                Return v
            End If
        End Function
        Private Shared Function NTOH(v As Integer) As Integer
            If BitConverter.IsLittleEndian Then
                Dim buf As Byte() = BitConverter.GetBytes(v)
                Array.Reverse(buf)
                Return BitConverter.ToInt32(buf, 0)
            Else
                Return v
            End If
        End Function
        Private Const HOST As String = "localhost"
        Private Const PORT As Integer = 12345
        Public Shared Sub Main(args As String())
            ' open connection
            Dim cli As New TcpClient(HOST, PORT)
            ' get reader and writer
            Dim stm As NetworkStream = cli.GetStream()
            Dim br As New BinaryReader(stm)
            Dim bw As New BinaryWriter(stm)
            Console.WriteLine("(connected)")
            ' send a and b
            Dim a As Integer = 123
            Dim b As Integer = 456
            bw.Write(HTON(a))
            bw.Write(HTON(b))
            bw.Flush()
            ' receive c and print
            Dim c As Integer = NTOH(br.ReadInt32())
            Console.WriteLine(c)
            ' send v
            Dim v As String = "ABC"
            Dim vb As Byte() = Encoding.UTF8.GetBytes(v)
            bw.Write(CByte(vb.Length))
            bw.Write(vb)
            bw.Flush()
            ' receive v2 and print
            Dim buflen As Integer = br.ReadByte()
            Dim buf As Byte() = New Byte(buflen - 1) {}
            Dim ix As Integer = 0
            While ix < buf.Length
                ix += br.Read(buf, ix, buf.Length - ix)
            End While
            Dim v2 As String = Encoding.UTF8.GetString(buf)
            Console.WriteLine(v2)
            ' close connection
            br.Close()
            bw.Close()
            cli.Close()
        End Sub
    End Class
End Namespace

The C API has changed regarding how to lookup information. There is an old style which was created for IPv4 and is known to have some problems with IPv6 - and there is a new style which was created to support both IPv4 and IPv6 nicely nicely.

Always use the new style when writing new code. The old style is only shown to understand existing programs or to develop on very old systems.

This example shows the old style.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define HOST "localhost"
#define PORT 12345

int main()
{
    int sd, status, ix, buflen;
    long int a, b, c, tmp;
    char *buf, *v, *v2, len;
    struct sockaddr_in remote;
    struct hostent *hostinfo;
#ifdef WIN32
    WSADATA WSAData;
    WSAStartup(0x0101, &WSAData);
#endif
    /* lookup host */
    hostinfo = gethostbyname(HOST);
    if(!hostinfo)
    {
        printf("Error looking up host %s: %s\n", HOST, strerror(errno));
        exit(0);
    }
    /* create socket */
    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* connect to host */
    remote.sin_family = hostinfo->h_addrtype;
    memcpy(&remote.sin_addr, hostinfo->h_addr_list[0], hostinfo->h_length);
    remote.sin_port = htons(PORT);
    status = connect(sd, (struct sockaddr *)&remote, sizeof(remote));
    if(status != 0)
    {
        printf("Error connecting to host %s port %d: %s\n", HOST, PORT, strerror(errno));
        exit(0);
    }
    /* send a and b */
    a = 123;
    b = 456;
    tmp = htonl(a);
    status = send(sd, (char *)&tmp, sizeof(tmp), 0);
    if(status < 0)
    {
        printf("Error sending a: %s\n", strerror(errno));
        exit(0);
    }
    tmp = htonl(b);
    status = send(sd, (char *)&tmp, sizeof(tmp), 0);
    if(status < 0)
    {
        printf("Error sending b: %s\n", strerror(errno));
        exit(0);
    }
    /* receive c and print */
    buflen = sizeof(tmp);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd, (char *)&tmp + ix, buflen - ix, 0);
    }
    c = ntohl(tmp);
    printf("%ld\n", c);
    /* send v */
    v = "ABC";
    len = strlen(v);
    status = send(sd, &len, sizeof(len), 0);
    if(status < 0)
    {
        printf("Error sending v: %s\n", strerror(errno));
        exit(0);
    }
    status = send(sd, v, len, 0);
    if(status < 0)
    {
        printf("Error sending v: %s\n", strerror(errno));
        exit(0);
    }
    /* receive v2 and print */
    recv(sd, &len, sizeof(len), 0);
    buflen = len;
    buf = malloc(buflen);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd, buf + ix, buflen - ix, 0);
    }
    v2 = malloc(buflen + 1);
    memcpy(v2, buf, buflen);
    v2[buflen] = '\0';
    printf("%s\n", v2);
    free(buf);
    free(v2);
    /* close socket */
#ifdef WIN32
    closesocket(sd);
    WSACleanup();
#else
    close(sd);
#endif
    return 0;
}

The C API has changed regarding how to lookup information. There is an old style which was created for IPv4 and is known to have some problems with IPv6 - and there is a new style which was created to support both IPv4 and IPv6 nicely nicely.

Always use the new style when writing new code. The old style is only shown to understand existing programs or to develop on very old systems.

This example shows the new style.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define HOST "localhost"
#define PORT "12345"

int main()
{
    int sd, status, ix, buflen;
    long int a, b, c, tmp;
    char *buf, *v, *v2, len;
    struct addrinfo hints, *res;
#ifdef WIN32
    WSADATA WSAData;
    WSAStartup(0x0101, &WSAData);
#endif
    /* lookup host */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = 0;
    status = getaddrinfo(HOST, PORT, &hints, &res);
    if(status != 0)
    {
        printf("Error looking up host: %s\n", HOST);
        exit(0);
    }
    /* create socket */
    sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* connect to host */
    status = connect(sd, res->ai_addr, res->ai_addrlen);
    if(status != 0)
    {
        printf("Error connecting to host %s port %s: %s\n", HOST, PORT, strerror(errno));
        exit(0);
    }
    freeaddrinfo(res);
    /* send a and b */
    a = 123;
    b = 456;
    tmp = htonl(a);
    status = send(sd, (char *)&tmp, sizeof(tmp), 0);
    if(status < 0)
    {
        printf("Error sending a: %s\n", strerror(errno));
        exit(0);
    }
    tmp = htonl(b);
    status = send(sd, (char *)&tmp, sizeof(tmp), 0);
    if(status < 0)
    {
        printf("Error sending b: %s\n", strerror(errno));
        exit(0);
    }
    /* receive c and print */
    buflen = sizeof(tmp);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd, (char *)&tmp + ix, buflen - ix, 0);
    }
    c = ntohl(tmp);
    printf("%ld\n", c);
    /* send v */
    v = "ABC";
    len = strlen(v);
    status = send(sd, &len, sizeof(len), 0);
    if(status < 0)
    {
        printf("Error sending v: %s\n", strerror(errno));
        exit(0);
    }
    status = send(sd, v, len, 0);
    if(status < 0)
    {
        printf("Error sending v: %s\n", strerror(errno));
        exit(0);
    }
    /* receive v2 and print */
    recv(sd, &len, sizeof(len), 0);
    buflen = len;
    buf = malloc(buflen);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd, buf + ix, buflen - ix, 0);
    }
    v2 = malloc(buflen + 1);
    memcpy(v2, buf, buflen);
    v2[buflen] = '\0';
    printf("%s\n", v2);
    free(buf);
    free(v2);
    /* close socket */
#ifdef WIN32
    closesocket(sd);
    WSACleanup();
#else
    close(sd);
#endif
    return 0;
}

Indy 10 has a convenient TIdTcpClient class.

program PlainBinClientHigh;

uses
  IdTCPClient;

const
  HOST = 'localhost';
  PORT = 12345;

var
  cli : TIdTCPClient;
  v, v2 : string;
  a, b, c, len : integer;

begin
  (* open connection *)
  cli := TIdTCPClient.Create;
  cli.Connect(HOST, PORT);
  (* send a and b *)
  a := 123;
  b := 456;
  cli.Socket.Write(a, true);
  cli.Socket.Write(b, true);
  (* receive c and print *)
  c := cli.Socket.ReadInt32(true);
  writeln(c);
  (* send v *)
  v := 'ABC';
  cli.Socket.Write(Byte(length(v)));
  cli.Socket.Write(v);
  (* received v2 and print *)
  len := cli.Socket.ReadByte;
  v2 := cli.Socket.ReadString(len);
  writeln(v2);
  (* close connection *)
  cli.Disconnect;
  cli.Free;
end.

Besides Indy it is also possible to use plain WinSock.

program PlainBinClientLow;

uses
  SysUtils,
  Windows,
  WinSock;

const
  HOST = 'localhost';
  PORT = 12345;

var
  wsa : TWSAData;
  s : TSocket;
  hostinfo : PHOstEnt;
  remote : TSockAddrIn;
  v, v2 : string;
  status, a, b, c, tmp, ix, buflen : integer;
  buf : array of byte;
  len : byte;

begin
  WSAStartup($101, wsa);
  (* create socket *)
  s := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if s = INVALID_SOCKET then begin
     writeln('Error creating socket: ' + SysErrorMessage(GetLastError));
     halt;
  end;
  (* connect to host *)
  hostinfo := GetHostByName(HOST);
  remote.sin_family := AF_INET;
  remote.sin_port :=  htons(PORT);
  remote.sin_addr.S_addr := PInAddr(hostinfo^.H_Addr_List^)^.S_addr;
  status := Connect(s, remote, sizeof(remote));
  if status <> 0 then begin
    writeln('Error connecting to host ' + HOST + ' port ' + IntToStr(PORT) + ': ' + SysErrorMessage(GetLastError));
    halt;
  end;
  (* send a and b *)
  a := 123;
  b := 456;
  tmp := htonl(a);
  status := Send(s, @tmp, sizeof(tmp), 0);
  if status < 0 then begin
    writeln('Error sending a: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  tmp := htonl(b);
  status := Send(s, @tmp, sizeof(tmp), 0);
  if status < 0 then begin
    writeln('Error sending b: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  (* receive c and print *)
  buflen := sizeof(tmp);
  ix := 0;
  while ix < buflen do begin
    ix := ix + Recv(s, PChar(@tmp) + ix, buflen - ix, 0);
  end;
  c := ntohl(tmp);
  writeln(c);
  (* send v *)
  v := 'ABC';
  len := Length(v);
  status := Send(s, len, sizeof(len), 0);
  if status < 0 then begin
    writeln('Error sending v: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  status := Send(s, v[1], len, 0);
  if status < 0 then begin
    writeln('Error sending v: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  (* receive v2 and print *)
  Recv(s, @len, sizeof(len), 0);
  SetLength(buf, len);
  buflen := len;
  ix := 0;
  while ix < buflen do begin
    ix := ix + Recv(s, buf[Low(buf) + ix], buflen - ix, 0);
  end;
  SetString(v2, PAnsiChar(@buf[Low(buf)]), len);
  writeln(v2);
  CloseSocket(s);
  WSACleanup;
end.

Singlethreaded server:

Singlethreaded server logic is:

listen
while
    accept
    receive/read
    send/write
    close
endwhile

Socket and ServerSocket classes has been in Java since version 1.0. Java also have DataInputStream and DataOutputStream classes for convenient reading and writing binary data.

Note that DataInputStream and DataOutputStream always use big endian.

package socket.plain.binary.traditional;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class SingleServer {
    private static final int PORT = 12345;
    public static void main(String[] args) {
        try {
            // listen on port
            @SuppressWarnings("resource")
            ServerSocket ss = new ServerSocket(PORT);
            while(true) {
                // accept connection
                Socket s = ss.accept();
                System.out.printf("(connection from %s)\n", s.getRemoteSocketAddress());
                // get streams
                DataInputStream dis = new DataInputStream(s.getInputStream());
                DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                // read a and b
                int a = dis.readInt();
                int b = dis.readInt();
                // calculate c and send
                int c = a + b;
                dos.writeInt(c);
                dos.flush();
                // read v
                int buflen = dis.readByte();
                byte[] buf = new byte[buflen];
                int ix = 0;
                while(ix < buf.length) {
                    ix += dis.read(buf, ix, buf.length - ix);
                }
                String v = new String(buf, "UTF-8");
                // calculate v2 and send
                String v2 = v + v;
                byte[] v2b = v2.getBytes("UTF-8");
                dos.writeByte(v2b.length);
                dos.write(v2b);
                dos.flush();
                // close connection
                dis.close();
                dos.close();
                s.close();
            }
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
}

Java 1.4 added several new IO features called NIO including a SocketChannel. It is not that much of an improvement for single threaded server except that is supports both little and big endian.

package socket.plain.binary.modern;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class SingleServer {
    private static final int PORT = 12345;
    public static void main(String[] args) {
        try {
            // listen on port
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.bind(new InetSocketAddress(PORT));
            while(true) {
                // accept connection
                SocketChannel sc = ssc.accept();
                System.out.printf("(connection from %s)\n", sc.getRemoteAddress());
                ByteBuffer bb;
                // read a and b
                bb = ByteBuffer.allocate(8);
                while(bb.hasRemaining() && sc.read(bb) > 0);
                bb.rewind();
                bb.order(ByteOrder.BIG_ENDIAN);
                int a = bb.getInt();
                int b = bb.getInt();
                // calculate c and send
                int c = a + b;
                bb = ByteBuffer.allocate(4);
                bb.order(ByteOrder.BIG_ENDIAN);
                bb.putInt(c);
                bb.rewind();
                sc.write(bb);
                // read v
                bb = ByteBuffer.allocate(1);
                sc.read(bb);
                bb.rewind();
                int buflen = bb.get();
                bb = ByteBuffer.allocate(buflen);
                while(bb.hasRemaining() && sc.read(bb) > 0);
                bb.rewind();
                byte[] buf = new byte[buflen];
                bb.get(buf);
                String v = new String(buf, "UTF-8");
                // calculate v2 and send
                String v2 = v + v;
                byte[] v2b = v2.getBytes("UTF-8");
                bb = ByteBuffer.allocate(1 + v2b.length);
                bb.put((byte)v2b.length);
                bb.put(v2b);
                bb.rewind();
                sc.write(bb);
                // close connection
                sc.close();
            }
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
}

.NET provide two levels of socket API:

This is the low level API.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SocketDemo.Binary.Low.SingleServer
{
    public class Program
    {
        private static byte[] IntToBytes(int v)
        {
            byte[] buf = BitConverter.GetBytes(v);
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return buf;
        }
        private static int BytesToInt(byte[] buf)
        {
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return BitConverter.ToInt32(buf, 0);
        }
        private const int PORT = 12345;
        private const int BACKLOG = 100;
        public static void Main(string[] args)
        {
            // listen on port
            Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ss.Bind(new IPEndPoint(IPAddress.Any, PORT));
            ss.Listen(BACKLOG);
            while(true) {
                // accept connection
                Socket s = ss.Accept();
                Console.WriteLine("(connection from {0})", s.RemoteEndPoint);
                byte[] buf;
                int ix;
                // read a and b
                buf = new Byte[4];
                ix = 0;
                while(ix < buf.Length)
                {
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
                }
                int a = BytesToInt(buf);
                buf = new Byte[4];
                ix = 0;
                while(ix < buf.Length)
                {
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
                }
                int b = BytesToInt(buf);
                // calculate c and send
                int c = a + b;
                s.Send(IntToBytes(c));
                // read v
                buf = new Byte[1];
                s.Receive(buf);
                int buflen = buf[0];
                buf = new byte[buflen];
                ix = 0;
                while(ix < buf.Length)
                {
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
                }
                string v = Encoding.UTF8.GetString(buf);
                // calculate v2 and send
                string v2 = v + v;
                byte[] v2b = Encoding.UTF8.GetBytes(v2);
                s.Send(new byte[] { (byte)v2b.Length });
                s.Send(v2b);
                // close connection
                s.Close();
            }
        }
    }
}

.NET provide two levels of socket API:

This is the high level API.

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SocketDemo.Binary.High.SingleServer
{
    public class Program
    {
        private static int HTON(int v)
        {
            if(BitConverter.IsLittleEndian)
            {
                byte[] buf = BitConverter.GetBytes(v);
                Array.Reverse(buf);
                return BitConverter.ToInt32(buf, 0);
            }
            else
            {
                return v;
            }
        }
        private static int NTOH(int v)
        {
            if(BitConverter.IsLittleEndian)
            {
                byte[] buf = BitConverter.GetBytes(v);
                Array.Reverse(buf);
                return BitConverter.ToInt32(buf, 0);
            }
            else
            {
                return v;
            }
        }
        private const int PORT = 12345;
        private const int BACKLOG = 100;
        public static void Main(string[] args)
        {
            // listen on port
            TcpListener srv = new TcpListener(IPAddress.Any, PORT);
            srv.Start(BACKLOG);
            while(true) {
                // accept connection
                TcpClient cli = srv.AcceptTcpClient();
                // get reader and writer
                NetworkStream stm = cli.GetStream();
                BinaryReader br = new BinaryReader(stm);
                BinaryWriter bw = new BinaryWriter(stm);
                Console.WriteLine("(connection from {0})", cli.Client.RemoteEndPoint);
                // read a and b
                int a = NTOH(br.ReadInt32());
                int b = NTOH(br.ReadInt32());
                // calculate c and send
                int c = a + b;
                bw.Write(HTON(c));
                bw.Flush();
                // read v
                int buflen = br.ReadByte();
                byte[] buf = new byte[buflen];
                int ix = 0;
                while(ix < buf.Length)
                {
                    ix += br.Read(buf, ix, buf.Length - ix);
                }
                string v = Encoding.UTF8.GetString(buf);
                // calculate v2 and send
                string v2 = v + v;
                byte[] v2b = Encoding.UTF8.GetBytes(v2);
                bw.Write((byte)v2b.Length);
                bw.Write(v2b);
                bw.Flush();
                // close connection
                br.Close();
                bw.Close();
                cli.Close();
            }
        }
    }
}

.NET provide two levels of socket API:

This is the low level API.

Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Namespace SocketDemo.Binary.Low.SingleServer
    Public Class Program
        Private Shared Function IntToBytes(v As Integer) As Byte()
            Dim buf As Byte() = BitConverter.GetBytes(v)
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return buf
        End Function
        Private Shared Function BytesToInt(buf As Byte()) As Integer
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return BitConverter.ToInt32(buf, 0)
        End Function
        Private Const PORT As Integer = 12345
        Private Const BACKLOG As Integer = 100
        Public Shared Sub Main(args As String())
            ' listen on port
            Dim ss As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            ss.Bind(New IPEndPoint(IPAddress.Any, PORT))
            ss.Listen(BACKLOG)
            While True
                ' accept connection
                Dim s As Socket = ss.Accept()
                Console.WriteLine("(connection from {0})", s.RemoteEndPoint)
                Dim buf As Byte()
                Dim ix As Integer
                ' read a and b
                buf = New [Byte](3) {}
                ix = 0
                While ix < buf.Length
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
                End While
                Dim a As Integer = BytesToInt(buf)
                buf = New [Byte](3) {}
                ix = 0
                While ix < buf.Length
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
                End While
                Dim b As Integer = BytesToInt(buf)
                ' calculate c and send
                Dim c As Integer = a + b
                s.Send(IntToBytes(c))
                ' read v
                buf = New [Byte](0) {}
                s.Receive(buf)
                Dim buflen As Integer = buf(0)
                buf = New Byte(buflen - 1) {}
                ix = 0
                While ix < buf.Length
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
                End While
                Dim v As String = Encoding.UTF8.GetString(buf)
                ' calculate v2 and send
                Dim v2 As String = v & v
                Dim v2b As Byte() = Encoding.UTF8.GetBytes(v2)
                s.Send(New Byte() {CByte(v2b.Length)})
                s.Send(v2b)
                ' close connection
                s.Close()
            End While
        End Sub
    End Class
End Namespace

.NET provide two levels of socket API:

This is the high level API.

Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Namespace SocketDemo.Binary.High.SingleServer
    Public Class Program
        Private Shared Function HTON(v As Integer) As Integer
            If BitConverter.IsLittleEndian Then
                Dim buf As Byte() = BitConverter.GetBytes(v)
                Array.Reverse(buf)
                Return BitConverter.ToInt32(buf, 0)
            Else
                Return v
            End If
        End Function
        Private Shared Function NTOH(v As Integer) As Integer
            If BitConverter.IsLittleEndian Then
                Dim buf As Byte() = BitConverter.GetBytes(v)
                Array.Reverse(buf)
                Return BitConverter.ToInt32(buf, 0)
            Else
                Return v
            End If
        End Function
        Private Const PORT As Integer = 12345
        Private Const BACKLOG As Integer = 100
        Public Shared Sub Main(args As String())
            ' listen on port
            Dim srv As New TcpListener(IPAddress.Any, PORT)
            srv.Start(BACKLOG)
            While True
                ' accept connection
                Dim cli As TcpClient = srv.AcceptTcpClient()
                ' get reader and writer
                Dim stm As NetworkStream = cli.GetStream()
                Dim br As New BinaryReader(stm)
                Dim bw As New BinaryWriter(stm)
                Console.WriteLine("(connection from {0})", cli.Client.RemoteEndPoint)
                ' read a and b
                Dim a As Integer = NTOH(br.ReadInt32())
                Dim b As Integer = NTOH(br.ReadInt32())
                ' calculate c and send
                Dim c As Integer = a + b
                bw.Write(HTON(c))
                bw.Flush()
                ' read v
                Dim buflen As Integer = br.ReadByte()
                Dim buf As Byte() = New Byte(buflen - 1) {}
                Dim ix As Integer = 0
                While ix < buf.Length
                    ix += br.Read(buf, ix, buf.Length - ix)
                End While
                Dim v As String = Encoding.UTF8.GetString(buf)
                ' calculate v2 and send
                Dim v2 As String = v & v
                Dim v2b As Byte() = Encoding.UTF8.GetBytes(v2)
                bw.Write(CByte(v2b.Length))
                bw.Write(v2b)
                bw.Flush()
                ' close connection
                br.Close()
                bw.Close()
                cli.Close()
            End While
        End Sub
    End Class
End Namespace

The C API has changed regarding how to lookup information. There is an old style which was created for IPv4 and is known to have some problems with IPv6 - and there is a new style which was created to support both IPv4 and IPv6 nicely nicely.

Always use the new style when writing new code. The old style is only shown to understand existing programs or to develop on very old systems.

This example shows the old style.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define PORT 12345
#define BACKLOG 100

int main()
{
    int sd, sd2, status, ix, buflen, slen;
    long int a, b, c, tmp;
    char *buf, *v, *v2, len;
    struct sockaddr_in local, remote;
#ifdef WIN32
    WSADATA WSAData;
    if(WSAStartup(0x0101, &WSAData) != 0)
    {
        printf("Error initializing\n");
        exit(0);
    }
#endif
    /* create socket */
    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* bind */
    local.sin_family = AF_INET;
    local.sin_port = htons(PORT);
    local.sin_addr.s_addr = INADDR_ANY;
    status = bind(sd, (struct sockaddr *)&local ,sizeof(local));
    if(status < 0)
    {
        printf("Error binding socket: %s\n", strerror(errno));
        exit(0);
    }
    /* listen */
    status = listen(sd, BACKLOG);
    if(status < 0)
    {
        printf("Error listening socket: %s\n", strerror(errno));
        exit(0);
    }
    for(;;)
    {
        /* accept */
        sd2 = accept(sd, 0, 0);
        if(sd2 < 0)
        {
            printf("Error accepting socket: %s\n", strerror(errno));
            exit(0);
        }
        slen = sizeof(remote);
        getpeername(sd2, (struct sockaddr *)&remote, &slen);
        printf("(connection from %s)\n", inet_ntoa(remote.sin_addr));
        /* read a and b */
        buflen = sizeof(tmp);
        ix = 0;
        while(ix < buflen)
        {
            ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
        }
        a = ntohl(tmp);
        buflen = sizeof(tmp);
        ix = 0;
        while(ix < buflen)
        {
            ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
        }
        b = ntohl(tmp);
        /* calculate c and send */
        c = a + b;
        tmp = htonl(c);
        status = send(sd2, (char *)&tmp, sizeof(tmp), 0);
        if(status < 0)
        {
            printf("Error sending c: %s\n", strerror(errno));
            exit(0);
        }
        /* read v */
        recv(sd2, &len, sizeof(len), 0);
        buflen = len;
        buf = malloc(buflen);
        ix = 0;
        while(ix < buflen)
        {
            ix = ix + recv(sd2, buf + ix, buflen - ix, 0);
        }
        v = malloc(buflen + 1);
        memcpy(v, buf, buflen);
        v[buflen] = '\0';
        free(buf);
        /* calculate v2 and send */
        v2 = malloc(2 * strlen(v) + 1);
        strcpy(v2, v);
        strcat(v2, v);
        len = strlen(v2);
        status = send(sd2, &len, sizeof(len), 0);
        if(status < 0)
        {
            printf("Error sending v2: %s\n", strerror(errno));
            exit(0);
        }
        status = send(sd2, v2, len, 0);
        if(status < 0)
        {
            printf("Error sending v2: %s\n", strerror(errno));
            exit(0);
        }
        free(v);
        free(v2);
        /* close socket */
#ifdef WIN32
        closesocket(sd2);
#else
        close(sd2);
#endif
    }
    return 0;
}

The C API has changed regarding how to lookup information. There is an old style which was created for IPv4 and is known to have some problems with IPv6 - and there is a new style which was created to support both IPv4 and IPv6 nicely nicely.

Always use the new style when writing new code. The old style is only shown to understand existing programs or to develop on very old systems.

This example shows the new style.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define PORT "12345"
#define BACKLOG 100

int main()
{
    int sd, sd2, status, ix, buflen, slen;
    long int a, b, c, tmp;
    char *buf, *v, *v2, len, addr[47];
    struct sockaddr remote;
    struct addrinfo hints, *res;
#ifdef WIN32
    WSADATA WSAData;
    if(WSAStartup(0x0101, &WSAData) != 0)
    {
        printf("Error initializing\n");
        exit(0);
    }
#endif
    /* find port */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET; //AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
    status = getaddrinfo(NULL, PORT, &hints, &res);
    if(status != 0)
    {
        printf("Error finding port: %s\n", PORT);
        exit(0);
    }
    /* create socket */
    sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* bind */
    status = bind(sd, res->ai_addr, res->ai_addrlen);
    if(status < 0)
    {
        printf("Error binding socket: %s\n", strerror(errno));
        exit(0);
    }
    freeaddrinfo(res);
    /* listen */
    status = listen(sd, BACKLOG);
    if(status < 0)
    {
        printf("Error listening socket: %s\n", strerror(errno));
        exit(0);
    }
    for(;;)
    {
        /* accept */
        sd2 = accept(sd, 0, 0);
        if(sd2 < 0)
        {
            printf("Error accepting socket: %s\n", strerror(errno));
            exit(0);
        }
        slen = sizeof(remote);
        getpeername(sd2, &remote, &slen);
        if(remote.sa_family == AF_INET)
            printf("(connection from %s)\n", inet_ntop(AF_INET, &((struct sockaddr_in *)&remote)->sin_addr, addr, sizeof(addr)));
        if(remote.sa_family == AF_INET6)
            printf("(connection from %s)\n", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&remote)->sin6_addr, addr, sizeof(addr)));
        /* read a and b */
        buflen = sizeof(tmp);
        ix = 0;
        while(ix < buflen)
        {
            ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
        }
        a = ntohl(tmp);
        buflen = sizeof(tmp);
        ix = 0;
        while(ix < buflen)
        {
            ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
        }
        b = ntohl(tmp);
        /* calculate c and send */
        c = a + b;
        tmp = htonl(c);
        status = send(sd2, (char *)&tmp, sizeof(tmp), 0);
        if(status < 0)
        {
            printf("Error sending c: %s\n", strerror(errno));
            exit(0);
        }
        /* read v */
        recv(sd2, &len, sizeof(len), 0);
        buflen = len;
        buf = malloc(buflen);
        ix = 0;
        while(ix < buflen)
        {
            ix = ix + recv(sd2, buf + ix, buflen - ix, 0);
        }
        v = malloc(buflen + 1);
        memcpy(v, buf, buflen);
        v[buflen] = '\0';
        free(buf);
        /* calculate v2 and send */
        v2 = malloc(2 * strlen(v) + 1);
        strcpy(v2, v);
        strcat(v2, v);
        len = strlen(v2);
        status = send(sd2, &len, sizeof(len), 0);
        if(status < 0)
        {
            printf("Error sending v2: %s\n", strerror(errno));
            exit(0);
        }
        status = send(sd2, v2, len, 0);
        if(status < 0)
        {
            printf("Error sending v2: %s\n", strerror(errno));
            exit(0);
        }
        free(v);
        free(v2);
        /* close socket */
#ifdef WIN32
        closesocket(sd2);
#else
        close(sd2);
#endif
    }
    return 0;
}

Indy 10 has a convenient TIdTcpServer class.

Indy does server sligtly different than other libraries.

program PlainBinSingleServerHigh;

uses
  IdTCPServer, IdContext;

const
  PORT = 12345;
  BACKLOG = 100;

type
  ClientHandler = class
    public
      procedure Run(ctx : TIdContext);
  end;

procedure ClientHandler.Run(ctx : TIdContext);

var
  v, v2 : string;
  a, b, c, len : integer;

begin
  writeln('(connection from ', ctx.Connection.Socket.Binding.PeerIP, ':', ctx.Connection.Socket.Binding.PeerPort, ')');
  (* read a and b *)
  a := ctx.Connection.Socket.ReadInt32(true);
  b := ctx.Connection.Socket.ReadInt32(true);
  (* calculate c and send *)
  c := a + b;
  ctx.Connection.Socket.Write(c, true);
  (* read v *)
  len := ctx.Connection.Socket.ReadByte;
  v :=  ctx.Connection.Socket.ReadString(len);
  (* calculate v2 and send *)
  v2 := v + v;
  ctx.Connection.Socket.Write(Byte(length(v2)));
  ctx.Connection.Socket.Write(v2);
  (* close connection *)
  ctx.Connection.Disconnect;
end;

var
  srv : TIdTCPServer;
  h : ClientHandler;

begin
  (* listen on port *)
  srv := TIdTCPServer.Create();
  srv.ListenQueue := BACKLOG;
  srv.Bindings.Add.Port := PORT;
  srv.StartListening;
  (* accept connection *)
  h := ClientHandler.Create;
  srv.OnExecute := @h.Run;
  srv.Active := true;
  readln;
  (* close connection *)
  srv.Free;
end.

Besides Indy it is also possible to use plain WinSock.

program PlainBinSingleServerLow;

uses
  SysUtils,
  Windows,
  WinSock;

const
  PORT = 12345;
  BACKLOG = 100;

var
  wsa : TWSAData;
  s, s2 : TSocket;
  local, remote : TSockAddrIn;
  v, v2 : string;
  status, slen, a, b, c, tmp, ix, buflen : integer;
  buf : array of byte;
  len : byte;

begin
  WSAStartup($101, wsa);
  (* create socket *)
  s := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if s = INVALID_SOCKET then begin
     writeln('Error creating socket: ' + SysErrorMessage(GetLastError));
     halt;
  end;
  (* bind *)
  local.sin_family := AF_INET;
  local.sin_port :=  htons(PORT);
  local.sin_addr.S_addr := INADDR_ANY;
  status := bind(s, local, sizeof(local));
  if status <> 0 then begin
    writeln('Error binding socket: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  (* listen *)
  status := listen(s, BACKLOG);
  if status <> 0 then begin
    writeln('Error listening socket: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  while true do begin
    (* accept *)
    slen := sizeof(remote);
    s2 := Accept(s, @remote, slen);
    writeln('(connection from ' + inet_ntoa(remote.sin_addr) + ')');
    (* read a and b *)
    buflen := sizeof(tmp);
    ix := 0;
    while ix < buflen do begin
      ix := ix + Recv(s2, PChar(@tmp) + ix, buflen - ix, 0);
    end;
    a := ntohl(tmp);
    buflen := sizeof(tmp);
    ix := 0;
    while ix < buflen do begin
      ix := ix + Recv(s2, PChar(@tmp) + ix, buflen - ix, 0);
    end;
    b := ntohl(tmp);
    (* calculate c and send *)
    c := a + b;
    tmp := htonl(c);
    status := Send(s2, @tmp, sizeof(tmp), 0);
    if status < 0 then begin
      writeln('Error sending c: ' + SysErrorMessage(GetLastError));
      halt;
    end;
    (* read v *)
    Recv(s2, @len, sizeof(len), 0);
    SetLength(buf, len);
    buflen := len;
    ix := 0;
    while ix < buflen do begin
      ix := ix + Recv(s2, buf[Low(buf) + ix], buflen - ix, 0);
    end;
    SetString(v, PAnsiChar(@buf[Low(buf)]), len);
    (* calculate v2 and send *)
    v2 := v + v;
    len := Length(v2);
    status := Send(s2, len, sizeof(len), 0);
    if status < 0 then begin
      writeln('Error sending v2: ' + SysErrorMessage(GetLastError));
      halt;
    end;
    status := Send(s2, v2[1], len, 0);
    if status < 0 then begin
      writeln('Error sending v2: ' + SysErrorMessage(GetLastError));
      halt;
    end;
    CloseSocket(s2);
  end;
  CloseSocket(s);
  WSACleanup;
end.

Multithreaded server:

Multithreaded server logic is:

function handler
    receive/read
    send/write
    close
endfunction

listen
while
    accept
    start thread with handler
endwhile

Socket and ServerSocket classes has been in Java since version 1.0. Java also have DataInputStream and DataOutputStream classes for convenient reading and writing binary data.

Note that DataInputStream and DataOutputStream always use big endian.

package socket.plain.binary.traditional;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MultiServer {
    public static class ClientHandler extends Thread {
        private Socket s;
        public ClientHandler(Socket s) {
            this.s = s;
        }
        @Override
        public void run() {
            try {
                System.out.printf("(handler in thread %d)\n", Thread.currentThread().getId());
                // get streams
                DataInputStream dis = new DataInputStream(s.getInputStream());
                DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                // read a and b
                int a = dis.readInt();
                int b = dis.readInt();
                // calculate c and send
                int c = a + b;
                dos.writeInt(c);
                dos.flush();
                // read v
                int buflen = dis.readByte();
                byte[] buf = new byte[buflen];
                int ix = 0;
                while(ix < buf.length) {
                    ix += dis.read(buf, ix, buf.length - ix);
                }
                String v = new String(buf, "UTF-8");
                // calculate v2 and send
                String v2 = v + v;
                byte[] v2b = v2.getBytes("UTF-8");
                dos.writeByte(v2b.length);
                dos.write(v2b);
                dos.flush();
                // close connection
                dis.close();
                dos.close();
                s.close();
            } catch (IOException e) {
                // TODO
                e.printStackTrace();
            }
        }
    }
    private static final int PORT = 12345;
    public static void main(String[] args) {
        try {
            // listen on port
            @SuppressWarnings("resource")
            ServerSocket ss = new ServerSocket(PORT);
            while(true) {
                // accept connection
                Socket s = ss.accept();
                System.out.printf("(connection from %s)\n", s.getRemoteSocketAddress());
                // start new thread to handle connection
                (new ClientHandler(s)).start();
            }
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
}

Java 1.4 added several new IO features called NIO including a SocketChannel. It is not that much of an improvement for multithreaded server except that is supports both little and big endian.

package socket.plain.binary.modern;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class MultiServer {
    public static class ClientHandler extends Thread {
        private SocketChannel sc;
        public ClientHandler(SocketChannel sc) {
            this.sc = sc;
        }
        @Override
        public void run() {
            try {
                System.out.printf("(handler in thread %d)\n", Thread.currentThread().getId());
                ByteBuffer bb;
                // read a and b
                bb = ByteBuffer.allocate(8);
                while(bb.hasRemaining() && sc.read(bb) > 0);
                bb.rewind();
                bb.order(ByteOrder.BIG_ENDIAN);
                int a = bb.getInt();
                int b = bb.getInt();
                // calculate c and send
                int c = a + b;
                bb = ByteBuffer.allocate(4);
                bb.order(ByteOrder.BIG_ENDIAN);
                bb.putInt(c);
                bb.rewind();
                sc.write(bb);
                // read v
                bb = ByteBuffer.allocate(1);
                sc.read(bb);
                bb.rewind();
                int buflen = bb.get();
                bb = ByteBuffer.allocate(buflen);
                while(bb.hasRemaining() && sc.read(bb) > 0);
                bb.rewind();
                byte[] buf = new byte[buflen];
                bb.get(buf);
                String v = new String(buf, "UTF-8");
                // calculate v2 and send
                String v2 = v + v;
                byte[] v2b = v2.getBytes("UTF-8");
                bb = ByteBuffer.allocate(1 + v2b.length);
                bb.put((byte)v2b.length);
                bb.put(v2b);
                bb.rewind();
                sc.write(bb);
                // close connection
                sc.close();
            } catch (IOException e) {
                // TODO
                e.printStackTrace();
            }
        }
    }
    private static final int PORT = 12345;
    public static void main(String[] args) {
        try {
            // listen on port
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.bind(new InetSocketAddress(PORT));
            while(true) {
                // accept connection
                SocketChannel sc = ssc.accept();
                System.out.printf("(connection from %s)\n", sc.getRemoteAddress());
                // start new thread to handle connection
                (new ClientHandler(sc)).start();
            }
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
}

.NET provide two levels of socket API:

This is the low level API.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace SocketDemo.Binary.Low.MultiServer
{
    public class Program
    {
        private static byte[] IntToBytes(int v)
        {
            byte[] buf = BitConverter.GetBytes(v);
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return buf;
        }
        private static int BytesToInt(byte[] buf)
        {
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return BitConverter.ToInt32(buf, 0);
        }
        private class ClientHandler
        {
            private Socket s;
            public ClientHandler(Socket s)
            {
                this.s = s;
            }
            public void Run()
            {
                Console.WriteLine("(handler in thread {0})", Thread.CurrentThread.ManagedThreadId);
                byte[] buf;
                int ix;
                // read a and b
                buf = new Byte[4];
                ix = 0;
                while(ix < buf.Length)
                {
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
                }
                int a = BytesToInt(buf);
                buf = new Byte[4];
                ix = 0;
                while(ix < buf.Length)
                {
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
                }
                int b = BytesToInt(buf);
                // calculate c and send
                int c = a + b;
                s.Send(IntToBytes(c));
                // read v
                buf = new Byte[1];
                s.Receive(buf);
                int buflen = buf[0];
                buf = new byte[buflen];
                ix = 0;
                while(ix < buf.Length)
                {
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
                }
                string v = Encoding.UTF8.GetString(buf);
                // calculate v2 and send
                string v2 = v + v;
                byte[] v2b = Encoding.UTF8.GetBytes(v2);
                s.Send(new byte[] { (byte)v2b.Length });
                s.Send(v2b);
                // close connection
                s.Close();
            }
        }
        private const int PORT = 12345;
        private const int BACKLOG = 100;
        public static void Main(string[] args)
        {
            // listen on port
            Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ss.Bind(new IPEndPoint(IPAddress.Any, PORT));
            ss.Listen(BACKLOG);
            while(true) {
                // accept connection
                Socket s = ss.Accept();
                Console.WriteLine("(connection from {0})", s.RemoteEndPoint);
                (new Thread((new ClientHandler(s)).Run)).Start();
            }
        }
    }
}

.NET provide two levels of socket API:

This is the high level API.

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace SocketDemo.Binary.High.MultiServer
{
    public class Program
    {
        private static int HTON(int v)
        {
            if(BitConverter.IsLittleEndian)
            {
                byte[] buf = BitConverter.GetBytes(v);
                Array.Reverse(buf);
                return BitConverter.ToInt32(buf, 0);
            }
            else
            {
                return v;
            }
        }
        private static int NTOH(int v)
        {
            if(BitConverter.IsLittleEndian)
            {
                byte[] buf = BitConverter.GetBytes(v);
                Array.Reverse(buf);
                return BitConverter.ToInt32(buf, 0);
            }
            else
            {
                return v;
            }
        }
        private class ClientHandler
        {
            private TcpClient cli;
            public ClientHandler(TcpClient cli)
            {
                this.cli = cli;
            }
            public void Run()
            {
                Console.WriteLine("(handler in thread {0})", Thread.CurrentThread.ManagedThreadId);
                // get reader and writer
                NetworkStream stm = cli.GetStream();
                BinaryReader br = new BinaryReader(stm);
                BinaryWriter bw = new BinaryWriter(stm);
                // read a and b
                int a = NTOH(br.ReadInt32());
                int b = NTOH(br.ReadInt32());
                // calculate c and send
                int c = a + b;
                bw.Write(HTON(c));
                bw.Flush();
                // read v
                int buflen = br.ReadByte();
                byte[] buf = new byte[buflen];
                int ix = 0;
                while(ix < buf.Length)
                {
                    ix += br.Read(buf, ix, buf.Length - ix);
                }
                string v = Encoding.UTF8.GetString(buf);
                // calculate v2 and send
                string v2 = v + v;
                byte[] v2b = Encoding.UTF8.GetBytes(v2);
                bw.Write((byte)v2b.Length);
                bw.Write(v2b);
                bw.Flush();
                // close connection
                br.Close();
                bw.Close();
                cli.Close();
            }
        }
        private const int PORT = 12345;
        private const int BACKLOG = 100;
        public static void Main(string[] args)
        {
            // listen on port
            TcpListener srv = new TcpListener(IPAddress.Any, PORT);
            srv.Start(BACKLOG);
            while(true) {
                // accept connection
                TcpClient cli = srv.AcceptTcpClient();
                Console.WriteLine("(connection from {0})", cli.Client.RemoteEndPoint);
                (new Thread((new ClientHandler(cli)).Run)).Start();
            }
        }
    }
}

.NET provide two levels of socket API:

This is the low level API.

Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading

Namespace SocketDemo.Binary.Low.MultiServer
    Public Class Program
        Private Shared Function IntToBytes(v As Integer) As Byte()
            Dim buf As Byte() = BitConverter.GetBytes(v)
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return buf
        End Function
        Private Shared Function BytesToInt(buf As Byte()) As Integer
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return BitConverter.ToInt32(buf, 0)
        End Function
        Private Class ClientHandler
            Private s As Socket
            Public Sub New(s As Socket)
                Me.s = s
            End Sub
            Public Sub Run()
                Console.WriteLine("(handler in thread {0})", Thread.CurrentThread.ManagedThreadId)
                Dim buf As Byte()
                Dim ix As Integer
                ' read a and b
                buf = New [Byte](3) {}
                ix = 0
                While ix < buf.Length
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
                End While
                Dim a As Integer = BytesToInt(buf)
                buf = New [Byte](3) {}
                ix = 0
                While ix < buf.Length
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
                End While
                Dim b As Integer = BytesToInt(buf)
                ' calculate c and send
                Dim c As Integer = a + b
                s.Send(IntToBytes(c))
                ' read v
                buf = New [Byte](0) {}
                s.Receive(buf)
                Dim buflen As Integer = buf(0)
                buf = New Byte(buflen - 1) {}
                ix = 0
                While ix < buf.Length
                    ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
                End While
                Dim v As String = Encoding.UTF8.GetString(buf)
                ' calculate v2 and send
                Dim v2 As String = v & v
                Dim v2b As Byte() = Encoding.UTF8.GetBytes(v2)
                s.Send(New Byte() {CByte(v2b.Length)})
                s.Send(v2b)
                ' close connection
                s.Close()
            End Sub
        End Class
        Private Const PORT As Integer = 12345
        Private Const BACKLOG As Integer = 100
        Public Shared Sub Main(args As String())
            ' listen on port
            Dim ss As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            ss.Bind(New IPEndPoint(IPAddress.Any, PORT))
            ss.Listen(BACKLOG)
            While True
                ' accept connection
                Dim s As Socket = ss.Accept()
                Console.WriteLine("(connection from {0})", s.RemoteEndPoint)
                Dim t As Thread = New Thread(AddressOf (New ClientHandler(s)).Run)
                t.Start()
            End While
        End Sub
    End Class
End Namespace

.NET provide two levels of socket API:

This is the high level API.

Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading

Namespace SocketDemo.Binary.High.MultiServer
    Public Class Program
        Private Shared Function HTON(v As Integer) As Integer
            If BitConverter.IsLittleEndian Then
                Dim buf As Byte() = BitConverter.GetBytes(v)
                Array.Reverse(buf)
                Return BitConverter.ToInt32(buf, 0)
            Else
                Return v
            End If
        End Function
        Private Shared Function NTOH(v As Integer) As Integer
            If BitConverter.IsLittleEndian Then
                Dim buf As Byte() = BitConverter.GetBytes(v)
                Array.Reverse(buf)
                Return BitConverter.ToInt32(buf, 0)
            Else
                Return v
            End If
        End Function
        Private Class ClientHandler
            Private cli As TcpClient
            Public Sub New(cli As TcpClient)
                Me.cli = cli
            End Sub
            Public Sub Run()
                Console.WriteLine("(handler in thread {0})", Thread.CurrentThread.ManagedThreadId)
                ' get reader and writer
                Dim stm As NetworkStream = cli.GetStream()
                Dim br As New BinaryReader(stm)
                Dim bw As New BinaryWriter(stm)
                ' read a and b
                Dim a As Integer = NTOH(br.ReadInt32())
                Dim b As Integer = NTOH(br.ReadInt32())
                ' calculate c and send
                Dim c As Integer = a + b
                bw.Write(HTON(c))
                bw.Flush()
                ' read v
                Dim buflen As Integer = br.ReadByte()
                Dim buf As Byte() = New Byte(buflen - 1) {}
                Dim ix As Integer = 0
                While ix < buf.Length
                    ix += br.Read(buf, ix, buf.Length - ix)
                End While
                Dim v As String = Encoding.UTF8.GetString(buf)
                ' calculate v2 and send
                Dim v2 As String = v & v
                Dim v2b As Byte() = Encoding.UTF8.GetBytes(v2)
                bw.Write(CByte(v2b.Length))
                bw.Write(v2b)
                bw.Flush()
                ' close connection
                br.Close()
                bw.Close()
                cli.Close()
            End Sub
        End Class
        Private Const PORT As Integer = 12345
        Private Const BACKLOG As Integer = 100
        Public Shared Sub Main(args As String())
            ' listen on port
            Dim srv As New TcpListener(IPAddress.Any, PORT)
            srv.Start(BACKLOG)
            While True
                ' accept connection
                Dim cli As TcpClient = srv.AcceptTcpClient()
                Console.WriteLine("(connection from {0})", cli.Client.RemoteEndPoint)
                Dim t As Thread = New Thread(AddressOf (New ClientHandler(cli)).Run)
                t.Start()
            End While
        End Sub
    End Class
End Namespace

I will not show C examples for multithreaded server. Multithreading is not standardized in C. Windows uses Windows threads. Most other OS'es use POSIX threads. An example would be more a threading example than a socket example.

To read about threads see here.

The C API has changed regarding how to lookup information. There is an old style which was created for IPv4 and is known to have some problems with IPv6 - and there is a new style which was created to support both IPv4 and IPv6 nicely nicely.

Always use the new style when writing new code. The old style is only shown to understand existing programs or to develop on very old systems.

I will not show C examples for multithreaded server. Multithreading is not standardized in older versions of C. Windows uses Windows threads. Most other OS'es use POSIX threads. An example would be more a threading example than a socket example.

Besides Indy it is also possible to use plain WinSock.

program PlainBinMultiServerLow;

uses
  Classes,
  SysUtils,
  Windows,
  WinSock;

type
  ClientHandler = class(TThread)
    constructor Create(s2 : TSocket);
  protected
    procedure Execute; override;
  private
    _s2 : TSocket;
  end;

constructor ClientHandler.Create(s2 : TSocket);

begin
  inherited Create(false);
  _s2 := s2;
end;

procedure ClientHandler.Execute;

var
  v, v2 : string;
  status, a, b, c, tmp, ix, buflen : integer;
  buf : array of byte;
  len : byte;

begin
  (* read a and b *)
  buflen := sizeof(tmp);
  ix := 0;
  while ix < buflen do begin
    ix := ix + Recv(_s2, PChar(@tmp) + ix, buflen - ix, 0);
  end;
  a := ntohl(tmp);
  buflen := sizeof(tmp);
  ix := 0;
  while ix < buflen do begin
    ix := ix + Recv(_s2, PChar(@tmp) + ix, buflen - ix, 0);
  end;
  b := ntohl(tmp);
  (* calculate c and send *)
  c := a + b;
  tmp := htonl(c);
  status := Send(_s2, @tmp, sizeof(tmp), 0);
  if status < 0 then begin
    writeln('Error sending c: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  (* read v *)
  Recv(_s2, @len, sizeof(len), 0);
  SetLength(buf, len);
  buflen := len;
  ix := 0;
  while ix < buflen do begin
    ix := ix + Recv(_s2, buf[Low(buf) + ix], buflen - ix, 0);
  end;
  SetString(v, PAnsiChar(@buf[Low(buf)]), len);
  (* calculate v2 and send *)
  v2 := v + v;
  len := Length(v2);
  status := Send(_s2, len, sizeof(len), 0);
  if status < 0 then begin
    writeln('Error sending v2: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  status := Send(_s2, v2[1], len, 0);
  if status < 0 then begin
    writeln('Error sending v2: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  CloseSocket(_s2);
  Self.Free;
end;

const
  PORT = 12345;
  BACKLOG = 100;

var
  wsa : TWSAData;
  s, s2 : TSocket;
  local, remote : TSockAddrIn;
  status, slen : integer;

begin
  WSAStartup($101, wsa);
  (* create socket *)
  s := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if s = INVALID_SOCKET then begin
     writeln('Error creating socket: ' + SysErrorMessage(GetLastError));
     halt;
  end;
  (* bind *)
  local.sin_family := AF_INET;
  local.sin_port :=  htons(PORT);
  local.sin_addr.S_addr := INADDR_ANY;
  status := bind(s, local, sizeof(local));
  if status <> 0 then begin
    writeln('Error binding socket: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  (* listen *)
  status := listen(s, BACKLOG);
  if status <> 0 then begin
    writeln('Error listening socket: ' + SysErrorMessage(GetLastError));
    halt;
  end;
  while true do begin
    (* accept *)
    slen := sizeof(remote);
    s2 := Accept(s, @remote, slen);
    writeln('(connection from ' + inet_ntoa(remote.sin_addr) + ')');
    ClientHandler.Create(s2).Start;
  end;
  CloseSocket(s);
  WSACleanup;
end.

Multiplexed server:

Multiplexed server logic is:

function accepthandler
    accept
    add to selector
endfunction

function inputhandler
    receive/read
    send/write
    close
endfunction

listen
add to selector
while
    select from selector
    for all activity
        call handler
    endfor
endwhile

Note that a multiplexed server is only using a single thread.

Java 1.4 added several new IO features called NIO including a SocketChannel. One of the advantages is that it supports select/multiplexing.

package socket.plain.binary.modern;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class SelectServer {
    public static interface EventHandler {
        public void process() throws IOException;
    }
    public static class ConnectionHandler implements EventHandler {
        private ServerSocketChannel ssc;
        private Selector sel;
        public ConnectionHandler(ServerSocketChannel ssc, Selector sel) {
            this.ssc = ssc;
            this.sel = sel;
        }
        @Override
        public void process() throws IOException {
            // accept connection
            SocketChannel sc = ssc.accept();
            System.out.printf("(connection from %s)\n", sc.getRemoteAddress());
            sc.configureBlocking(false);
            // register receive integers handler
            sc.register(sel, SelectionKey.OP_READ, new ReceiveIntegersHandler(sc, sel));
        }
    }
    public static class ReceiveIntegersHandler implements EventHandler {
        private SocketChannel sc;
        private Selector sel;
        public ReceiveIntegersHandler(SocketChannel sc, Selector sel) {
            this.sc = sc;
            this.sel = sel;
        }
        @Override
        public void process() throws IOException {
            ByteBuffer bb;
            // read a and b
            bb = ByteBuffer.allocate(8);
            while(bb.hasRemaining() && sc.read(bb) >= 0);
            bb.rewind();
            bb.order(ByteOrder.BIG_ENDIAN);
            int a = bb.getInt();
            int b = bb.getInt();
            // calculate c and send
            int c = a + b;
            bb = ByteBuffer.allocate(4);
            bb.order(ByteOrder.BIG_ENDIAN);
            bb.putInt(c);
            bb.rewind();
            sc.write(bb);
            // register receive string handler
            sc.register(sel, SelectionKey.OP_READ, new ReceiveStringHandler(sc));
        }
    }
    public static class ReceiveStringHandler implements EventHandler {
        private SocketChannel sc;
        public ReceiveStringHandler(SocketChannel sc) {
            this.sc = sc;
        }
        @Override
        public void process() throws IOException {
            ByteBuffer bb;
            // read v
            bb = ByteBuffer.allocate(1);
            while(bb.hasRemaining() && sc.read(bb) >= 0);
            bb.rewind();
            int buflen = bb.get();
            bb = ByteBuffer.allocate(buflen);
            while(bb.hasRemaining() && sc.read(bb) >= 0);
            bb.rewind();
            byte[] buf = new byte[buflen];
            bb.get(buf);
            String v = new String(buf, "UTF-8");
            // calculate v2 and send
            String v2 = v + v;
            byte[] v2b = v2.getBytes("UTF-8");
            bb = ByteBuffer.allocate(1 + v2b.length);
            bb.put((byte)v2b.length);
            bb.put(v2b);
            bb.rewind();
            sc.write(bb);
            // close connection
            sc.close();
        }
    }
    private static final int PORT = 12345;
    public static void main(String[] args) {
        try {
            // selector initiating all events
            Selector sel = Selector.open();
            // listen on port
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.bind(new InetSocketAddress(PORT));
            ssc.configureBlocking(false);
            // register connection handler
            ssc.register(sel, SelectionKey.OP_ACCEPT, new ConnectionHandler(ssc, sel));
            while(true) {
                sel.select();
                Iterator<SelectionKey> it = sel.selectedKeys().iterator();
                while(it.hasNext()) {
                    SelectionKey sk = it.next();
                    EventHandler eh = (EventHandler) sk.attachment();
                    eh.process();
                    it.remove();
                }
            }
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
}

.NET provide two levels of socket API:

Only the low level API supports multiplexing.

Note that the style used here is not common, but I think it does make sense.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SocketDemo.Binary.Low.SingleServer
{
    public class Program
    {
        private static byte[] IntToBytes(int v)
        {
            byte[] buf = BitConverter.GetBytes(v);
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return buf;
        }
        private static int BytesToInt(byte[] buf)
        {
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return BitConverter.ToInt32(buf, 0);
        }
        public delegate void EventHandler(Socket s, IList sel, IDictionary<Socket,EventHandler> h);
        public static void AcceptHandler(Socket ss, IList sel, IDictionary<Socket,EventHandler> h)
        {
            // accept connection
            Socket s = ss.Accept();
            Console.WriteLine("(connection from {0})", s.RemoteEndPoint);
            // next handler
            sel.Add(s);
            h[s] = ReceiveIntegerHandler;
        }
        public static void ReceiveIntegerHandler(Socket s, IList sel, IDictionary<Socket,EventHandler> h)
        {
            byte[] buf;
            int ix;
            // read a and b
            buf = new Byte[4];
            ix = 0;
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            int a = BytesToInt(buf);
            buf = new Byte[4];
            ix = 0;
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            int b = BytesToInt(buf);
            // calculate c and send
            int c = a + b;
            s.Send(IntToBytes(c));
            // next handler
            h[s] = ReceiveStringHandler;
        }
        public static void ReceiveStringHandler(Socket s, IList sel, IDictionary<Socket,EventHandler> h)
        {
            byte[] buf;
            int ix;
            // read v
            buf = new Byte[1];
            s.Receive(buf);
            int buflen = buf[0];
            buf = new byte[buflen];
            ix = 0;
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            string v = Encoding.UTF8.GetString(buf);
            // calculate v2 and send
            string v2 = v + v;
            byte[] v2b = Encoding.UTF8.GetBytes(v2);
            s.Send(new byte[] { (byte)v2b.Length });
            s.Send(v2b);
            // close connection
            s.Close();
            // next handler
            sel.Remove(s);
            h.Remove(s);
        }
        private const int PORT = 12345;
        private const int BACKLOG = 100;
        public static void Main(string[] args)
        {
            // listen on port
            Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ss.Bind(new IPEndPoint(IPAddress.Any, PORT));
            ss.Listen(BACKLOG);
            IList sel = new ArrayList();
            IDictionary<Socket,EventHandler> h = new Dictionary<Socket,EventHandler>();
            sel.Add(ss);
            h.Add(ss, AcceptHandler);
            while(true) {
                // select
                IList sav = new ArrayList(sel);
                Socket.Select(sel, null, null, 1000000);
                foreach(Socket s in sel)
                {
                    h[s](s, sav, h);
                }
                sel = sav;
            }
        }
    }
}

.NET provide two levels of socket API:

Only the low level API supports multiplexing.

Note that the style used here is not common, but I think it does make sense.

Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Namespace SocketDemo.Binary.Low.SingleServer
    Public Class Program
        Private Shared Function IntToBytes(v As Integer) As Byte()
            Dim buf As Byte() = BitConverter.GetBytes(v)
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return buf
        End Function
        Private Shared Function BytesToInt(buf As Byte()) As Integer
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return BitConverter.ToInt32(buf, 0)
        End Function
        Public Delegate Sub EventHandler(s As Socket, sel As IList, h As IDictionary(Of Socket, EventHandler))
        Public Shared Sub AcceptHandler(ss As Socket, sel As IList, h As IDictionary(Of Socket, EventHandler))
            ' accept connection
            Dim s As Socket = ss.Accept()
            Console.WriteLine("(connection from {0})", s.RemoteEndPoint)
            ' next handler
            sel.Add(s)
            h(s) = AddressOf ReceiveIntegerHandler
        End Sub
        Public Shared Sub ReceiveIntegerHandler(s As Socket, sel As IList, h As IDictionary(Of Socket, EventHandler))
            Dim buf As Byte()
            Dim ix As Integer
            ' read a and b
            buf = New [Byte](3) {}
            ix = 0
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim a As Integer = BytesToInt(buf)
            buf = New [Byte](3) {}
            ix = 0
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim b As Integer = BytesToInt(buf)
            ' calculate c and send
            Dim c As Integer = a + b
            s.Send(IntToBytes(c))
            ' next handler
            h(s) = AddressOf ReceiveStringHandler
        End Sub
        Public Shared Sub ReceiveStringHandler(s As Socket, sel As IList, h As IDictionary(Of Socket, EventHandler))
            Dim buf As Byte()
            Dim ix As Integer
            ' read v
            buf = New [Byte](0) {}
            s.Receive(buf)
            Dim buflen As Integer = buf(0)
            buf = New Byte(buflen - 1) {}
            ix = 0
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim v As String = Encoding.UTF8.GetString(buf)
            ' calculate v2 and send
            Dim v2 As String = v & v
            Dim v2b As Byte() = Encoding.UTF8.GetBytes(v2)
            s.Send(New Byte() {CByte(v2b.Length)})
            s.Send(v2b)
            ' close connection
            s.Close()
            ' next handler
            sel.Remove(s)
            h.Remove(s)
        End Sub
        Private Const PORT As Integer = 12345
        Private Const BACKLOG As Integer = 100
        Public Shared Sub Main(args As String())
            ' listen on port
            Dim ss As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            ss.Bind(New IPEndPoint(IPAddress.Any, PORT))
            ss.Listen(BACKLOG)
            Dim sel As IList = New ArrayList()
            Dim h As IDictionary(Of Socket, EventHandler) = New Dictionary(Of Socket, EventHandler)()
            sel.Add(ss)
            h.Add(ss, AddressOf AcceptHandler)
            While True
                ' select
                Dim sav As IList = New ArrayList(sel)
                Socket.[Select](sel, Nothing, Nothing, 1000000)
                For Each s As Socket In sel
                    h(s)(s, sav, h)
                Next
                sel = sav
            End While
        End Sub
    End Class
End Namespace

The C API has changed regarding how to lookup information. There is an old style which was created for IPv4 and is known to have some problems with IPv6 - and there is a new style which was created to support both IPv4 and IPv6 nicely nicely.

Always use the new style when writing new code. The old style is only shown to understand existing programs or to develop on very old systems.

This example shows the old style.

Note that the style used here is not common, but I think it does make sense.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#define FD_SETSIZE 1024
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define PORT 12345
#define BACKLOG 100

struct handler2;
typedef void (*handler)(int sd2, fd_set *sel, struct handler2 *h);
struct handler2
{
    handler fptr;
};

void accept_handler(int sd, fd_set *sel, struct handler2 *h);
void integer_handler(int sd2, fd_set *sel, struct handler2 *h);
void string_handler(int sd2, fd_set *sel, struct handler2 *h);

void accept_handler(int sd, fd_set *sel, struct handler2 *h)
{
    int sd2, slen;
    struct sockaddr_in remote;
    /* accept */
    sd2 = accept(sd, 0, 0);
    if(sd2 < 0)
    {
        printf("Error accepting socket: %s\n", strerror(errno));
        exit(0);
    }
    slen = sizeof(remote);
    getpeername(sd2, (struct sockaddr *)&remote, &slen);
    printf("(connection from %s)\n", inet_ntoa(remote.sin_addr));
    /* next handler */
    FD_SET(sd2, sel);
    h[sd2].fptr = integer_handler;
}

void integer_handler(int sd2, fd_set *sel, struct handler2 *h)
{
    int status, ix, buflen;
    long int a, b, c, tmp;
    /* read a and b */
    buflen = sizeof(tmp);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
    }
    a = ntohl(tmp);
    buflen = sizeof(tmp);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
    }
    b = ntohl(tmp);
    /* calculate c and send */
    c = a + b;
    tmp = htonl(c);
    status = send(sd2, (char *)&tmp, sizeof(tmp), 0);
    if(status < 0)
    {
        printf("Error sending c: %s\n", strerror(errno));
        exit(0);
    }
    /* next handler */
    h[sd2].fptr = string_handler;
}

void string_handler(int sd2, fd_set *sel, struct handler2 *h)
{
    int status, ix, buflen;
    char *buf, *v, *v2, len;
    /* read v */
    recv(sd2, &len, sizeof(len), 0);
    buflen = len;
    buf = malloc(buflen);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd2, buf + ix, buflen - ix, 0);
    }
    v = malloc(buflen + 1);
    memcpy(v, buf, buflen);
    v[buflen] = '\0';
    free(buf);
    /* calculate v2 and send */
    v2 = malloc(2 * strlen(v) + 1);
    strcpy(v2, v);
    strcat(v2, v);
    len = strlen(v2);
    status = send(sd2, &len, sizeof(len), 0);
    if(status < 0)
    {
        printf("Error sending v2: %s\n", strerror(errno));
        exit(0);
    }
    status = send(sd2, v2, len, 0);
    if(status < 0)
    {
        printf("Error sending v2: %s\n", strerror(errno));
        exit(0);
    }
    free(v);
    free(v2);
    /* close socket */
#ifdef WIN32
    closesocket(sd2);
#else
    close(sd2);
#endif
    /* next handler */
    FD_CLR(sd2, sel);
    h[sd2].fptr = NULL;
}

int main()
{
    int sd, status, i;
    struct sockaddr_in local;
    fd_set sel, sav;
    struct timeval tv;
    struct handler2 h[FD_SETSIZE];
#ifdef WIN32
    WSADATA WSAData;
    if(WSAStartup(0x0101, &WSAData) != 0)
    {
        printf("Error initializing\n");
        exit(0);
    }
#endif
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    /* create socket */
    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* bind */
    local.sin_family = AF_INET;
    local.sin_port = htons(PORT);
    local.sin_addr.s_addr = INADDR_ANY;
    status = bind(sd, (struct sockaddr *)&local ,sizeof(local));
    if(status < 0)
    {
        printf("Error binding socket: %s\n", strerror(errno));
        exit(0);
    }
    /* listen */
    status = listen(sd, BACKLOG);
    if(status < 0)
    {
        printf("Error listening socket: %s\n", strerror(errno));
        exit(0);
    }
    FD_ZERO(&sel);
    FD_SET(sd, &sel);
    h[sd].fptr = accept_handler;
    for(;;)
    {
        /* select */
        sav = sel;
        status = select(FD_SETSIZE, &sel, NULL, NULL, &tv);
        if(status < 0)
        {
            printf("Error selecting: %s\n", strerror(errno));
            exit(0);
        }
        for(i = 0; i < = FD_SETSIZE; i++)
        {
            if(FD_ISSET(i, &sel))
            {
                h[i].fptr(i, &sav, h);
            }
        }
        sel = sav;
    }
    return 0;
}

The C API has changed regarding how to lookup information. There is an old style which was created for IPv4 and is known to have some problems with IPv6 - and there is a new style which was created to support both IPv4 and IPv6 nicely nicely.

Always use the new style when writing new code. The old style is only shown to understand existing programs or to develop on very old systems.

This example shows the new style.

Note that the style used here is not common, but I think it does make sense.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#define FD_SETSIZE 1024
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define PORT "12345"
#define BACKLOG 100

struct handler2;
typedef void (*handler)(int sd2, fd_set *sel, struct handler2 *h);
struct handler2
{
    handler fptr;
};

void accept_handler(int sd, fd_set *sel, struct handler2 *h);
void integer_handler(int sd2, fd_set *sel, struct handler2 *h);
void string_handler(int sd2, fd_set *sel, struct handler2 *h);

void accept_handler(int sd, fd_set *sel, struct handler2 *h)
{
    int sd2, slen;
    struct sockaddr remote;
    char addr[INET6_ADDRSTRLEN];
    /* accept */
    sd2 = accept(sd, 0, 0);
    if(sd2 < 0)
    {
        printf("Error accepting socket: %s\n", strerror(errno));
        exit(0);
    }
    slen = sizeof(remote);
    getpeername(sd2, &remote, &slen);
    if(remote.sa_family == AF_INET)
        printf("(connection from %s)\n", inet_ntop(AF_INET, &((struct sockaddr_in *)&remote)->sin_addr, addr, sizeof(addr)));
    if(remote.sa_family == AF_INET6)
        printf("(connection from %s)\n", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&remote)->sin6_addr, addr, sizeof(addr)));
    /* next handler */
    FD_SET(sd2, sel);
    h[sd2].fptr = integer_handler;
}

void integer_handler(int sd2, fd_set *sel, struct handler2 *h)
{
    int status, ix, buflen;
    long int a, b, c, tmp;
    /* read a and b */
    buflen = sizeof(tmp);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
    }
    a = ntohl(tmp);
    buflen = sizeof(tmp);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd2, (char *)&tmp + ix, buflen - ix, 0);
    }
    b = ntohl(tmp);
    /* calculate c and send */
    c = a + b;
    tmp = htonl(c);
    status = send(sd2, (char *)&tmp, sizeof(tmp), 0);
    if(status < 0)
    {
        printf("Error sending c: %s\n", strerror(errno));
        exit(0);
    }
    /* next handler */
    h[sd2].fptr = string_handler;
}

void string_handler(int sd2, fd_set *sel, struct handler2 *h)
{
    int status, ix, buflen;
    char *buf, *v, *v2, len;
    /* read v */
    recv(sd2, &len, sizeof(len), 0);
    buflen = len;
    buf = malloc(buflen);
    ix = 0;
    while(ix < buflen)
    {
        ix = ix + recv(sd2, buf + ix, buflen - ix, 0);
    }
    v = malloc(buflen + 1);
    memcpy(v, buf, buflen);
    v[buflen] = '\0';
    free(buf);
    /* calculate v2 and send */
    v2 = malloc(2 * strlen(v) + 1);
    strcpy(v2, v);
    strcat(v2, v);
    len = strlen(v2);
    status = send(sd2, &len, sizeof(len), 0);
    if(status < 0)
    {
        printf("Error sending v2: %s\n", strerror(errno));
        exit(0);
    }
    status = send(sd2, v2, len, 0);
    if(status < 0)
    {
        printf("Error sending v2: %s\n", strerror(errno));
        exit(0);
    }
    free(v);
    free(v2);
    /* close socket */
#ifdef WIN32
    closesocket(sd2);
#else
    close(sd2);
#endif
    /* next handler */
    FD_CLR(sd2, sel);
    h[sd2].fptr = NULL;
}

int main()
{
    int sd, status, i;
    struct addrinfo hints, *res;
    fd_set sel, sav;
    struct timeval tv;
    struct handler2 h[FD_SETSIZE];
#ifdef WIN32
    WSADATA WSAData;
    if(WSAStartup(0x0101, &WSAData) != 0)
    {
        printf("Error initializing\n");
        exit(0);
    }
#endif
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    /* find port */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET; //AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
    status = getaddrinfo(NULL, PORT, &hints, &res);
    if(status != 0)
    {
        printf("Error finding port: %s\n", PORT);
        exit(0);
    }
    /* create socket */
    sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* bind */
    status = bind(sd, res->ai_addr, res->ai_addrlen);
    if(status < 0)
    {
        printf("Error binding socket: %s\n", strerror(errno));
        exit(0);
    }
    freeaddrinfo(res);
    /* listen */
    status = listen(sd, BACKLOG);
    if(status < 0)
    {
        printf("Error listening socket: %s\n", strerror(errno));
        exit(0);
    }
    FD_ZERO(&sel);
    FD_SET(sd, &sel);
    h[sd].fptr = accept_handler;
    for(;;)
    {
        /* select */
        sav = sel;
        status = select(FD_SETSIZE, &sel, NULL, NULL, &tv);
        if(status < 0)
        {
            printf("Error selecting: %s\n", strerror(errno));
            exit(0);
        }
        for(i = 0; i < = FD_SETSIZE; i++)
        {
            if(FD_ISSET(i, &sel))
            {
                h[i].fptr(i, &sav, h);
            }
        }
        sel = sav;
    }
    return 0;
}

Asynchroneous server:

Aynschroneous server logic is:

function accepthandler
    accept
    at read call inputhandler
endfunction

function inputhandler
    receive/read
    send/write
    close
endfunction

listen
at accept call accepthandler

Note that an asynchroneous server is using threads behind the scene.

.NET provide two levels of socket API:

Only the low level API supports asynchroneous in a meaningful way.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SocketDemo.Binary.Low.AsyncServer
{
    public class Program
    {
        private static byte[] IntToBytes(int v)
        {
            byte[] buf = BitConverter.GetBytes(v);
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return buf;
        }
        private static int BytesToInt(byte[] buf)
        {
            if(BitConverter.IsLittleEndian) Array.Reverse(buf);
            return BitConverter.ToInt32(buf, 0);
        }
        private class ReceiveContext
        {
            public byte[] Buffer { get; set; }
            public Socket Socket { get; set; }
        }
        private static void ReceiveStringHandler(IAsyncResult ar)
        {
            int ix;
            ReceiveContext rcvctx = (ReceiveContext) ar.AsyncState;
            Socket s = rcvctx.Socket;
            byte[] buf = rcvctx.Buffer;
            // complete read v
            s.EndReceive(ar);
            int buflen = buf[0];
            buf = new byte[buflen];
            ix = 0;
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            string v = Encoding.UTF8.GetString(buf);
            // calculate v2 and send
            string v2 = v + v;
            byte[] v2b = Encoding.UTF8.GetBytes(v2);
            s.Send(new byte[] { (byte)v2b.Length });
            s.Send(v2b);
            // close connection
            s.Close();

        }
        private static void ReceiveIntegersHandler(IAsyncResult ar)
        {
            int ix;
            ReceiveContext rcvctx = (ReceiveContext) ar.AsyncState;
            Socket s = rcvctx.Socket;
            byte[] buf = rcvctx.Buffer;
            // complete read a and b
            ix = s.EndReceive(ar);
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            int a = BytesToInt(buf);
            buf = new Byte[4];
            ix = 0;
            while(ix < buf.Length)
            {
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None);
            }
            int b = BytesToInt(buf);
            // calculate c and send
            int c = a + b;
            s.Send(IntToBytes(c));
            // begin read v
            rcvctx.Buffer = new Byte[1];
            s.BeginReceive(rcvctx.Buffer, 0, rcvctx.Buffer.Length, 0, ReceiveStringHandler, rcvctx);
        }
        private static void ConnectionHandler(IAsyncResult ar)
        {
            Socket ss = (Socket) ar.AsyncState;
            // complete accept connection
            Socket s = ss.EndAccept(ar);
            Console.WriteLine("(connection from {0})", s.RemoteEndPoint);
            // begin read a and b
            ReceiveContext rcvctx = new ReceiveContext { Buffer = new Byte[4], Socket = s };
            s.BeginReceive(rcvctx.Buffer, 0, rcvctx.Buffer.Length, 0, ReceiveIntegersHandler, rcvctx);
            // begin accept connection
            ss.BeginAccept(ConnectionHandler, ss);
        }
        private const int PORT = 12345;
        private const int BACKLOG = 100;
        public static void Main(string[] args)
        {
            // listen on port
            Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ss.Bind(new IPEndPoint(IPAddress.Any, PORT));
            ss.Listen(BACKLOG);
            // begin accept connection
            ss.BeginAccept(ConnectionHandler, ss);
            Console.ReadKey();
        }
    }
}

.NET provide two levels of socket API:

Only the low level API supports asynchroneous in a meaningful way.

Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Namespace SocketDemo.Binary.Low.AsyncServer
    Public Class Program
        Private Shared Function IntToBytes(v As Integer) As Byte()
            Dim buf As Byte() = BitConverter.GetBytes(v)
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return buf
        End Function
        Private Shared Function BytesToInt(buf As Byte()) As Integer
            If BitConverter.IsLittleEndian Then
                Array.Reverse(buf)
            End If
            Return BitConverter.ToInt32(buf, 0)
        End Function
        Private Class ReceiveContext
            Public Property Buffer() As Byte()
            Public Property Socket() As Socket
        End Class
        Private Shared Sub ReceiveStringHandler(ar As IAsyncResult)
            Dim ix As Integer
            Dim rcvctx As ReceiveContext = DirectCast(ar.AsyncState, ReceiveContext)
            Dim s As Socket = rcvctx.Socket
            Dim buf As Byte() = rcvctx.Buffer
            ' complete read v
            s.EndReceive(ar)
            Dim buflen As Integer = buf(0)
            buf = New Byte(buflen - 1) {}
            ix = 0
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim v As String = Encoding.UTF8.GetString(buf)
            ' calculate v2 and send
            Dim v2 As String = v & v
            Dim v2b As Byte() = Encoding.UTF8.GetBytes(v2)
            s.Send(New Byte() {CByte(v2b.Length)})
            s.Send(v2b)
            ' close connection
            s.Close()

        End Sub
        Private Shared Sub ReceiveIntegersHandler(ar As IAsyncResult)
            Dim ix As Integer
            Dim rcvctx As ReceiveContext = DirectCast(ar.AsyncState, ReceiveContext)
            Dim s As Socket = rcvctx.Socket
            Dim buf As Byte() = rcvctx.Buffer
            ' complete read a and b
            ix = s.EndReceive(ar)
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim a As Integer = BytesToInt(buf)
            buf = New [Byte](3) {}
            ix = 0
            While ix < buf.Length
                ix += s.Receive(buf, ix, buf.Length - ix, SocketFlags.None)
            End While
            Dim b As Integer = BytesToInt(buf)
            ' calculate c and send
            Dim c As Integer = a + b
            s.Send(IntToBytes(c))
            ' begin read v
            rcvctx.Buffer = New [Byte](0) {}
            s.BeginReceive(rcvctx.Buffer, 0, rcvctx.Buffer.Length, 0, AddressOf ReceiveStringHandler, rcvctx)
        End Sub
        Private Shared Sub ConnectionHandler(ar As IAsyncResult)
            Dim ss As Socket = DirectCast(ar.AsyncState, Socket)
            ' complete accept connection
            Dim s As Socket = ss.EndAccept(ar)
            Console.WriteLine("(connection from {0})", s.RemoteEndPoint)
            ' begin read a and b
            Dim rcvctx As New ReceiveContext() With { .Buffer = New [Byte](3) {}, .Socket = s }
            s.BeginReceive(rcvctx.Buffer, 0, rcvctx.Buffer.Length, 0, AddressOf ReceiveIntegersHandler, rcvctx)
            ' begin accept connection
            ss.BeginAccept(AddressOf ConnectionHandler, ss)
        End Sub
        Private Const PORT As Integer = 12345
        Private Const BACKLOG As Integer = 100
        Public Shared Sub Main(args As String())
            ' listen on port
            Dim ss As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            ss.Bind(New IPEndPoint(IPAddress.Any, PORT))
            ss.Listen(BACKLOG)
            ' begin accept connection
            ss.BeginAccept(AddressOf ConnectionHandler, ss)
            Console.ReadKey()
        End Sub
    End Class
End Namespace

The Java library Netty provide an async framework on top of NIO.

package socket.plain.binary.netty;

import java.io.UnsupportedEncodingException;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class AsyncServer {
    public static enum NextPhase { WAIT_INTADD, WAIT_STRDUP, DONE }
    private static class ReadHandler extends ChannelInboundHandlerAdapter {
        private ByteBuf inbb;
        private NextPhase state;
        public ReadHandler(Channel ch) {
            inbb = ch.alloc().buffer();
            state = NextPhase.WAIT_INTADD;
        }
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException, InterruptedException {
            // append bytes read read from socket to buffer
            inbb.writeBytes((ByteBuf)msg);
            // act based on current state
            switch(state) {
                case WAIT_INTADD:
                    // test if received a and b
                    if(inbb.readableBytes() >= 8) {
                        // read a and b
                        int a = inbb.getInt(0);
                        int b = inbb.getInt(4);
                        // calculate c and send
                        int c = a + b;
                        ByteBuf outbb = ctx.channel().alloc().buffer();
                        outbb.writeInt(c);
                        ctx.channel().writeAndFlush(outbb);
                        // update state
                        state = NextPhase.WAIT_STRDUP;
                    }
                    break;
                case WAIT_STRDUP:
                    // test if received buflen
                    if(inbb.readableBytes() >= 9) {
                        // read v
                        int buflen = inbb.getByte(8);
                        if(inbb.readableBytes() < 9 + buflen) return; // test if received buf
                        byte[] buf = new byte[buflen];
                        inbb.getBytes(9, buf, 0, buflen);
                        String v = new String(buf, "UTF-8");
                        // calculate v2 and send
                        String v2 = v + v;
                        ByteBuf outbb = ctx.channel().alloc().buffer();
                        outbb.writeByte(v2.length());
                        outbb.writeBytes(v2.getBytes("UTF-8"));
                        ctx.channel().writeAndFlush(outbb);
                        // update state
                        state = NextPhase.DONE;
                    }
                    break;
                case DONE:
                    // ignore
                    break;
            }
        }
    }
    private static final int PORT = 12345;
    private static final int BACKLOG = 100;
    public static void main(String[] args) throws InterruptedException {
        // setup context
        EventLoopGroup g = new NioEventLoopGroup(1);
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(g).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, BACKLOG);
            // define handler
            b.childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     System.out.printf("(connection from %s)\n", ch.remoteAddress());
                     ch.pipeline().addLast(new ReadHandler(ch));
                 }
             });
            // listen on port
            ChannelFuture f = b.bind(PORT).sync();
            // close connection when done (never)
            f.channel().closeFuture().sync();
        } finally {
            // teardown context
            g.shutdownGracefully();
        }       
    }
}

Article history:

Version Date Description
1.0 May 2nd 2018 Initial version
1.1 July 29th 2018 Add Delphi/Lazarus Indy examples
1.2 June 19th 2019 Add Delphi/Lazarus WinSock examples
1.3 June 28th 2022 Add Java Netty example

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj