C# - 接收数据时的事件和代理

时间:2022-04-25 20:00:31

I want to display a string when I receive data from a server. For that I was thinking on using delegates and events. I'm new to this topic (Delegates and Events) so I'm have not been able to set this up. Here is what I've done:

我想从服务器接收数据时显示一个字符串。为此我正在考虑使用委托和事件。我是这个主题的新手(代表和事件),所以我无法设置它。这就是我所做的:

public delegate void ClientHandleData(byte[] data, int bytesRead);
public event ClientHandleData OnDataReceived;

public void ConnectToServer(string ipAddress, int port)
{
    this.port = port;
    tcpClient = new TcpClient(ipAddress, port);
    clientStream = tcpClient.GetStream();

    Thread t = new Thread(new ThreadStart(ListenForData));
    started = true;
    t.Start();
}
private void ListenForData()
{
   int bytesRead;

   while (started)
   {
      bytesRead = 0;

      try
      {
          bytesRead = clientStream.Read(buffer.ReadBuffer, 0, readBufferSize);      
      }
      catch
      {
        //A socket error has occurred
        MessageBox.Show("A socket error has occurred);
        break;
      }

      if (OnDataReceived != null)
      {

                // display string to a textbox on the UI
      }

    Thread.Sleep(15);
   }

   started = false;

   Disconnect();
}

3 个解决方案

#1


3  

You can just write

你可以写

OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);

If you want to be sure that your event will not be set to null after the if statement you can do this:

如果要确保在if语句之后不将事件设置为null,则可以执行以下操作:

var handler = OnDataReceived;
handler?.Invoke(buffer.ReadBuffer, bytesRead);

Be careful when updating UI, because you can only update UI from UI thread. If you are using WPF you can do this:

更新UI时要小心,因为您只能从UI线程更新UI。如果您使用WPF,您可以这样做:

Dispatcher.Invoke(() => {
   // Update your UI.
});

And also make sure that someone actually subscribed to the event:

并确保有人真正订阅了该活动:

public void Foo()
{
    objectWithTheEvent.OnDataReceived += OnOnDataReceived;
}

private void OnOnDataReceived(byte[] data, int count)
{

}

#2


0  

Let us have a look at your TcpClient listening code. When you call stream.Read() you can not be sure how much data will be read from your socket so you have to read until the end of the stream or you have to know how much date you are supposed to read from socket. Let us assume that you know how much data you are supposed to read

我们来看看你的TcpClient监听代码。当您调用stream.Read()时,您无法确定将从套接字读取多少数据,因此您必须读取直到流的末尾,或者您必须知道应该从套接字读取多少日期。让我们假设你知道你应该阅读多少数据

var readSofar = 0;
var iNeedToRead = 500;//500Bytes
try{
    while(readSoFar<iNeedToRead){
       var readFromSocket = clientStream.Read(buffer.ReadBuffer, readSofar, readBufferSize-readSofar);
       if(readFromSocket==0){
         //Remote server ended your connection or timed out etc
         //Do your error handling may be stop reading
       } 
       readSofar += readFromSocket;
    }

    }
     catch {
       //A socket error has occurred
       MessageBox.Show("A socket error has occurred);
       break;
     }
 if (OnDataReceived != null){
    // display string to a textbox on the UI
 }

You can use null propogation operator like this.

您可以像这样使用null propogation运算符。

OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);

If you are using WindowsForm each controller has to be updated from UI thread therefore you have to call from the subscriber method

如果您使用WindowsForm,则必须从UI线程更新每个控制器,因此您必须从订阅者方法调用

private void IReceivedData(byte[] data, int count){
    this.Invoke(()=>{...Your code});
}

#3


0  

Following would be simple Async-Await based implementation, where there's no need for extra delegate / event / Thread to notify the caller. Client will listen to Server in an Async manner and update on Main / Ui thread on call completion

以下是基于Async-Await的简单实现,其中不需要额外的委托/事件/线程来通知调用者。客户端将以异步方式侦听服务器,并在呼叫完成时更新Main / Ui线程

public class Client
{
    static async Task Main()
    {
      var server = new Server();

      var result = await server.ConnectToServer("IPAddress(Server)",100); // Server Port

      // Update the result out here (On Main thread), there will not be any Thread Exception
    }
}

public class Server
{   
    // Allocate a buffer array for Read Data
    public byte[] ReadBuffer = new byte[15];

    public async Task<Result> ConnectToServer(string ipAddress, int port)
    {
        var tcpClient = new TcpClient(ipAddress, port);

        return await ListenForData(tcpClient);
    }

    private async Task<Result> ListenForData(TcpClient tcpClient)
    {
        int bytesRead = 0;

        var clientStream = tcpClient.GetStream();

        bytesRead = await clientStream.ReadAsync(ReadBuffer, 0, 15);

        return new Result {Buffer = ReadBuffer, Data = bytesRead};
    }
}

public class Result
{
    public byte[] Buffer {get; set;}

    public int Data {get; set;}
}

How It works:

