c#-从服务器广播消息

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

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

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

在那里,我启动套接字以侦听是否有人发送消息.

如您所见,我在那里建立了线程,并且希望该线程不时向所有连接的用户发送一些消息(目前所有时间仅几个字节用于测试).

这是线程中的方法

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

此方法仅是最后一次尝试,但未成功.问题是它使我发现以下错误:

socket is reference of null.

在这种情况下,但无论如何,我都无法将消息发送给每个客户端.

这是我跟踪客户端与服务器连接的方式.

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

解决方法:

虽然我无法为您编写所有代码,但我会教您一些可以使您步入正轨的模式.

我假设您正在通过TCP进行通信.套接字代表一个连接.如果要发送内容,则必须使用与接收相同的Socket实例.创建一个新的套接字并绑定将用于打开另一个监听端口(此处不适用).

可能希望每个连接都具有一个对象,以跟踪所有相关状态.例如:

class MyConnection {
 Socket socket;
 Player player;
}

在List< MyConnection>中跟踪该类的实例.然后,您可以遍历该列表并使用套接字发送内容.

您可以简化很多接受循环.这是一个很好的模式:

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

Microsoft的示例代码中的所有异步内容都没有用.只有一个接受线程.异步用于保存线程.保存一个线程没有帮助.而且,阻止事件完全抵消了异步的好处.

如果连接的客户端数量少(< 100),并且/或者如果您关心简单的代码,则可以简单地摆脱所有异步IO并使用同步IO(无Begin / End). 或者,您可以对套接字使用基于任务的包装器,也可以使用具有基于任务的方法的NetworkStream.然后,您可以使用await.这也摆脱了那些使编写简单逻辑变得非常困难的回调. 如果不足以帮助您,我们可以在评论中进行处理.

上一篇:Python编码规范07-基础规范--文件和sockets


下一篇:如何在Windows上通过python发送ARP数据包而不需要winpcap?