AndrewH
AndrewH

Reputation: 69

TCPIP Asynchronous Server connection dropout

i have written(Copied) code for a tcpip server. We have software that sends a message via TCPIP and expects a return message back to say Receive is Acknowledged. This works fine. But after sending the acknowledgment the connection drops out. I am not sure where in the code this dropout is occurring. So when the next message is sent, there is no connection so it does a rollback and reconnects again and then send the message. So every time i send multiple messages i will keep getting rollback alerts. We will be constantly sending messages all day long. Below is the code :

class Program
{
    // State object for reading client data asynchronously
    public class StateObject
    {
        // Client  socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 1024;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
        public StringBuilder sb = new StringBuilder();
    }

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

        public AsynchronousSocketListener()
        {
        }
        public static void StartListening()
        {
            // Data buffer for incoming data.
            byte[] bytes = new Byte[1024];

            // Establish the local endpoint for the socket.
            // The DNS name of the computer
            // running the listener is "host.contoso.com".
            IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
            IPAddress ipAddress = ipHostInfo.AddressList[0];
            ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 8080);

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

            // Bind the socket to the local endpoint and listen for incoming connections.
            try
            {
                listener.Bind(localEndPoint);
                listener.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...");
                    listener.BeginAccept(
                        new AsyncCallback(AcceptCallback),
                        listener);

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

            }
            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();
            ar.AsyncWaitHandle.WaitOne(100);

            // 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;
            String s1 = "MSH|^~\\&|BB|NSCCH|MILLENNIUM|BB|20150522080258137||ACK";
            String s2 = "|20150522080258137883|P|2.4";//\r\nMSA|AA|Q245548634T287475114";
            //String s3 = String.Empty;
            String s4 = "MSA|AA|Q245548634T287475114";
            //string s5 = "0x02";
            int value = Convert.ToInt32("0x1C", 16);
            char cc = (char)value;
            int value_eob = Convert.ToInt32("0x0b", 16);
            char eob = (char)value_eob;
            int value_eod = Convert.ToInt32("0x0D", 16);
            char eod = (char)value_eod;
            int value_eoa = Convert.ToInt32("0x0A", 16);
            char eoa = (char)value_eoa;

            /*StringBuilder sb1 = new StringBuilder(s5);
            sb1.Append(s1);
            sb1.Append(s2);
            s3 = sb1.ToString();*/

            //byte[] s4 = Encoding.ASCII.GetBytes(s3);
            // 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();
                //s3 = state.sb.ToString();


                // s3 = s1.ToString() + s2.ToString();
                if (bytesRead < 1024) //content.IndexOf("<EOF>") > -1)
                {

                    string s6 = eob.ToString() + s1 + s2 + eod.ToString() + s4 + cc.ToString() + eod.ToString() + eoa.ToString();// "\r\n";// +cc.ToString() + "\r\n";

                    // 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);
                    Console.WriteLine("Send {0} bytes from socket. \n Data : {1}",
                        s6.Length, s6);
                    // Echo the data back to the client.
                    Send(handler, s6);

                }
                else
                {
                    // Not all data received. Get more.
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
                }
            }
            else
            {
                if (content.Length > 0)
                {
                    string s6 = eob.ToString() + s1 + s2 + eod.ToString() + s4 + cc.ToString() + eod.ToString() + eoa.ToString();// "\r\n";// +cc.ToString() + "\r\n";

                    // 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);
                    Console.WriteLine("Send {0} bytes from socket. \n Data : {1}",
                        s6.Length, s6);
                    // Echo the data back to the client.
                    Send(handler, s6);

                }
            }

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

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

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

        public static int Main(string[] args)
        {
            StartListening();
            return 0;
        }
    }
}

Upvotes: 0

Views: 250

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70661

You have at least two major bugs in your code:

  1. You are assuming that a receive operation will always complete having filled the provided buffer, unless the client has stopped sending data (i.e. there's no more data to be received at the moment). This is often going to be true, but it is not guaranteed and your code will fail spuriously if it makes that assumption.
  2. Once you believe that you have received all of the current transmission from the client, you do not call BeginReceive() again. This renders the connection completely useless from that point forward.

Another possible bug is:

  1. Your code does not correctly partition transmitted data. There does not seem to be any code here that will reliably detect the boundary between two different logical messages in the stream of bytes received. Depending on how quickly data is sent by a client, it is possible that some or all data intended to be part of a later logical message is consumed as part of an earlier logical message.

Unfortunately, there is not enough precise detail provided in your question. The phrase "connection drops out" is non-specific and has no well-defined technical meaning. It could mean you get an error on the socket, or it could simply mean that you never receive any more data, or it could mean something else entirely.

That said, if you mean "never receive any more data", then I would say it is most likely that the #2 bug above is the basic cause of your problem. Of course, the #1 bug can in conjunction with the #2 bug also lead to "never receive any more data".

Without a good, minimal, complete code example that reliably reproduces the problem (please note that for network-related issues, a complete example includes both endpoints of the connection), it's impossible to know for sure all of the bugs in the code example, never mind what the actual problem is. However, I would recommend fixing the above bugs and see if that improves things.

As a general rule, your network code should run like this:

  • Receive data
  • Interpret the data, identifying logical message boundary (if any)
  • If a complete logical message has been received, process it. Be sure to retain any excess data that might have been received, for processing as part of the next logical message.
  • Continue the above, always initiating a new receive operation after handling the results of the current completed operation, until a receive operation completes with a byte count of zero.
  • When a zero-byte receive operation completes, complete processing of the data received so far (if there is any not yet processed) and acknowledge the graceful closure of the connection by sending any remaining data (if any) and calling Socket.Shutdown(SocketShutdown.Both), and only then calling Socket.Close() to actually close the socket.

Upvotes: 2

Related Questions