繁体   English   中英

异步客户端 - 服务器套接字错误中的 ObjectDisposedException

[英]ObjectDisposedException in asyncrhonous client-server socket error

我正在按照 MSDN 代码编写异步客户端/服务器套接字。 它应该是这样工作的:客户端向服务器发送 10 个操作,服务器读取它们,验证它可以从帐户中取回钱,如果操作有效则执行操作并将其发送回客户端。

服务器接收操作就好了,但是当它发回它们时,客户端抛出 ObjectDisposedException。 在客户端读取操作之前服务器套接字是否关闭? 在关闭套接字之前是否必须等待客户端读取?

客户端代码:

public class StateObject
{
    //Socket del cliente
    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 AsynchronousClient
{
    //Puerto para el servidor
    //
    private const int puerto = 11000;

    private static ManualResetEvent connectDone =
        new ManualResetEvent(false);
    private static ManualResetEvent sendDone =
        new ManualResetEvent(false);
    private static ManualResetEvent receiveDone =
        new ManualResetEvent(false);

    //Respuesta del servidor
    private static String response = String.Empty;
    private static Operacion recibido;

    private static void StartClient(Operacion op)
    {  
        try
        {
            //Establecemos un endpoint 
            IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
            IPAddress ipAddress = ipHostInfo.AddressList[0];
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, puerto);

            //Creamos el socket del cliente  
            Socket client = new Socket(ipAddress.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);

            //Nos conectamos al endpoint del servidor  
            client.BeginConnect(remoteEP,
                new AsyncCallback(ConnectCallback), client);
            connectDone.WaitOne();

            //Enviamos los datos al servidor  
            Send(client, op);
            sendDone.WaitOne();

            //Recibimos respuesta del servidor  
            Receive(client);
            receiveDone.WaitOne();
            if(recibido.Valido.Equals("false"))
            {
                Console.WriteLine("No se ha podido proceder con una operación: {0} {1} {2}",
                    recibido.Origen, recibido.Destino, recibido.Cantidad);
            }

            //Liberamos el socket
            client.Shutdown(SocketShutdown.Both);
            client.Close();

        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.ToString());
        }

    }

    private static void ConnectCallback(IAsyncResult ar)
    {
        try
        {
            Socket client = (Socket)ar.AsyncState;
            client.EndConnect(ar);
            connectDone.Set();
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.ToString());
        }
    }

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

            client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReceiveCallback), state);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    private static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            //Hacemos casting del StateObject y recuperamos el Socket
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.workSocket;

            //Leemos los datos del servidor  
            int bytesRead = client.EndReceive(ar);

            if (bytesRead > 0)
            {
                //Puede que no hayamos leído todo así que se va añadiendo  
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

                //Se recupera lo que falta  
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);
            }
            else
            {
                // All the data has arrived; put it in response.  
                if (state.sb.Length > 1)
                {
                    //Deserializacion del objeto
                    response = state.sb.ToString();
                    byte[] byteArray = Encoding.ASCII.GetBytes(response);
                    MemoryStream stream = new MemoryStream(byteArray);
                    recibido = (Operacion)new XmlSerializer(typeof(Operacion)).Deserialize(stream);

                }
                // Signal that all bytes have been received.  
                receiveDone.Set();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    private static void Send(Socket client, Operacion data)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Operacion));
        Stream stream = new MemoryStream();
        serializer.Serialize(stream, data);
        byte[] byteData = ((MemoryStream)stream).ToArray();
        client.BeginSend(byteData, 0, byteData.Length, 0,
            new AsyncCallback(SendCallback), client);
    }

    private static void SendCallback(IAsyncResult ar)
    {
        try
        {
            Socket client = (Socket)ar.AsyncState;
            int bytesSent = client.EndSend(ar);
            Console.WriteLine("Operaciones enviadas al servidor. {0} bytes enviados.", bytesSent);
            sendDone.Set();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    public static int Main(String[] args)
    {
        for (int i = 0; i < 10; i++)
        {
            Operacion op = new Operacion("Destino1", "Destino2", 15, "");
            StartClient(op);
        }
        Console.WriteLine("Fin de envío.");
        Console.ReadLine();
        return 0;
    }
}

服务器代码:

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 static List<Operacion> opList;
    private static Dictionary<string, double> listaCuentas;
    private static String respuesta = String.Empty;

    public AsynchronousSocketListener()
    {
    }

    public static void StartListening()
    {
        IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

        Socket listener = new Socket(ipAddress.AddressFamily,
            SocketType.Stream, ProtocolType.Tcp);

        try
        {
            listener.Bind(localEndPoint);
            listener.Listen(100);

            while (true)
            {

                allDone.Reset();

                //Empezamos a escuchar alguna conexión 
                Console.WriteLine("Esperando conexión...");
                listener.BeginAccept(
                    new AsyncCallback(AcceptCallback),
                    listener);

                //Esperamos a que la conexión se realice antes de continuar 
                allDone.WaitOne();
            }

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
        Console.WriteLine(opList.ToString());
        Console.WriteLine("\nPress ENTER to continue...");
        Console.Read();
    }

    public static void AcceptCallback(IAsyncResult ar)
    {
        allDone.Set();

        Socket listener = (Socket)ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        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 cuenta1 = String.Empty;
        String cuenta2 = String.Empty;
        Double cantidad = 0;
        String validez = String.Empty;

        StateObject state = (StateObject)ar.AsyncState;
        Socket handler = state.workSocket;

        int bytesRead = handler.EndReceive(ar);

        if (bytesRead > 0)
        {
            Stream stream = new MemoryStream(state.buffer);
            Operacion recibido = (Operacion)new XmlSerializer(typeof(Operacion)).Deserialize(stream);
            cuenta1 = recibido.Origen;
            cuenta2 = recibido.Destino;
            cantidad = recibido.Cantidad;

            //Hay que incluir un lock
            Console.WriteLine("{0} envía a {1}: {2}", cuenta1, cuenta2, cantidad);
            if (cantidad <= listaCuentas[cuenta1])
            {
                lock(((ICollection)listaCuentas).SyncRoot)
                {
                    Console.WriteLine("Fondos disponibles. Se procede a realizar la operacion.");
                    recibido.Valido = "true";
                    listaCuentas[cuenta1] = listaCuentas[cuenta1] - cantidad;
                    listaCuentas[cuenta2] = listaCuentas[cuenta2] + cantidad;
                    opList.Add(recibido);
                    Console.WriteLine("{0}: {1}", cuenta1, listaCuentas[cuenta1]);
                    Console.WriteLine("{0}: {1}", cuenta2, listaCuentas[cuenta2]);
                    Send(handler, recibido);
                }
            } else
                {
                    Console.WriteLine("No hay fondos para realizar la operacion.");
                    recibido.Valido = "false";
                    Send(handler, recibido);
            }
        }
    }

    private static void Send(Socket handler, Operacion data)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Operacion));
        Stream stream = new MemoryStream();
        serializer.Serialize(stream, data);
        byte[] byteData = ((MemoryStream)stream).ToArray(); 
        handler.BeginSend(byteData, 0, byteData.Length, 0,
        new AsyncCallback(SendCallback), handler);
    }

    private static void SendCallback(IAsyncResult ar)
    {
        try
        { 
            Socket handler = (Socket)ar.AsyncState;
            int bytesEnviados = handler.EndSend(ar);
            Console.WriteLine("[0] bytes enviados al cliente.", bytesEnviados);

            handler.Shutdown(SocketShutdown.Both);
            handler.Close();
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.ToString());
        }
    }


    public static int Main(String[] args)
    {
        listaCuentas = new Dictionary<string, double>();
        listaCuentas.Add("Destino1", 1500 );
        listaCuentas.Add("Destino2", 2500);
        listaCuentas.Add("Destino3", 150);
        listaCuentas.Add("Destino4", 10);
        opList = new List<Operacion>();
        StartListening();
        return 0;
    }
}

