Search code examples
.netsocketstcpserversocket

How to keep client and server talking


I am trying to build my own asynchronous client/server app using .net sockets.

Using these two examples, the basic functionality works.

Server

Client

This example sends a string to the server upon connection, and the server replies, echoing the string that it received, back to the client.

After this reply, the client then shuts down.

// Release the socket.  
client.Shutdown(SocketShutdown.Both);  
client.Close();

In my scenario, I am trying to connect to the server and then send data to the server from the client via input from the user. I reply back with <OK>.

In this example I linked above, the client always waits for a callback after sending (Hence my reply of <OK>).

I am not shutting down the client like in the example, as I want to send consecutive strings on demand. I need the server to keep listening and replying to confirm receipt of string.

I am quite sure I am missing some core understanding of what should and should not happen.

Right now, I can send a string, the server replies and the client receives this <ok>. I then try to send another string, but the server never "receives" this string. I guess the server is now also waiting for a callback after sending the string, but If I return something to the server after receiving the client side, the whole loop continues and repeats?

How do I make my server wait for more "new" data after replying ok?

Do I have to tell it to "listen" again for any new data? This seems like restarting the connection, which is not my intention...

I would appreciate some insight into what I am missing here...

(P.S. the server is wired with a callback event when sending. I left it like this as this was what the code example that I linked, implemented. DO I have to change my server send routines? (Not to wait for a callback)?

My Server Code:

          public static void StartListening()
        {
            // Establish the local endpoint for the socket.  
            // The DNS name of the computer  
            // running the listener is "host.contoso.com".  
            IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
            IPAddress ipAddress = ipHostInfo.AddressList[1];

            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
            xServerLog.AddLine("Server started: Listening on: " + localEndPoint.Address.ToString());
            xServerLog.ipAddress = localEndPoint.Address.ToString();
            xServerLog.port = port;

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

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

                while (true)
                {
                    // Set the event to nonsignaled state.  
                    allDone.Reset();

                    // Start an asynchronous socket to listen for connections.  
                    Console.WriteLine("Waiting for a connection...");

                    xServerLog.AddLine("Waiting for connections...");

                    myTCPserver.BeginAccept(
                        new AsyncCallback(AcceptCallback),
                        myTCPserver);

                    // Wait until a connection is made before continuing.  
                    allDone.WaitOne();
                }

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

            Console.WriteLine("\nPress ENTER to continue...");
            xServerLog.AddLine("I think things are done here...");

         //   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);

            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));

                // Check for end-of-file tag. If it is not there, read   
                // more data.  
                content = state.sb.ToString();
                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);

                    xServerLog.AddLine("RX:" + content);


                    // Echo the data back to the client.  
                    Send(handler, "<OK>");
                }
                else
                {
                    // Not all data received. Get more.  
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
                }
            }
        }

        private 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);

                xServerLog.AddLine("TX:OK"); // + bytesSent.ToString() + " bytes to client");

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

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

        public void Start()
        {
            StartListening();
            //  return 0;
        }

My Client Code:

  class xtcpClient
{

    // State object for receiving data from remote device.  
    public class StateObject
    {
        // Client socket.  
        public Socket workSocket = null;
        // Size of receive buffer.  
        public const int BufferSize = 256;
        // Receive buffer.  
        public byte[] buffer = new byte[BufferSize];
        // Received data string.  
        public StringBuilder sb = new StringBuilder();
    }

    public class AsynchronousClient
    {
        //remote ip port
        //  private static string remoteServerIP = "0.0.0.1";

        private static IPAddress rIP;// = IPAddress.Parse(remoteServerIP);
        private static IPEndPoint remoteEP; // = new IPEndPoint(rIP, port);

        // Create a TCP/IP socket.  
        private static Socket myTCPClient; // = new Socket(rIP.AddressFamily,SocketType.Stream, ProtocolType.Tcp);

        // The port number for the remote device.  
        private static int port; // = 11000;

        // ManualResetEvent instances signal completion.  
        private static ManualResetEvent connectDone =
            new ManualResetEvent(false);
        private static ManualResetEvent sendDone =
            new ManualResetEvent(false);
        private static ManualResetEvent receiveDone =
            new ManualResetEvent(false);

        // The response from the remote device.  
        //  private static String response = String.Empty;

        private static string serverResponse = string.Empty;

