C# 客户端 - 服务器套接字断开处理

Posted

技术标签:

【中文标题】C# 客户端 - 服务器套接字断开处理【英文标题】:C# Client - Server Socket Disconnect Handling 【发布时间】:2016-01-15 21:09:48 【问题描述】:

我正在编写我的论文时有一个客户端 - 服务器代码。 而且我可以产生一个连接并发送数据,但是一旦客户端断开连接并尝试重新连接,一切都会横向运行,我似乎无法弄清楚为什么。抛出了太多异常,我只是不知道从哪里开始捕获它们。 什么做错或不做不允许下界客户端或服务器正确处理断开连接?!

这是我的服务器端代码:

using System;
using System.Net;
using System.Net.Sockets;

namespace Server.Networking

public class ServerSocket

    private Socket _socket;
    Byte[] _buffer = new byte[61144];

    public ServerSocket()
    
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    

    public void Bind(int port)
    
        _socket.Bind(new IPEndPoint(IPAddress.Any, port));
    

    public void Listener(int backlog)
    
        _socket.Listen(backlog);
    

    public void Accept()
    
        _socket.BeginAccept(AcceptedCallback, null);
    

    private void AcceptedCallback(IAsyncResult result)
    
        try
        
            Socket clientSocket = _socket.EndAccept(result);
            if (clientSocket.Connected)
            
                Console.WriteLine("Client has connected!");
                _buffer = new byte[61144];
                clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
                Accept();
            
            else
            
                Console.WriteLine("Client hasn't connected!");
                return;
            
        catch(SocketException ex)
        
            Console.WriteLine(ex.Message);
            close(clientSocket);
        
    

    private void ReceivedCallback(IAsyncResult result)
    
        try
        
            Socket clientSocket = result.AsyncState as Socket;
            SocketError SE;
            int bufferSize = clientSocket.EndReceive(result, out SE);
            if (bufferSize > 0)
            
                if (SE == SocketError.Success)
                
                    byte[] packet = new byte[bufferSize];
                    Array.Copy(_buffer, packet, packet.Length);
                    Console.WriteLine("Handling packet from IP:" + clientSocket.RemoteEndPoint.ToString());
                    //Handle packet stuff here.
                    PacketHandler.Handle(packet, clientSocket);
                    _buffer = new byte[61144];
                    clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
                
                else
                
                    close(clientSocket);
                                    
            
            else
            
                Console.WriteLine("Probably received bad data.");
                close(clientSocket);
            
        
        catch (SocketException ex)
        
            Console.WriteLine(ex.Message);
            close(clientSocket);
        
    

    public void close(Socket sock)
    
        Console.WriteLine("Closing socket for IP:" + sock.RemoteEndPoint.ToString() + " and releasing resources.");
        sock.Dispose();
        sock.Close();
    


这是我的客户端代码:

using System;
using System.Net;
using System.Linq;
using System.Net.Sockets;
using Client.Networking.Packets;
using System.Net.NetworkInformation;
using Client.Networking.Packets.Request;

namespace Client.Networking

public class ClientSocket

    private Socket _socket;
    private byte[] _buffer;

    public delegate void RaiseConnect(object source, TextArgs e);
    public static event EventHandler Disconnected;
    private static void RaiseDisconnect()
    
        EventHandler handler = Disconnected;
        if(handler !=null)
        
            handler(null, EventArgs.Empty);
        
    

    public ClientSocket()
    
        udpbroadcast.Connect += new RaiseConnect(OnConnectRaise);
    

    public string machineIP()
    
        return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
    

    private void OnConnectRaise(object sender, TextArgs e)
    
        CheckRegisteredRequest computer_name = new CheckRegisteredRequest(Environment.MachineName.ToString() + "," + machineIP());
        Connect(e.Message, 6556);
        Send(computer_name.Data);
    

    public void Connect(string ipAddress, int port)
    
        string ip = ipAddress;
        int porT = port;
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IAsyncResult result =_socket.BeginConnect(new IPEndPoint(IPAddress.Parse(ip), port), ConnectCallback, null);
        bool success = result.AsyncWaitHandle.WaitOne(5000, true);
        if(!success)
        
            _socket.Close();
            Console.WriteLine("Failed to connect to server. Trying again.");
            Connect(ip, port);
        
    

    private void ConnectCallback(IAsyncResult result)
    
        try 
            if (_socket.Connected)
            
                Console.WriteLine("Connected to the server!");
                _buffer = new byte[61144];
                _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, null);
            
            else
            
                Console.WriteLine("Could not connect.");
                Close(_socket);
            
        
        catch(SocketException ex)
        
            Console.WriteLine("ClientSocket ConnectCallback - "+ex.Message);
            Close(_socket);
        
    

    private void ReceivedCallback(IAsyncResult result)
    
        try
        
            SocketError SE;
            int buflength = _socket.EndReceive(result, out SE);
            if (buflength > 0)
            
                if(SE == SocketError.Success)
                
                    byte[] packet = new byte[buflength];
                    Array.Copy(_buffer, packet, packet.Length);

                    //Handle the Package
                    PacketHandler.Handle(packet, _socket);

                    _buffer = new byte[61144];
                    _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, null);
                
                else
                
                    Close(_socket);
                
            
            else
            
                Close(_socket);
            
        
        catch (Exception ex)
        
            Console.WriteLine("ClientSocket ReceivedCallback - " + ex.Message);
            Close(_socket);
        
    

    public void Send(byte[] data)
    
        byte[] send = new byte[data.Length];
        send = data;
        if( _socket.Connected)
        
            _socket.Send(data);
        
        else
        
            Console.WriteLine("Not connected yet!");
            Close(_socket);
        
    

    public bool connectionStatus()
    
        return _socket.Connected;
    

    public static void Close(Socket sock)
    
        Console.WriteLine("Closing the socket and releasing resources.");
        sock.Dispose();
        sock.Close();
        RaiseDisconnect();
    


