0

I've been trying to send a byte array from client to server to build a authentication server.

I have send the byte array succesfully using the Microsoft asynchronous server socket example. link But in their example they are using string to indiate end of byte transfer.

content.IndexOf("<EOF>") > -1

I don't like this approach and removed it, so the server keeps waiting for a messages from the client until the client or server packet handler decides to close the socket.

I don't understand the need of a string to indicate the end of transfer. I can check this by array length? Is the approach I am using safe enough to ensure the data integrity?

My code server side: The changes are made in the method: ReadCallback

public class AsynchronousSocketListener
{
    // Thread signal.  
    public static ManualResetEvent allDone = new ManualResetEvent(false);

    public AsynchronousSocketListener()
    {
    }

    public static void StartListening(String hostname, int port)
    {
        // Establish the local endpoint for the socket.  
        // The DNS name of the computer  
        // example running the listener is "localhost".  
        IPHostEntry ipHostInfo = Dns.GetHostEntry(hostname);
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);

        // Create a TCP/IP socket.  
        Socket listener = new Socket(ipAddress.AddressFamily,
            SocketType.Stream, ProtocolType.Tcp);

        try
        {
            // Bind the socket to the local endpoint and listen for incoming connections.  
            listener.Bind(localEndPoint);
            listener.Listen(100);

            // Do not stop after one connection
            while (true)
            {
                // Set the event to nonsignaled state.  
                allDone.Reset();

                // Start an asynchronous socket to listen for connections.  
                Console.WriteLine("Listening for new connection");
                listener.BeginAccept(
                    new AsyncCallback(AcceptCallback),
                    listener);

                // Wait until a connection is made before listening again
                // for a new connection.  
                allDone.WaitOne();
                Console.WriteLine("Connection established.");

            }

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }

        Console.WriteLine("\nPress ENTER to continue...");
        Console.Read();

    }

    public static void AcceptCallback(IAsyncResult ar)
    {
        // Signal the main thread to continue.  
        allDone.Set();

        // Get the socket that handles the client request.  
        Socket listener = (Socket)ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        // Create the state object.  
        StateObject state = new StateObject();
        state.workSocket = handler;
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
            new AsyncCallback(ReadCallback), state);
    }

    public static void ReadCallback(IAsyncResult ar)
    {
        String content = String.Empty;

        // Retrieve the state object and the handler socket  
        // from the asynchronous state object.  
        StateObject state = (StateObject)ar.AsyncState;
        Socket handler = state.workSocket;

        // Read data from the client socket.
        //int bytesRead = handler.EndReceive(ar);

        // New way, handles forcibly disconnect.
        SocketError errorCode;
        int bytesRead = 0;
        try
        {
            bytesRead = handler.EndReceive(ar, out errorCode);

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }

        if (bytesRead > 0)
        {
            // There  might be more data, so store the data received so far.  
            state.sb.Append(Encoding.ASCII.GetString(
                state.buffer, 0, bytesRead));

            PacketHandler.CheckPacket(
                handler: handler,
                data:  state.buffer,
                length: bytesRead
                );

            // Check for end-of-file tag. If it is not there, read
            // more data.  
            content = state.sb.ToString();

            Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
            content.Length, content);


            // Keep listening to the socket for messages while the socket is connected.
            if (handler.Connected)
            {
                // Empty the previous message
                state.sb.Clear();

                try
                {
                    // Recursieve? Calling itself til socket is disposed off.
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReadCallback), state);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                Thread.Sleep(1000);

            }

            // If it got this far, means socket is closed. Double checking before disposing of the socket.
            if (!handler.Connected)
            {

                try
                {
                    // https://stackoverflow.com/questions/4160347/close-vs-shutdown-socket
                    handler.Shutdown(SocketShutdown.Both);
                    handler.Close();
                    Console.WriteLine("Closed socket");

                }
                catch (Exception ex)
                {
                    Console.WriteLine("Failed close socket. " + ex.Message);
                }
            }



            //if (content.IndexOf("<EOF>") > -1)
            //{
            //    // All the data has been read from the
            //    // client. Display it on the console.  
            //    Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
            //        content.Length, content);
            //    // Echo the data back to the client.  
            //    Send(handler, content);
            //}
            //else
            //{
            //    // Not all data received. Get more.  
            //    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
            //    new AsyncCallback(ReadCallback), state);
            //}
        }
    }

    public static void Send(Socket handler, String data)
    {
        // Convert the string data to byte data using ASCII encoding.  
        byte[] byteData = Encoding.ASCII.GetBytes(data);

        // Begin sending the data to the remote device.  
        handler.BeginSend(byteData, 0, byteData.Length, 0,
            new AsyncCallback(SendCallback), handler);
    }

    private static void SendCallback(IAsyncResult ar)
    {
        try
        {
            // Retrieve the socket from the state object.  
            Socket handler = (Socket)ar.AsyncState;

            // Complete sending the data to the remote device.  
            int bytesSent = handler.EndSend(ar);
            Console.WriteLine("Sent {0} bytes to client.", bytesSent);

            handler.Shutdown(SocketShutdown.Both);
            handler.Close();

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}
Y Kilic
  • 1
  • 2
  • 2
    Sending a token to mark the end of a transfer is common enough, but not very secure. What happens if an attacker never sends the token? Better to design a header that specifies the length of the data to be received, validate the header, then read the expected payload and validate that. Why are you rolling your own Auth service? – jwdonahue Apr 17 '20 at 20:44
  • Hmm own header? I'll didn't know this was possible with C#. I will read into this, thank you for the info. The auth server is for a MMO game I am developing in Unity. Yes there are a lot of pre-made services out there but I find it interesting to build one myself. – Y Kilic Apr 17 '20 at 21:12
  • "own header" but not as in "IP Header" or "TCP Header". @jwdonahue talks about implementing your custom application layer protocol built over TCP. It's like, first 4 bytes as integer are the length of the message (N), then comes the message itself (N bytes). – Oguz Ozgul Apr 17 '20 at 21:25
  • Exactly, but since it's an Auth service, I would include some random salt and checksum for the header and encrypt the whole thing. You can send the numbers as text, and convert to unsigned integer from the text to avoid having to deal with [endianess](https://en.wikipedia.org/wiki/Endianness) and [network byte order](https://en.wikipedia.org/wiki/Endianness#Networking). – jwdonahue Apr 17 '20 at 21:39
  • All that stuff about salt and encryption is a huge red herring and not needed at all. If you need to transmit data securely, use SSL. As far as your question itself goes, I agree that using some kind of marker to terminate the byte stream is a poor idea. Better when sending binary data to simply preface the bytes with a count (sent itself of course as bytes). Alternatively, the server can simply call `Socket.Shutdown(SocketShutdown.Send)` when it's done sending, and that will initiate a graceful closure. Bottom line: you should do a lot of reading about TCP and sockets first. – Peter Duniho Apr 17 '20 at 22:19

0 Answers0