Question

Im trying to forward an event OnClientMessage from my class Client over the class Server to outside my libary.

Client.cs

public class Client
{
    private TcpClient tcpClient;
    private StreamWriter writer;
    private Boolean alive = true;
    private int id;

    public delegate void OnClientMessageHandler(Client sender, String message);
    public delegate void OnClientDisconnectHandler(Client sender);

    public event OnClientMessageHandler OnClientMessage;
    public event OnClientDisconnectHandler OnClientDisconnect;

    public Client(TcpClient tcpClient, int id)
    {
        this.tcpClient = tcpClient;
        this.id = id;

        writer = new StreamWriter(tcpClient.GetStream());

        new Thread(() =>
        {
            Listen(new StreamReader(tcpClient.GetStream()));
        }).Start();
    }

    void Listen(StreamReader reader)
    {
        while (tcpClient.GetStream().DataAvailable && alive)
        {
           OnClientMessage(this, reader.ReadLine());
           Thread.Sleep(150);
        }
    }

    public void Write(String message)
    {
        writer.WriteLine(message);
        writer.Flush();
    }

    public int GetID()
    {
        return id;
    }

    public void Close()
    {
        alive = false;
        writer.Close();
        tcpClient.Close();
        OnClientDisconnect(this);
    }
}

Server.cs

public class Server
{
    private IPAddress serverIP;
    private short serverPort;

    private TcpListener serverListener;
    private int serverClientCount;
    public List<Client> serverClients = new List<Client>();

    private Boolean running;

    public delegate void OnClientMessageHandler(Client sender, String message);
    public delegate void OnClientDisconnectHandler(Client sender);

    public event OnClientMessageHandler OnClientMessage;
    public event OnClientDisconnectHandler OnClientDisconnect;

    public Server(IPAddress ip, short port, Boolean autoStart = true)
    {
        this.serverIP = ip;
        this.serverPort = port;

        if(autoStart)
            OpenServer();
    }

    public void OpenServer()
    {
        serverListener = new TcpListener(serverIP, serverPort);
        serverListener.Start();
        running = true;

        while (running)
        {
            if (serverListener.Pending())
            {
                TcpClient tcpClient = serverListener.AcceptTcpClient();
                new Thread(() =>
                {
                    Client client;
                    client = new Client(tcpClient, serverClientCount);

                    client.OnClientMessage += new Client.OnClientMessageHandler(OnClientMessage);
                    client.OnClientDisconnect += new Client.OnClientDisconnectHandler(OnClientDisconnect);

                    serverClients.Add(client);
                    serverClientCount++;
                }).Start();
            }
            else
            {
                Thread.Sleep(150);
            }
        }
    }

    public void WriteToClient(Client client, String message)
    {
        client.Write(message);
    }

    public void WriteToAll(String message)
    {
        serverClients.ForEach(client => client.Write(message));
    }

    public void Shutdown()
    {
        running = false;
        serverClients.ForEach(client => client.Close());
        serverListener.Stop();
    }
}

Now when the event is firing the application crashes with Delegate to an instance method cannot have null 'this'.
Are I'm doing something wrong or isn't this the right way to forward an event?

Was it helpful?

Solution

This is pretty unique, never once seen anybody do this. It is a regression in the .NET Framework, 3.5 gives you a much better exception. Basic problem is that you made the event subscribe itself. A simple version to repro that crash:

using System;

class Program {
    public static event Action Kaboom;
    static void Main(string[] args) {
        Kaboom += new Action(Kaboom);    // 3.5 raises an exception here
        var handler = Kaboom;
        if (handler != null) handler();  // kaboom
    }
}

You are not actually "forwarding" the event. Untangle your names and code. Add, say, a Fire() method.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top