Socket-Level Server Programming & .NET
by Paul Buis 


Listing One
// TCPechodAsync.cs
// Author: Paul Buis, Computer Science Dept., Ball State University

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

/// <summary>
/// Example of asynchronous callbacks for network socket I/O in .NET framework
/// </summary>
public class TCPechodAsync 
{
    /// <summary>
    /// max number of unaccepted clients
    /// </summary>
   private const int osListenQueueSize = 64;

    /// <summary>
    /// size of OS's buffer for unsent & unacknowledged data
    /// </summary>
    private const int osSendBufferSize = 0x7ffe;

    /// <summary>
    /// size of OS's buffer for data that has arrived
    /// from client but not yet received by program
    /// </summary>
    private const int osReceiveBufferSize = 0x7ffe;

    /// <summary>
    ///  socket bound to a specific TCP port waiting for clients to connect
    /// </summary>
    protected Socket listenSocket;

    /// <summary>
    /// socket bound to a specific UDP socket
    /// </summary>
    protected Socket udpSocket;

    /// <summary>
    /// initializes listenSocket to listen for clients on specified port #
    /// </summary>
    /// <param name="port">port number to use</param>
    public TCPechodAsync(int port)
    {
        const AddressFamily af = AddressFamily.InterNetwork;
        try
        {
           IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);
           listenSocket = new Socket(af, SocketType.Stream, ProtocolType.Tcp);
           listenSocket.Bind(localEndPoint);
           listenSocket.Listen(osListenQueueSize);

           udpSocket = new Socket(af, SocketType.Dgram, ProtocolType.Udp);
           udpSocket.Bind(localEndPoint);
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            Console.Error.WriteLine(e.StackTrace);
        }
    }
    /// <summary>
    ///  registers an event handler to respond to client connections
    ///  and starts a thread for the UDP echo service
    /// </summary>
    public void Run()
    {
        listenSocket.BeginAccept(new AsyncCallback(finishAccept), null);
        ThreadStart threadStart = new ThreadStart(doUDP);
        Thread udpThread = new Thread(threadStart);
        udpThread.Start();
    }
    /// <summary>
    /// handles UDP echo service
    /// </summary>
    public void doUDP()
    {
        const int udpBuffSize = 0xffff;
        byte[] buffer = new byte[udpBuffSize];
        int nBytes; // number of bytes actually read
        while (true)
        {
            IPEndPoint remoteIPEndPoint = new IPEndPoint(IPAddress.Any, 0);
            EndPoint remoteEndPoint = (EndPoint)remoteIPEndPoint;
            try
            {
                nBytes = udpSocket.ReceiveFrom(buffer, 
                    ref remoteEndPoint);
                udpSocket.SendTo(buffer, nBytes, SocketFlags.None,
                    remoteEndPoint);
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message);
                Console.Error.WriteLine(e.StackTrace);
            }
        }
    }
    /// <summary>
    /// event handler to respond to client connections. 
    /// It accepts the connection, re-registers itself and 
    /// registers a handler to respond received data.
    /// </summary>
    /// <param name="ar">result of accept operation</param>
    protected void finishAccept(IAsyncResult ar)
    {
        Socket clientSocket = null;
        try
        {
            clientSocket = listenSocket.EndAccept(ar);
            listenSocket.BeginAccept(new AsyncCallback(finishAccept), null);
            clientSocket.SetSocketOption(SocketOptionLevel.Socket,
                SocketOptionName.SendBuffer, osSendBufferSize);
            clientSocket.SetSocketOption(SocketOptionLevel.Socket,
                SocketOptionName.ReceiveBuffer, osReceiveBufferSize);
            ClientState state = new ClientState(clientSocket);
            AsyncCallback callback = new AsyncCallback(finishReceive);
            clientSocket.BeginReceive(state.buffer, 0, state.buffer.Length,
               SocketFlags.Partial, callback, state);
            WaitCallback dnsCallback = new WaitCallback(doInverseDNS);
            ThreadPool.QueueUserWorkItem(dnsCallback, 
                                  clientSocket.RemoteEndPoint);
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            Console.Error.WriteLine(e.StackTrace);
            if (clientSocket != null)
                clientSocket.Close();
        }
    }
    /// <summary>
    /// A work item to do an inverse DNS lookup on an endpoint for logging.
    /// This is done concurrently with finishRead() and finishSend()
    /// </summary>
    /// <param name="state">The remote endpoint containing the address that
    ///  requires the reverse lookup</param>
    protected void doInverseDNS(Object state)
    {
        IPEndPoint ep = (IPEndPoint)state;
        IPHostEntry he = Dns.GetHostByAddress(ep.Address);
        Console.Out.WriteLine("Connection from {0} ({1}) port {2}",
            he.HostName, ep.Address, ep.Port);
    }
    /// <summary>
    /// event handler to process data received from client.
    /// It starts sending data back to client and registers
    /// handler to be notified when send is complete.
    /// </summary>
    /// <param name="ar">result of receive operation</param>
    protected void finishReceive(IAsyncResult ar)
    {
        ClientState state = (ClientState)ar.AsyncState;
        try
        {
            int numBytes = state.socket.EndReceive(ar);
            if (numBytes <= 0)
            {
                state.socket.Close();
                return;
            }
            AsyncCallback callback = new AsyncCallback(finishSend);
            state.socket.BeginSend(state.buffer, 0, numBytes,
                SocketFlags.None, callback, state);
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            Console.Error.WriteLine(e.StackTrace);
            state.socket.Close();
        }
   }
    /// <summary>
    /// Event handler for completion of sending data to client.
    /// It registers handler for next data recieve.
    /// </summary>
    /// <param name="ar">result of send operation</param>
    protected void finishSend(IAsyncResult ar)
    {
        ClientState state = (ClientState)ar.AsyncState;
        try
        {
            state.socket.EndSend(ar);
            AsyncCallback callback = new AsyncCallback(finishReceive);
            state.socket.BeginReceive(state.buffer, 0, state.buffer.Length,
                SocketFlags.Partial, callback, state);
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            Console.Error.WriteLine(e.StackTrace);
            state.socket.Close();
        }
    }
    /// <summary>
    /// Parses command line and sets up the service to run
    /// on the port specified in the command line.
    /// </summary>
    /// <param name="args">command-line argument for port number to use</param>
    public static void Main(string[] args)
    {
        int port;
        switch (args.Length)
        {
            case 0:
                port = 2000;
                break;
            case 1:
                port = Int32.Parse(args[0]);
                break;
            default:
                Console.WriteLine("Usage: TCPechodAsync [port]");
                return;
        }
        TCPechodAsync echod = new TCPechodAsync(port);
        echod.Run();

        //wait forever to prevent other threads from getting killed
        WaitHandle waiter = new ManualResetEvent(false);
        waiter.WaitOne();
    }
    /// <summary>
   /// Class to represent the state of interaction with one client
    /// </summary>
    protected class ClientState
    {
        /// <summary>
        /// size of buffer to use for all clients
        /// </summary>
        private const int clientBufferSize = 4096;

        /// <summary>
        /// buffer to use for this client
        /// </summary>
        public byte[] buffer = new byte[clientBufferSize];

        /// <summary>
        /// client socket
        /// </summary>
        public Socket socket;

        /// <summary>
        ///  constructs a state object for specified client socket
        /// </summary>
        /// <param name="socket">socket to use for this client</param>
        public ClientState(Socket socket)
        {
            this.socket = socket;
        }
    }
}

Listing Two
/* generic C code to bind a socket to a port and mark it as listening */
SOCKET listenSocket;
struct sockaddr_in localEndPoint;
listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&localEndPoint, 0, sizeof(localEndPoint));
localEndPoint.sa_family=AF_INET;
localEndPoint.sin_addr.s_addr = INADDR_ANY;
localEndPoint.sin_port = htons((u_short) port)
bind(listenSocket, &localEndPoint, sizeof(localEndPoint));
listen(s, 5);





5


