简体   繁体   中英

Best way to receive all data over TCP socket

I am sending base64 encoded XML from a web app to a listening Windows service but if i send a large amount of data (like 1500 characters or more) it seems like stream.DataAvailable will return false too early and the XML will be incomplete.

Is there a better way to wait for all of the data before continuing/exiting the while loop?

public static void loop(object obj)
    {
        TcpListener listener = (TcpListener) obj;

        // Enter the listening loop. 
        while (true)
        {
            // Set the event to nonsignaled state.
            tcpClientConnected.Reset();

            // Perform a blocking call to accept requests.
            listener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), listener);

            // Wait until a connection is made and processed before  
            // continuing.
            tcpClientConnected.WaitOne();
        }
    }

    public static void DoAcceptTcpClientCallback(IAsyncResult ar)
    {
        // Get the listener that handles the client request.
        TcpListener listener = (TcpListener)ar.AsyncState;

        TcpClient client = listener.EndAcceptTcpClient(ar);

        // Signal the calling thread to continue.
        tcpClientConnected.Set();

        try {
            // Get a stream object for reading and writing
            NetworkStream stream = client.GetStream();
            stream.ReadTimeout = 30 * 1000;
            client.Client.ReceiveTimeout = 40;

            if (stream.CanRead)
            {
                // Buffer for reading data
                Byte[] bytes = new Byte[2048];

                // Translate data bytes to an ASCII string.
                string requestBody = "";

                int numberOfBytesRead = 0;
                // Incoming message may be larger than the buffer size. 
                do
                {
                    Array.Clear(bytes, 0, bytes.Length);
                    numberOfBytesRead = stream.Read(bytes, 0, bytes.Length);
                    requestBody = string.Concat(requestBody, Encoding.ASCII.GetString(bytes, 0, numberOfBytesRead));
                }
                while (stream.DataAvailable);

                try
                {
                    Control controller = new Control(requestBody);
                    controller.dispatch(stream);
                }
                catch (Exception e)
                {
                    string error_msg = "Controller exception: " + e.Message;
                    error_msg += "\n" + e.StackTrace;
                    error_msg += "\n" + requestBody;
                    log(error_msg);
                }
            }

            stream.Close();
        }
        catch (SocketException e)
        {
            string error_msg = "Socket exception: " + e.Message;
            log(error_msg);
        } finally
        {
            client.Close();
        }
    }

Rule of thumb: never-ever place raw data on socket. You have to wrap it into a protocol that can help both parties to understand: is there any data to read, is there all data sent, etc.

As noted in comments, simplest way is to prepend your data with a fixed-length integer value that indicates a length of further data to follow. In more sophisticated cases you could reuse things like ProtocolBuffers or Apache Thrift .

Next station here could be your HTTP server or something like that (as HTTP can be customized with such things as new header fields, encryption, caching etc. etc. etc...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM