C# 如何使 TCP 服务器在有来自客户端的新数据时引发 event/callback
C# How to make a TCP server raise an event/callback when there is new data from the client
所以基本上我希望我的服务器在连接的客户端发送数据时引发事件(或回调)。我想不出解决这个问题的方法,经过几天的搜索后在网上也找不到任何东西。
我想到的是制作一个异步 foreach 循环,遍历所有连接的用户,并检查每个用户是否有任何数据要读取(使用 TcpClient.Avaliable,但网络流也可以检查这个)但是像这样没有任何停止的无限循环将是不好的做法,并且会使用大量的资源(至少据我所知,我是线程和网络的新手)。
每当服务器从客户端获取数据(在本例中为一条消息,因为它是一个聊天应用程序)时,我都需要执行逻辑,基本上将其广播给所有其他用户,但我只是找不到如何检测是否任何用户已发送数据,因此它会引发一个事件来广播消息、记录消息等...
由于我是 threading/networking 的新手,请提前解释。
根据要求,这是我的代码,请注意它是原型 Y 并且有点未完成,但我相信它能说明问题:
//Properties
public List<User> ConnectedUsers { get; private set; } = new List<User>();
public TcpListener listener { get; set; }
public bool IsListeningForConnections { get; set; }
public int DisconnectionCheckInterval { get; set; } //in seconds
//Events
public event EventHandler<ServerEventArgs> UserConnected;
public event EventHandler<ServerEventArgs> MessageReceived;
public NetworkManager()
{
listener = new TcpListener(IPAddress.Parse("192.168.1.86"), 6000); //binds // TODO: Change to: user input / prop file
DisconnectionCheckInterval = 10;
IsListeningForConnections = false;
}
public async void StartListeningForConnections()
{
IsListeningForConnections = true;
listener.Start();
while (IsListeningForConnections)
{
User newUser = new User();
newUser.TcpClient = await listener.AcceptTcpClientAsync();
OnUserConnected(newUser); // raises/triggers the event
}
}
public void StartListeningForDisconnections()
{
System.Timers.Timer disconnectionIntervalTimer = new System.Timers.Timer(DisconnectionCheckInterval * 1000);
//TODO: setup event
//disconnectionIntervalTimer.Elasped += ;
disconnectionIntervalTimer.AutoReset = true;
disconnectionIntervalTimer.Enabled = true;
//disconnectionIntervalTimer.Stop();
//disconnectionIntervalTimer.Dispose();
}
public async void StartListeningForData()
{
//??????????
}
public async void SendData(string data, TcpClient recipient)
{
try
{
byte[] buffer = Encoding.ASCII.GetBytes(data);
NetworkStream stream = recipient.GetStream();
await stream.WriteAsync(buffer, 0, buffer.Length); //await
Array.Clear(buffer, 0, buffer.Length);
}
catch { } //TODO: handle exception when message couldn't be sent (user disconnected)
}
public string ReceiveData(TcpClient sender)
{
try
{
NetworkStream stream = sender.GetStream();
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
return Encoding.ASCII.GetString(buffer).Trim('[=11=]');
}
catch
{
return null; //TODO: handle exception when message couldn't be read (user disconnected)
}
}
protected virtual void OnUserConnected(User user)
{
ConnectedUsers.Add(user);
UserConnected?.Invoke(this, new ServerEventArgs() { User = user });
}
protected virtual void OnMessageReceived(User user, Message message) //needs trigger
{
MessageReceived?.Invoke(this, new ServerEventArgs() { User = user, Message = message });
}
基本上不同的 class 将调用以“StartListeningForX”开头的所有 3 个 classes,然后当其中一项检查通过时引发 3 个相应事件之一(disconnection/connection/new 消息),并处理该数据,我只是不知道如何在每个用户收到新消息时调用事件。
如果您有权访问客户端代码,我会考虑使用 RabbitMQ 之类的服务或类似的队列服务。这允许 link 不同的应用程序通过消息代理或队列聚集在一起,并 messages/events 实时。
您可以在收到事件时调用一些函数。
What I've thought of was making an asynchronous foreach loop that looped through all the connected users, and check if there is any data to be read on each one (using TcpClient.Avaliable, but a network stream could also check this) but an infinite loop like this without any stop would be bad practice and use an insane amount of resources
标准做法是为每个连接的客户端设置一个“无限”循环,以便每个套接字始终进行读取。我将“无限”放在引号中,因为它实际上最终会停止;通过读取 0 字节(表示流结束)或接收异常(表示连接断开)。
I am new to threading/networking
有趣的是,我经常看到开发人员尝试同时学习网络和线程。让我明确一点:线程和 TCP/IP 套接字 都 非常复杂,需要花费大量时间来学习所有尖角。试图同时学习 这两个主题是疯狂的。我强烈建议选择其中一个来学习(我建议先学习线程),只有在掌握了一个之后,才能继续学习另一个。
所以基本上我希望我的服务器在连接的客户端发送数据时引发事件(或回调)。我想不出解决这个问题的方法,经过几天的搜索后在网上也找不到任何东西。 我想到的是制作一个异步 foreach 循环,遍历所有连接的用户,并检查每个用户是否有任何数据要读取(使用 TcpClient.Avaliable,但网络流也可以检查这个)但是像这样没有任何停止的无限循环将是不好的做法,并且会使用大量的资源(至少据我所知,我是线程和网络的新手)。 每当服务器从客户端获取数据(在本例中为一条消息,因为它是一个聊天应用程序)时,我都需要执行逻辑,基本上将其广播给所有其他用户,但我只是找不到如何检测是否任何用户已发送数据,因此它会引发一个事件来广播消息、记录消息等... 由于我是 threading/networking 的新手,请提前解释。
根据要求,这是我的代码,请注意它是原型 Y 并且有点未完成,但我相信它能说明问题:
//Properties
public List<User> ConnectedUsers { get; private set; } = new List<User>();
public TcpListener listener { get; set; }
public bool IsListeningForConnections { get; set; }
public int DisconnectionCheckInterval { get; set; } //in seconds
//Events
public event EventHandler<ServerEventArgs> UserConnected;
public event EventHandler<ServerEventArgs> MessageReceived;
public NetworkManager()
{
listener = new TcpListener(IPAddress.Parse("192.168.1.86"), 6000); //binds // TODO: Change to: user input / prop file
DisconnectionCheckInterval = 10;
IsListeningForConnections = false;
}
public async void StartListeningForConnections()
{
IsListeningForConnections = true;
listener.Start();
while (IsListeningForConnections)
{
User newUser = new User();
newUser.TcpClient = await listener.AcceptTcpClientAsync();
OnUserConnected(newUser); // raises/triggers the event
}
}
public void StartListeningForDisconnections()
{
System.Timers.Timer disconnectionIntervalTimer = new System.Timers.Timer(DisconnectionCheckInterval * 1000);
//TODO: setup event
//disconnectionIntervalTimer.Elasped += ;
disconnectionIntervalTimer.AutoReset = true;
disconnectionIntervalTimer.Enabled = true;
//disconnectionIntervalTimer.Stop();
//disconnectionIntervalTimer.Dispose();
}
public async void StartListeningForData()
{
//??????????
}
public async void SendData(string data, TcpClient recipient)
{
try
{
byte[] buffer = Encoding.ASCII.GetBytes(data);
NetworkStream stream = recipient.GetStream();
await stream.WriteAsync(buffer, 0, buffer.Length); //await
Array.Clear(buffer, 0, buffer.Length);
}
catch { } //TODO: handle exception when message couldn't be sent (user disconnected)
}
public string ReceiveData(TcpClient sender)
{
try
{
NetworkStream stream = sender.GetStream();
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
return Encoding.ASCII.GetString(buffer).Trim('[=11=]');
}
catch
{
return null; //TODO: handle exception when message couldn't be read (user disconnected)
}
}
protected virtual void OnUserConnected(User user)
{
ConnectedUsers.Add(user);
UserConnected?.Invoke(this, new ServerEventArgs() { User = user });
}
protected virtual void OnMessageReceived(User user, Message message) //needs trigger
{
MessageReceived?.Invoke(this, new ServerEventArgs() { User = user, Message = message });
}
基本上不同的 class 将调用以“StartListeningForX”开头的所有 3 个 classes,然后当其中一项检查通过时引发 3 个相应事件之一(disconnection/connection/new 消息),并处理该数据,我只是不知道如何在每个用户收到新消息时调用事件。
如果您有权访问客户端代码,我会考虑使用 RabbitMQ 之类的服务或类似的队列服务。这允许 link 不同的应用程序通过消息代理或队列聚集在一起,并 messages/events 实时。
您可以在收到事件时调用一些函数。
What I've thought of was making an asynchronous foreach loop that looped through all the connected users, and check if there is any data to be read on each one (using TcpClient.Avaliable, but a network stream could also check this) but an infinite loop like this without any stop would be bad practice and use an insane amount of resources
标准做法是为每个连接的客户端设置一个“无限”循环,以便每个套接字始终进行读取。我将“无限”放在引号中,因为它实际上最终会停止;通过读取 0 字节(表示流结束)或接收异常(表示连接断开)。
I am new to threading/networking
有趣的是,我经常看到开发人员尝试同时学习网络和线程。让我明确一点:线程和 TCP/IP 套接字 都 非常复杂,需要花费大量时间来学习所有尖角。试图同时学习 这两个主题是疯狂的。我强烈建议选择其中一个来学习(我建议先学习线程),只有在掌握了一个之后,才能继续学习另一个。