代码之家  ›  专栏  ›  技术社区  ›  Aleksa Ristic

从服务器广播消息

  •  3
  • Aleksa Ristic  · 技术社区  · 7 年前

    我正在学习插座,目前;我正在努力向所有连接的客户端广播消息。

    服务器只是用以下代码启动:

    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 回复  |  直到 7 年前
        1
  •  2
  •   usr    7 年前

    虽然我不能为您编写所有代码,但我会教您一些模式,让您走上正轨。

    我假设您正在通过TCP进行通信。套接字表示连接。如果你想发送一些东西,你必须使用相同的 Socket Bind 将用于打开另一个侦听端口(此处不适用)。

    class MyConnection {
     Socket socket;
     Player player;
    }
    

    List<MyConnection> socket 送东西。


    您可以大大简化accept循环。这里有一个很好的模式:

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

    Microsofts示例代码中的所有异步内容都毫无用处。只有一个接受线程。异步用于保存线程。保存一个线程没有帮助。此外,事件阻塞完全否定了异步的好处。

    如果连接的客户端数量较少(<100)和/或您关心简单的代码,则可以简单地去掉所有异步IO并使用同步IO(无开始/结束)。

    NetworkStream 它有基于任务的方法。然后您可以使用 await . 这也消除了那些使编写简单逻辑变得非常困难的回调。

    如果这不足以帮助您,我们可以在评论中工作。

        2
  •  0
  •   stefan    7 年前

    使用:

    • 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();
    
    推荐文章