        private static void _open_client()
        {

            remoteEP = new IPEndPoint(rIP, port);

            myTCPClient = new Socket(rIP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            // Connect to the remote endpoint.  
            myTCPClient.BeginConnect(remoteEP,
                new AsyncCallback(ConnectCallback), myTCPClient);
            connectDone.WaitOne();
        }

        //////////private static void StartClient(IPAddress remoteIP, int remotePort)
        //////////{
        //////////    // Connect to a remote device.  
        //////////    try
        //////////    {
        //////////        _open_client();
        //////////        //rIP = remoteIP;
        //////////        //port = remotePort;

        //////////        // Establish the remote endpoint for the socket.  
        //////////        // The name of the   
        //////////        // remote device is "host.contoso.com".  
        //////////        //IPHostEntry ipHostInfo = Dns.GetHostEntry("host.contoso.com");
        //////////        //IPAddress ipAddress = ipHostInfo.AddressList[1];

        //////////        // Send test data to the remote device.  
        //////////        //   string sendReply = "";

        //////////        //  sendReply = Send(myTCPClient, "echo 123<EOF>"); //send an initial test echo 
        //////////        //   xClientLog.AddLine("Sent: Echo<EOF>");
        //////////        //  xClientLog.AddLine("Server Replied: " + sendReply);

        //////////        // Write the response to the console.  
        //////////        //  Console.WriteLine("Response received : {0}", response);
        //////////        //  xClientLog.AddLine("Response received: " + response);

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

        public void _close_connection()
        {
            // Release the socket.  
            myTCPClient.Shutdown(SocketShutdown.Both);
            myTCPClient.Close();
        }
        private static string Send(Socket client, String data)
        {

            if (client is null)
            {
                _open_client();
            }


            // Convert the string data to byte data using ASCII encoding.  
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device.  
            client.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), client); //fire sendcallback when done sending...

            sendDone.WaitOne();

            // Receive the response from the remote device.  
            Receive(myTCPClient);
            receiveDone.WaitOne();

            return serverResponse; //return server response

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

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


                // Signal that all bytes have been sent.  
                sendDone.Set(); //signal the send event to finish, it will start listening for a reply...
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

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

                // Complete the connection.  
                client.EndConnect(ar);

                Console.WriteLine("Socket connected to {0}",
                    client.RemoteEndPoint.ToString());
                xClientLog.AddLine("Socket connected to: " + client.RemoteEndPoint.ToString());

                // Signal that the connection has been made.  
                connectDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void Receive(Socket client)
        {
            try
            {
                // Create the state object.  
                StateObject state = new StateObject();
                state.workSocket = client;

                // Begin receiving the data from the remote device.  
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state); //fire receivcallback event when done receining
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the state object and the client socket   
                // from the asynchronous state object.  
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;

                // Read data from the remote device.  
                int bytesRead = client.EndReceive(ar);

                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));

                    serverResponse = state.sb.ToString();
                    if (serverResponse.IndexOf("<OK>") > -1) {
                        // All the data has been read from the server
                        xClientLog.AddLine("RX:" + serverResponse);
                        receiveDone.Set();
                    }
                    else
                    {
                        // Get the rest of the data.  
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                            new AsyncCallback(ReceiveCallback), state);
                    }

                }

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



        //public static int Main(String[] args)
        //{
        //    StartClient();
        //    return 0;
        //}
        public void startClient(string remoteIP, int remotePort)
        {
            try
            {
                rIP = IPAddress.Parse(remoteIP);
                port = remotePort;

                _open_client();
            }
            catch (Exception e)
            {

                System.Windows.MessageBox.Show(e.Message + Environment.NewLine + e.InnerException.Message);
            }

        }
        public string sendData(string DataToSend)
        {
            return (Send(myTCPClient, DataToSend));
        }
    }
}

Solution

  • Probably because each 'call to accept' on your server accepts a new client. If you want to keep accepting indefinitely you can try something like this to make sure there is always a 'call to accept' available for new clients:

    while (true) {
    var clientSocket = listeningSocket.Accept();
    Task.Factory.StartNew(() => HandleClient(clientSocket));
    }
    

    It's also possible that the client closes the connection. If the server shuts down the Socket connection with the Shutdown method, and all available data has been received, the Receive method will complete and return nothing.