Отправить сообщение обратно в список клиентов в любое время с асинхронными сокетами в C # - PullRequest
4 голосов
/ 21 февраля 2012

Я установил асинхронный сервер, он отлично работает, подключаясь, получая и отправляя сообщение обратно подключающемуся клиенту.

Сам сервер является Game-World-Server (стиль mmorpg).Когда пользователь отправляет свою позицию туда, где он находится, мне нужно отправить это всем клиентам с PlayerPositionNotice.Я знаю, что мне здесь не хватает некоторых базовых вещей, но когда я пытаюсь сохранить StateObject, созданный в методе accept, и использовать этот сокет для отправки информации игроку в любой момент времени, происходит сбой, потому что сокет закрыт.= / Не знаю, почему это происходит, и я бы искал пару движков на этом, но вернулся пустым.

Вот как я создал свой сервер:

Сначала у нас естьглобальные вещи:

    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;

Секунда у нас есть вещи для инициализации:

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

Пока все хорошо, я ожидаю ... А теперь остальная часть класса сервера.

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

StateManager содержит список StateObject. Ниже вы можете увидеть, как я их создаю.

ГОСУДАРСТВЕННЫЙ МЕНЕДЖЕР:

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

ГОСУДАРСТВЕННЫЙ ОБЪЕКТ

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

Моя проблема здесь в том, что когда кто-нибудь из этого списка отправляет что-либо, я хочу отправить уведомление множеству других клиентов.Как и где я могу это реализовать?Кто-нибудь может ударить меня в правильном направлении?=)

1 Ответ

2 голосов
/ 21 февраля 2012

Этот код кажется правильным, и я не знаю, почему вы получаете ошибку "socket is closed", но есть еще одна проблема: в методе Send (byte [], StateObject state), потому что вы вызываете его при полученииот пользователя и отправьте полученные данные этому пользователю (не всем другим пользователям, чтобы заметить их)

Как вы сказали, если вам нужно отправить новое местоположение всем другим пользователям:

Позвонитеэтот метод вместо вашего Send (byte [] сообщение, состояние StateObject), когда получено новое местоположение.

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 { }
    }

Надеюсь, это немного поможет:)

...