Вещательное сообщение с сервера - PullRequest
0 голосов
/ 09 января 2019

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

Сервер просто запускается с этим кодом:

public static int Main(String[] args)
{

    Thread t1 = new Thread(Test);
    t1.Start();


    #region Start listening to socket
    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);

        Console.WriteLine("[{0}] Server started listening!", DateTime.Now);
        while (true)
        {
            // Set the event to nonsignaled state.  
            allDone.Reset();

            // Start an asynchronous socket to listen for connections.
            listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

            // Wait until a connection is made before continuing.  
            allDone.WaitOne();
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
    #endregion

    Console.WriteLine("\nPress ENTER to continue...");
    Console.Read();

    return 0;
}

Там я запускаю сокет для прослушивания, если кто-то отправляет сообщение.

Как видите, у меня есть thread, и я хочу, чтобы этот поток время от времени отправлял всем подключенным пользователям какое-то сообщение (на данный момент всего несколько байтов только для тестирования).

Вот этот метод из потока

private static void Test()
{
    while (true)
    {
        for (int i = 0; i < Players.Length; i++)
        {
            if (Players[i] == null)
                continue;

            Socket cs = new Socket(Players[i].IPEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            cs.Bind(Players[i].IPEndPoint);

            byte[] data = Encoding.ASCII.GetBytes("Random data");

            cs.Send(data);
        }
    }
}

Этот метод является последней попыткой, но не удачным. Проблема в том, что это сбрасывает мне ошибки, которые:

Сокет

является нулевым.

В этом случае, но в любом случае мне не удалось отправить сообщение каждому клиенту.

Вот как я отслеживаю соединение клиента с сервером.

case "0": // Client tries to connect to server

    if (nClients >= MAX_PLAYERS)
    {
        Send(handler, "Too many players. Try again later.");
        return;
    }

    for (int i = 0; i < MAX_PLAYERS; i++)
    {
        if(Players[i] == null)
        {
            Players[i] = new Client();
            Players[i].Name = parts[1].ToString();
            Players[i].IPEndPoint = handler.RemoteEndPoint as IPEndPoint;

            Send(handler, String.Format("1|{0}", i));
            Console.WriteLine("[{0}] Succesfully registered client ID: {1}, NAME: {2}!", DateTime.Now, i, parts[1].ToString());
            i = MAX_PLAYERS;
            nClients++;
        }
    }
    break;

Это только часть кода, ориентированная на обработку сообщения о соединении, который находится внутри моего метода:

private static void HandleMessages(string message, Socket handler)

и я звоню отсюда:

public static void AcceptCallback(IAsyncResult ar)
{
    // Signal the main thread to continue.  
    allDone.Set();

    // Get the socket that handles the client request.  
    Socket listener = (Socket)ar.AsyncState;
    Socket handler = listener.EndAccept(ar);

    // Create the state object.  
    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 content = String.Empty;

    // Retrieve the state object and the handler socket  
    // from the asynchronous state object.  
    StateObject state = (StateObject)ar.AsyncState;
    Socket handler = state.workSocket;

    // Read data from the client socket.   
    int bytesRead = handler.EndReceive(ar);

    if (bytesRead > 0)
    {
        // There  might be more data, so store the data received so far.  
        state.sb.Append(Encoding.ASCII.GetString(
            state.buffer, 0, bytesRead));

        // Check for end-of-file tag. If it is not there, read   
        // more data.  
        content = state.sb.ToString();
        if (content.IndexOf("<EOF>") > -1)
        {
            string message = content.Remove(content.Length - 5, 5);
            HandleMessages(message, handler);
        }
        else
        {
            // Not all data received. Get more.  
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
            new AsyncCallback(ReadCallback), state);
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 13 января 2019

Использование:

  • using System.Net.Sockets;
  • using System.Threading;

Класс узла клиента:

Создать класс для хранения информации о клиентах

public TcpClient tclient;
public byte[] Tx, Rx;
public string strId;
public string strName;

public ClientNode(TcpClient _tclient, byte[] _tx, byte[] _rx, string _str, string _name)
{
    tclient = _tclient;
    Tx = _tx;
    Rx = _rx;
    strId = _str;
    strName = _name;
}

public string ToStrng()
{
    return strName;
}

Полный сервер: (Просто создайте класс и вставьте ниже код в этот класс сервера)

bool mIsListening = false;
TcpListener mTCPListener;
private List<ClientNode> mlClientSocks;
TcpClient tcpc;
IPAddress IP;
int Port;
Thread t;

public void StartServer(string _IP, int _Port)
{
    IP = IPAddress.Parse(_IP);
    Port = _Port;

    t = new Thread(new ThreadStart(this.StartProcessing));
    t.Start();
}

public void StartProcessing()
{
    //Server is started
    mlClientSocks = new List<ClientNode>();

    try
    {
        mTCPListener = new TcpListener(IP, Port);
        //Server is running now
        mTCPListener.Start();
        mIsListening = true;
        mTCPListener.BeginAcceptTcpClient(onCompleteAcceptTcpClient, mTCPListener);
    }
    catch (Exception exx)
    {
        // Handle exception message hare
    }
}

void onCompleteAcceptTcpClient(IAsyncResult iar)
{
    TcpListener tcpl = (TcpListener)iar.AsyncState;
    TcpClient tclient = null;
    ClientNode cNode = null;
    if (!mIsListening)
    {
        //Stopped listening for incoming connections
        return;
    }

    try
    {
        tclient = tcpl.EndAcceptTcpClient(iar);
        //Client Connected...
        StreamReader sR = new StreamReader(tclient.GetStream());

        // Read the username (waiting for the client to use WriteLine())
        String username = (String)sR.ReadLine();

        tcpl.BeginAcceptTcpClient(onCompleteAcceptTcpClient, tcpl);

        lock (mlClientSocks)
        {
            // add newly connected client node in List
            mlClientSocks.Add((cNode = new ClientNode(
                tclient,
                new byte[512],
                new byte[512],
                tclient.Client.RemoteEndPoint.ToString(),
                username,
            )));
        }

        // broadcasting newly connected client to all other clients
        BroadcastClients("New client connected: " + username);

        tclient.GetStream().BeginRead(cNode.Rx, 0, cNode.Rx.Length, onCompleteReadFromTCPClientStream, tclient);
    }
    catch (Exception exc)
    {
        // handle exception here
    }
}

void onCompleteReadFromTCPClientStream(IAsyncResult iar)
{
    int nCountReadBytes = 0;
    string strRecv;
    ClientNode cn = null;

    try
    {
        lock (mlClientSocks)
        {
            tcpc = (TcpClient)iar.AsyncState;

            // find client from list
            cn = mlClientSocks.Find(x => x.strId == tcpc.Client.RemoteEndPoint.ToString());
            // check if client is connected
            if (IsConnected)
                nCountReadBytes = tcpc.GetStream().EndRead(iar);
            else
                nCountReadBytes = 0;

            //Disconnect Client if there is no byte
            if (nCountReadBytes == 0)
            {
                mlClientSocks.Remove(cn);
                return;
            }

            // read message recieved from client (node)
            strRecv = Encoding.ASCII.GetString(cn.Rx, 0, nCountReadBytes).Trim();

            /*

              Handle messages from clients

            */

            cn.Rx = new byte[512];

            tcpc.GetStream().BeginRead(cn.Rx, 0, cn.Rx.Length, onCompleteReadFromTCPClientStream, tcpc);


        }
    }
    catch (Exception)
    {
        lock (mlClientSocks)
        {
            //Client is Disconnected and removed from list
            mlClientSocks.Remove(cn);
        }
    }
}

private void onCompleteWriteToClientStream(IAsyncResult iar)
{
    try
    {
        TcpClient tcpc = (TcpClient)iar.AsyncState;
        tcpc.GetStream().EndWrite(iar);
    }
    catch (Exception exc)
    {
        // handle exception
    }
}

public void StopServer()
{
    StopListing();
}

public void StopListing()
{
    // stop server thread
    t.Interrupt();
    try
    {
        mIsListening = false;
        mTCPListener.Stop();
    }
    catch (Exception eee)
    {
        // handle exception
    }
}

public bool IsConnected
{
    get
    {
        try
        {
            if (tcpc != null && tcpc.Client != null && tcpc.Client.Connected)
            {
                // Detect if client disconnected
                if (tcpc.Client.Poll(0, SelectMode.SelectRead))
                {
                    byte[] buff = new byte[1];
                    if (tcpc.Client.Receive(buff, SocketFlags.Peek) == 0)
                    {
                        // Client disconnected
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }

                return true;
            }
            else
            {
                return false;
            }
        }
        catch
        {
            return false;
        }
    }
}

После создания класса сервера с указанным выше кодом, теперь создайте функцию (BroadcastClients) в этом классе серверов для широковещательных клиентов, таких как:

void BroadcastClients(string BroadcastingMsg)
{
    if (mlClientSocks.Count() <= 0)
        return;
    else
    {
        ClientNode cn = null;

        mlClientSocks.ForEach(delegate (ClientNode clntN)
        {
            cn = clntN;

            try
            {
                // broadcasting online clients list
                cn.Tx = Encoding.ASCII.GetBytes(BroadcastingMsg);
                cn.tclient.GetStream().BeginWrite(cn.Tx, 0, cn.Tx.Length, onCompleteWriteToClientStream, cn.tclient);
            }
            catch (Exception e)
            {
                // handle exception 
            }
        });
    }
}

После создания вышеуказанного метода вы можете использовать его в любом месте, где хотите рассылать сообщения клиентам.

Использовать указанный выше класс сервера, например:

MyServer server = new MyServer();
server.StartServer("127.0.0.1",8000);

// For stopping server use this
// server.StopServer();
0 голосов
/ 13 января 2019

Хотя я не могу написать весь код для вас, я научу вас некоторым схемам, которые должны помочь вам в этом.

Я предполагаю, что вы общаетесь по TCP. Сокет представляет собой соединение. Если вы хотите что-то отправить, вы должны использовать тот же экземпляр Socket, который вы используете для получения. Создание нового сокета и Bind будет использоваться для открытия другого порта прослушивания (здесь не применимо).

Возможно, вы хотите иметь один объект для каждого соединения, который отслеживает все связанные состояния. Например:

class MyConnection {
 Socket socket;
 Player player;
}

Отслеживание экземпляров этого класса в List<MyConnection>. Затем вы можете просмотреть этот список и использовать socket для отправки чего-либо.


Вы можете значительно упростить цикл принятия. Вот хороший шаблон:

while (true) {
 var connectionSocket = listeningSocket.Accept();
 Task.Run(() => ProcessConnection(connectionSocket));
}

Все эти асинхронные элементы в примере кода Microsoft не имеют смысла. Есть только одна принимающая нить. Async используется для сохранения потоков. Сохранение одного потока не помогает. Кроме того, блокировка событий полностью сводит на нет преимущества асинхронности.

Если число подключенных клиентов мало (<100) и / или если вы заботитесь о простом коде, вы можете просто избавиться от всех асинхронных операций ввода-вывода и использовать синхронный ввод-вывод (без начала / конца). </p>

В качестве альтернативы вы можете использовать основанную на задачах оболочку для сокета или использовать NetworkStream, которая имеет методы на основе задач. Затем вы можете использовать await. Это также избавляет от тех обратных вызовов, которые сильно затрудняют написание простой логики.

Мы можем работать с комментариями, если этого недостаточно, чтобы помочь вам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...