怎么运行的:

  1. Client class is the caller, Ui, which calls the ConnectToServer in a Asynchronous way
  2. 客户端类是调用者Ui,它以异步方式调用ConnectToServer
  3. Ideally the call shall go into a Button Click or similar event
  4. 理想情况下,呼叫应进入按钮点击或类似事件
  5. It connects and waits for data in a non blocking and Async manner
  6. 它以非阻塞和异步方式连接和等待数据
  7. When data returns, you can update the client control
  8. 数据返回时,您可以更新客户端控件

This is probably the simplest implementation possible, you need to fill the data placeholders like IPAddress, Port with correct values

这可能是最简单的实现,您需要使用正确的值填充IPAddress,Port等数据占位符

#1


3  

You can just write

你可以写

OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);

If you want to be sure that your event will not be set to null after the if statement you can do this:

如果要确保在if语句之后不将事件设置为null,则可以执行以下操作:

var handler = OnDataReceived;
handler?.Invoke(buffer.ReadBuffer, bytesRead);

Be careful when updating UI, because you can only update UI from UI thread. If you are using WPF you can do this:

更新UI时要小心,因为您只能从UI线程更新UI。如果您使用WPF,您可以这样做:

Dispatcher.Invoke(() => {
   // Update your UI.
});

And also make sure that someone actually subscribed to the event:

并确保有人真正订阅了该活动:

public void Foo()
{
    objectWithTheEvent.OnDataReceived += OnOnDataReceived;
}

private void OnOnDataReceived(byte[] data, int count)
{

}

#2


0  

Let us have a look at your TcpClient listening code. When you call stream.Read() you can not be sure how much data will be read from your socket so you have to read until the end of the stream or you have to know how much date you are supposed to read from socket. Let us assume that you know how much data you are supposed to read

我们来看看你的TcpClient监听代码。当您调用stream.Read()时,您无法确定将从套接字读取多少数据,因此您必须读取直到流的末尾,或者您必须知道应该从套接字读取多少日期。让我们假设你知道你应该阅读多少数据

var readSofar = 0;
var iNeedToRead = 500;//500Bytes
try{
    while(readSoFar<iNeedToRead){
       var readFromSocket = clientStream.Read(buffer.ReadBuffer, readSofar, readBufferSize-readSofar);
       if(readFromSocket==0){
         //Remote server ended your connection or timed out etc
         //Do your error handling may be stop reading
       } 
       readSofar += readFromSocket;
    }

    }
     catch {
       //A socket error has occurred
       MessageBox.Show("A socket error has occurred);
       break;
     }
 if (OnDataReceived != null){
    // display string to a textbox on the UI
 }

You can use null propogation operator like this.

您可以像这样使用null propogation运算符。

OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);

If you are using WindowsForm each controller has to be updated from UI thread therefore you have to call from the subscriber method

如果您使用WindowsForm,则必须从UI线程更新每个控制器,因此您必须从订阅者方法调用

private void IReceivedData(byte[] data, int count){
    this.Invoke(()=>{...Your code});
}

#3


0  

Following would be simple Async-Await based implementation, where there's no need for extra delegate / event / Thread to notify the caller. Client will listen to Server in an Async manner and update on Main / Ui thread on call completion

以下是基于Async-Await的简单实现,其中不需要额外的委托/事件/线程来通知调用者。客户端将以异步方式侦听服务器,并在呼叫完成时更新Main / Ui线程

public class Client
{
    static async Task Main()
    {
      var server = new Server();

      var result = await server.ConnectToServer("IPAddress(Server)",100); // Server Port

      // Update the result out here (On Main thread), there will not be any Thread Exception
    }
}

public class Server
{   
    // Allocate a buffer array for Read Data
    public byte[] ReadBuffer = new byte[15];

    public async Task<Result> ConnectToServer(string ipAddress, int port)
    {
        var tcpClient = new TcpClient(ipAddress, port);

        return await ListenForData(tcpClient);
    }

    private async Task<Result> ListenForData(TcpClient tcpClient)
    {
        int bytesRead = 0;

        var clientStream = tcpClient.GetStream();

        bytesRead = await clientStream.ReadAsync(ReadBuffer, 0, 15);

        return new Result {Buffer = ReadBuffer, Data = bytesRead};
    }
}

public class Result
{
    public byte[] Buffer {get; set;}

    public int Data {get; set;}
}

How It works:

怎么运行的:

  1. Client class is the caller, Ui, which calls the ConnectToServer in a Asynchronous way
  2. 客户端类是调用者Ui,它以异步方式调用ConnectToServer
  3. Ideally the call shall go into a Button Click or similar event
  4. 理想情况下,呼叫应进入按钮点击或类似事件
  5. It connects and waits for data in a non blocking and Async manner
  6. 它以非阻塞和异步方式连接和等待数据
  7. When data returns, you can update the client control
  8. 数据返回时,您可以更新客户端控件

This is probably the simplest implementation possible, you need to fill the data placeholders like IPAddress, Port with correct values

这可能是最简单的实现,您需要使用正确的值填充IPAddress,Port等数据占位符