【问题讨论】:

在此处查看我的答案(每次客户端连接时都需要再次调用BeginAccept 以接受新连接;我建议将其线程化):***.com/questions/30688760/… 【参考方案1】:

我能想到两件事。 Socket 上的文档建议在 Close() 之前先调用 Shutdown()。

此外,您不能重用套接字对象。因此,请确保在尝试新连接之前使用新的 Socket 对象,或者通过 _socket = new Socket(),或者使用全新的 ServerSocket/ClientSocket。

【讨论】:

【参考方案2】:

我设法让它与以下代码一起工作。在服务器代码中,我通过 AcceptedCallback 以下列方式启动 BeginReceive:

clientSocket.BeginReceive(new byte[] 0, 0, 0, 0, ReceivedCallback, clientSocket);

然后在 ReceivedCallback 中,我会像这样以不同的方式开始接收:

clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);

虽然受到异常的困扰,但我似乎跳过了检查我在初始连接中收到的内容。

在客户端代码中,在 ConnectCallback 方法中,我确实开始以相反的方式接收:

_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, null);

ReceivedCallback 方法中,我反向执行了 BeginReceive:

_socket.BeginReceive(new byte[]  0 , 0, 0, 0, ReceivedCallback, null);

经过三个小时的调试,这在某种程度上对我来说是有意义的。 服务器每次接受连接时都会产生一个新的套接字对象,以避免线程化。所以在Server端的AcceptedCallback方法,初始的_socket.BeginReceive应该通过一个空字节数组调用ReceivedCallback方法才触发它。然后在 ReceivedCallback 中,您使用一个自定义为套接字接收到的长度的数组来执行 BeginReceive。而在客户端,则相反。

这是我的服务器端代码:

    private Socket _socket;
    Byte[] _buffer = new byte[61144];

    public ServerSocket()
    
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    

    public void Bind(int port)
    
        _socket.Bind(new IPEndPoint(IPAddress.Any, port));
    

    public void Listener(int backlog)
    
        _socket.Listen(backlog);
    

    public void Accept()
    
        _socket.BeginAccept(AcceptedCallback, null);
    

    private void AcceptedCallback(IAsyncResult result)
    
        Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        
            Console.WriteLine("We accepted a connection.");
            clientSocket = _socket.EndAccept(result);
            if (clientSocket.Connected)
            
                Console.WriteLine("Client has connected!");
                _buffer = new byte[61144];
                //clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
                clientSocket.BeginReceive(new byte[] 0, 0, 0, 0, ReceivedCallback, clientSocket);
                Accept();
            
            else
            
                Console.WriteLine("Client hasn't connected!");
                Accept();
            
        
        catch (Exception ex)
        
            Console.WriteLine("Socket was probably forcefully closed." + ex.Message);
            Console.WriteLine("Continue accepting other connections.");
            clientSocket.Close();
            Accept();
        
    

    private void ReceivedCallback(IAsyncResult result)
    
        Socket clientSocket = result.AsyncState as Socket;
        SocketError ER;
        try
        
            int bufferSize = clientSocket.EndReceive(result, out ER);
            if (ER == SocketError.Success)
            
                byte[] packet = new byte[bufferSize];
                Array.Copy(_buffer, packet, packet.Length);
                Console.WriteLine("Handling packet from IP:" + clientSocket.RemoteEndPoint.ToString());
                //Handle packet stuff here.
                PacketHandler.Handle(packet, clientSocket);
                _buffer = new byte[61144];
                clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
                //clientSocket.BeginReceive(new byte[]  0 , 0, 0, 0, ReceivedCallback, clientSocket);
            
            else
            
                Console.WriteLine("No bytes received, we're closing the connection.");
                clientSocket.Close();
            
        catch(SocketException ex)
        
            Console.WriteLine("We caught a socket exception:" + ex.Message);
            clientSocket.Close();
        
    

