Send a message back to a list of clients at any given time with async sockets in C#

守給你的承諾、 提交于 2019-12-23 13:15:13

问题


Ive got an async server set up, it works perfectly by connecting, receiving and sending back a message to the connecting client.

The server itself is a Game-World-Server (mmorpg style). When a user sends its position to where its located, I need to push this out to all the clients with a PlayerPositionNotice. I know I'm missing some basic stuff here, but when i try to save the StateObject that was created in the accept method, and use that socket to send back information to the player at any given time it fails because the socket is closed. =/ Don't know why this happens and would I've searched a couple of engines on this but came back empty.

This is how i created my server:

First off we have the global stuff:

    public StateManager _stateManager = new StateManager();
    public bool IsClosing = false;

    private const int _port = 1025;

    private IPHostEntry _localhost;
    private IPEndPoint _endpoint;
    private Socket _serverSocket;
    private Thread _serverThread;

Second of we have the initialize stuff:

    public void Start()
    {
        _serverThread = new Thread(Initialize);
        _serverThread.Start();
    }

    /// <summary>
    /// Main entry point for the server
    /// </summary>
    private void Initialize()
    {
        Console.WriteLine("Server Main Socket Thread Initialized.");

        _localhost = Dns.GetHostEntry(Dns.GetHostName());

        try
        {
            _endpoint = new IPEndPoint(_localhost.AddressList[0], _port);

            _serverSocket = new Socket(_endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _serverSocket.Bind(_endpoint);
            _serverSocket.Listen(100);

            _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
        }
        catch (ArgumentOutOfRangeException)
        {
            Console.WriteLine(" >> Port number " + _port + " would seem to be invalid, should be between 1024 and 65000");
        }
        catch (SocketException)
        {
            Console.WriteLine(" >> Could not create socket, check to make sure not duplicating port");
        }
        catch (Exception e)
        {
            Console.WriteLine(" >> Error occured while binding socket, IE:" + e.InnerException);
        }
    }

So far so good, i expect.. And now to the rest of the server class.

    private void acceptCallback(IAsyncResult result)
    {
        Console.WriteLine("Connection Accepted");
        StateObject state = null;

        try
        {
            state = new StateObject
            {
                workSocket = ((Socket)result.AsyncState).EndAccept(result)
            };

            _stateManager.AddConnection(state);


            state.workSocket.BeginReceive(state.buffer, 0, state.buffer.Length, 
                SocketFlags.None, new AsyncCallback(receiveCallback), state);

            _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
        }
        catch (SocketException)
        {
            _stateManager.RemoveConnection(state);
            _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
        }
        catch (Exception)
        {
            _stateManager.RemoveConnection(state);
            _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
        }
    }

    private void receiveCallback(IAsyncResult result)
    {
        var state = (StateObject)result.AsyncState;

        try
        {
            // Buffer and count bytes read
            int bytesRead = state.workSocket.EndReceive(result);

            if (!state.workSocket.Connected)
                _stateManager.RemoveConnection(state);

            if (bytesRead > 0)
            {
                // Parse the message to the protocol manager and return a reply
                var replyingData = ProtocolManager.Parse(state.buffer);
                if (replyingData != null)
                    Send(replyingData, state);

                //Queue the next receive
                state.workSocket.BeginReceive(state.buffer, 0, state.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), state);
            }
            else
            {
                _stateManager.RemoveConnection(state);
            }
        }
        catch (SocketException e)
        {
            _stateManager.RemoveConnection(state);
        }
    }

    public bool Send(byte[] message, StateObject state)
    {
        Console.WriteLine("Sending " + message.Length + " bytes");
        if (state != null && state.workSocket.Connected)
        {
            lock (state.workSocket)
            {
                //we use a blocking mode send, no async on the outgoing
                //since this is primarily a multithreaded application, shouldn't cause problems to send in blocking mode
                state.workSocket.Send(message, message.Length, SocketFlags.None);
            }
        }
        else return false;
        return true;
    }

The stateManager contains a list of StateObject.. Below you can see how i build them.

STATE MANAGER:

public class StateManager
{
    private List<StateObject> _connections = new List<StateObject>();

    public void AddConnection(StateObject so)
    {
        lock (_connections)
        {
            _connections.Add(so);
        }
    }

    public void RemoveConnection(StateObject so)
    {
        if (so.workSocket != null)
        {
            so.workSocket.Close();
            lock (_connections)
            {
                _connections.Remove(so);
            }
        }
    }
}

STATE OBJECT

public class StateObject
{
    public Socket workSocket = null;
    public const int BufferSize = 1024;
    public byte[] buffer = new byte[BufferSize];
    public StringBuilder sb = new StringBuilder();
}

My problem here is that whenever anybody in this list sends something i want to send back a notice to alot of other clients. How and where can i implement this? Anybody that can kick me in the right direction? =)


回答1:


This code seem to be correct and I don't know why you get "socket is closed" error, but there is another problem: in Send(byte[] message, StateObject state) method, because you call this when receiving from user and send received data back to that user.(not to all other users to notice them)

As you said, if you need to send new location to all other users:

Call this method instead of your Send(byte[] message, StateObject state), when received new location.

public void NoticeAllusers(byte []buffer,StateObject state)
    {
        foreach(StateObject obj in _stateManager._connections)
        {
            if (obj != state)
            {
                obj.workSocket.BeginSend(buffer,<parameters you>...., new AsyncCallback(OnSend) state.workSocket);
            }
        }
    }

    public void OnSend(IAsyncResult ar)
    {
        try
        {
            Socket sock = (Socket)ar.AsyncState;
            sock.EndSend(ar);
        }
        catch { }
    }

I hope it will help a little :)



来源:https://stackoverflow.com/questions/9376313/send-a-message-back-to-a-list-of-clients-at-any-given-time-with-async-sockets-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!