谢谢!

这是因为您的服务器在客户端确认收到 TCP 层的数据之前关闭了连接。

当字节仍在传输中时,即接收方尚未确认它们,然后当您关闭套接字时,套接字将发送TCP RESET 如果所有字节都被确认,那么当您关闭套接字时,服务器将经历一个干净的TCP_FIN套接字关闭过程。

这种异常发生在小数据包和TCP DELAYED ACK - 客户端接收字节,它挂在他们身上寻找更多(延迟它的 ack),服务器关闭连接,但因为它没有收到它发送的所有字节 ACKd一个 RST。 TCP 堆栈挂在这些字节上等待更多,因此没有确认它们,不会使它们可用于socket TCP 堆栈只会使 ACKd 字节可用于socket

TLDR; 对于快速网络,您需要在关闭连接之前在服务器端等待 250 毫秒,如果这不是高性能应用程序,那么我建议等待 800 毫秒

如 RFC 1122 中所指定,TCP 使用延迟确认来减少在媒体上发送的数据包数量。 Windows 2000 和更高版本中的 TCP 不是为接收到的每个 TCP 段发送确认,而是采用通用方法来实现延迟确认。 当 TCP 在特定连接上接收数据时,只有在以下条件之一为真时,它才会发送回确认: 没有为接收到的前一个段发送确认。 接收到一个段,但在 200 毫秒内没有其他段到达该连接。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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