这是我的客户端代码:

private Socket _socket;
    private CheckRegisteredRequest computer_name;
    private bool isClosed;
    private byte[] _buffer;
    public delegate void RaiseConnect(object source, TextArgs e);

    public static event EventHandler Disconnected;
    private static void RaiseDisconnect()
    
        EventHandler handler = Disconnected;
        if (handler != null)
        
            handler(null, EventArgs.Empty);
        
    

    public ClientSocket()
    
        isClosed = true;
        udpbroadcast.Connect += new RaiseConnect(OnConnectRaise);
        computer_name = new CheckRegisteredRequest(Environment.MachineName.ToString() + "," + machineIP());
    

    private void OnConnectRaise(object sender, TextArgs e)
    
        if(!isClosed)
        
            _socket.Close();
            Connect(e.Message, 6556);
        
        else
        
            Connect(e.Message, 6556);
        
    

    public void Connect(string ipAddress, int port)
    
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _socket.BeginConnect(new IPEndPoint(IPAddress.Parse(ipAddress), port), ConnectCallback, null);
        isClosed = false;
        /*IAsyncResult result = 
        bool success = result.AsyncWaitHandle.WaitOne(5000, true);
        if (!success)
        
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
            Console.WriteLine("Failed to connect to server. Trying again.");
            RaiseDisconnect();
        */
    

    private void ConnectCallback(IAsyncResult result)
    
        try
        
            if(_socket.Connected)
            
                _socket.EndConnect(result);
                Console.WriteLine("We initiated a connection to the server.");
                Console.WriteLine("Connected to the server!");
                Send(computer_name.Data);
                _buffer = new byte[61144];
                _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, null);
            
            else
            
                Console.WriteLine("Could not connect.");
                if(!isClosed)
                
                    _socket.Close();
                    isClosed = true;
                    RaiseDisconnect();
                
                else
                
                    isClosed = true;
                    RaiseDisconnect();
                
            
        
        catch (SocketException ex)
        
            Console.WriteLine("ConnectCallback Exception Caught.");
            Console.WriteLine("Shutting down and closing socket for reusal.");
            if (!isClosed)
            
                _socket.Close();
                isClosed = true;
                RaiseDisconnect();
            
            else
            
                isClosed = true;
                RaiseDisconnect();
            
        
    

    private void ReceivedCallback(IAsyncResult result)
    
        try
        
            SocketError SE;
            int buflength = _socket.EndReceive(result, out SE);
            if (SE == SocketError.Success)
            
                byte[] packet = new byte[buflength];
                Array.Copy(_buffer, packet, packet.Length);

                //Handle the Package
                PacketHandler.Handle(packet, _socket);

                _buffer = new byte[61144];
                //_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, null);
                _socket.BeginReceive(new byte[]  0 , 0, 0, 0, ReceivedCallback, null);
            
            else
            
                Console.WriteLine("No bytes received, we're closing the connection.");
                if (!isClosed)
                
                    _socket.Close();
                    isClosed = true;
                    RaiseDisconnect();
                
                else
                
                    isClosed = true;
                    RaiseDisconnect();
                
            
        
        catch (Exception ex)
        
            Console.WriteLine("ReceivedCallback Exception Caught.");
            Console.WriteLine("Shutting down and closing socket for reusal.");
            if (!isClosed)
            
                _socket.Close();
                isClosed = true;
                RaiseDisconnect();
            
            else
            
                isClosed = true;
                RaiseDisconnect();
            
        
    

    public void Send(byte[] data)
    
        byte[] send = new byte[data.Length];
        send = data;
        if (_socket.Connected)
        
            _socket.Send(data);
        
        else
        
            Console.WriteLine("We're not connected yet!");
            if (!isClosed)
            
                _socket.Close();
                isClosed = true;
                RaiseDisconnect();
            
            else
            
                isClosed = true;
                RaiseDisconnect();
            
        
    

    public bool connectionStatus()
    
        return _socket.Connected;
    

    public string machineIP()
    
        return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
    

【讨论】:

以上是关于C# 客户端 - 服务器套接字断开处理的主要内容,如果未能解决你的问题,请参考以下文章

JAVA:处理套接字断开连接

C#多线程聊天服务器,处理断开连接

客户端断开连接后,我是不是必须关闭服务器端的套接字?

如何在socket.io中的断开事件上获取断开连接的客户端的套接字ID

Winsock - 客户端断开连接,关闭套接字循环/最大连接数

自动重连socket客户端的设计选择