简体   繁体   中英

C# Client - Server Socket Disconnect Handling

I've got a client - server code going on at the moment while working on my thesis. And I can spawn a connection and send data, but as soon as the client disconnects and tries to reconnect everything goes sideways and I can't seem to figure out why. Too many exceptions are being thrown and I just have no idea where to start catching them all. What am doing wrong or not doing that isn't allowing nether client nor server to handle disconnections properly ?!

This is my ServerSide Code:

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

And this is my ClientSide Code:

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

Two things I can think of. The documentation on Socket recommends calling Shutdown() first, before Close().

Also, you cannot reuse a socket object. So make sure you're using a new Socket object before trying a new connection, either by _socket = new Socket(), or by using a totally new ServerSocket/ClientSocket.

I managed to get it working with the following code. Within the server code, I initiate the BeginReceive witihn the AcceptedCallback in the following way:

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

Then within the ReceivedCallback , i do BeginReceive differently like this:

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

While being bothered by the exceptions, it seems that I've skipped checking what I'm receiving in the initial connection.

At the client code, within the ConnectCallback method i do begin receive the other way around:

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

And within the ReceivedCallback method I did BeginReceive the in reverse:

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

After three hours of debugging, it makes sense in a way to me. The server spawns a new socket object every time it Accepts a connection, in order to avoid threading. So on the Server side at the AcceptedCallback method, the initial _socket.BeginReceive should call the ReceivedCallback method via an empty byte array to only triggers it. Then within the ReceivedCallback you do BeginReceive with an array customized to the length that's been received by the socket. Whereas at Client side, it's reversed.

This is my server side code:

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

And this is my Client side code:

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

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