使用C#异步套接字读取面向行的数据

时间:2011-06-20 17:49:30

标签: c# asynchronous tcp

我是C#的新手,但已经完成了多年的TCP和套接字编程。 我想创建一个程序来执行异步I / O(仅接收) 多个服务器。数据是文本,并由换行符分隔(非常简单)。

我的问题是,如果我使用StreamReader,是否可以假设 在我的异步读回调函数中,我是否可以认为

  1. 它将能够获得至少一整行数据,
  2. 它不会以某种方式阻止。
  3. 我理解它的方式,我想在我的网站上做一个StreamReader ReadLine 异步读回调。但是BeginRead中的内容会知道我真的 想做面向行的数据?我显然不知道阅读的长度 那时候。

    我希望这是有道理的,并提前感谢!

    米奇

3 个答案:

答案 0 :(得分:1)

从您的帖子中我认为您需要创建自己的类,并将 SYNCHRONOUS 套接字包装到其中。当整行可用时,您将以阻塞的方式Read()套接字,缓冲数据,拆分行并将事件激发给类使用者。

答案 1 :(得分:1)

基于我对您的问题的理解:您打算像阻塞流一样使用StreamReader,但您使用的是异步套接字...即使我的理解错误,仍请查看建议因为它们还涵盖了您真正打算使用StreamReader进行异步读取的情况。

  1. 它将能够获得至少一整行数据,并且 是和否:只要底层缓冲区有一个新行,它就会读取完整的数据行,但是,这是一个异步事件,所以一旦StreamReader用完数据(即它到达最后一个新行) )即使你得到另一个异步回调,它也将不再有任何东西可读。基本上,您将接收缓冲区绑定到SocketAsyncEventArgs,当您收到回调时,您的缓冲区就会被填充。之后您使用缓冲区做什么(您可以将其输入StreamReader以读取行输入)。

  2. 它不会以某种方式阻止。
    它不会阻止,但它不会获取您正在寻找的所有数据,具体取决于您实现异步回调的方式。这让我想到了下一点......

  3. 我的下一点:我不知道这是一个连续的数据流,还是只是一些行分隔的“批量”数据,但是,你有几个选择:

    1. 使用1个内存流和与该内存流关联的读/写流。每当您收到回调时,您使用StreamWriter编写缓冲区并使用StreamReader读取行,直到您无法再读取任何新行。
    2. 继续将缓冲区写入流,直到从套接字接收到更多字节为止(即收到的字节数为0),然后使用该流初始化StreamReader,然后读取所有行。这假定您具有固定大小的传入数据,而不是连续的“输入”流。
    3. 创建一个阻塞读取器,让它存在于一个单独的线程上,它只在异步读取引发某种类型的标志时才会读取(想想ManualResetEvent或某些东西)。它有点挫败异步套接字的目的,但它仍然可以让你利用这些好处。它比纯粹的阻塞套接字好一点,因为阻塞完全由你控制。
    4. 我认为你最好的选择是#1,但另外2个可能不是不可能的,这取决于你想要做什么。

      旁注:我created an async HTTP client,所以请随意查看它并清除任何有用的内容。我认为这两个函数对你来说是最重要的:

      开始接收:

      private void BeginReceive()
      {
          if ( _clientState == EClientState.Receiving)
          {
              if (_asyncTask.BytesReceived != 0 && _asyncTask.TotalBytesReceived <= _maxPageSize)
              {
                  SocketAsyncEventArgs e = new SocketAsyncEventArgs();
                  e.SetBuffer(_asyncTask.ReceiveBuffer, 0, _asyncTask.ReceiveBuffer.Length);
                  e.Completed += new EventHandler<SocketAsyncEventArgs>(ReceiveCallback);
                  e.UserToken = _asyncTask.Host;
      
                  bool comletedAsync = false;
                  try
                  {
                      comletedAsync = _socket.ReceiveAsync(e);
                  }
                  catch (SocketException se)
                  {
                      Console.WriteLine("Error receiving data from: " + _asyncTask.Host);
                      Console.WriteLine("SocketException: {0} Error Code: {1}", se.Message, se.NativeErrorCode);
      
                      ChangeState(EClientState.Failed);
                  }
      
                  if (!comletedAsync)
                  {
                      // The call completed synchronously so invoke the callback ourselves
                      ReceiveCallback(this, e);
                  }
              }
              else
              {
                  //Console.WriteLine("Num bytes received: " + _asyncTask.TotalBytesReceived);
                  ChangeState(EClientState.ReceiveDone);
              }
          }
      }
      

      和ReceiveCallback:

      private void ReceiveCallback(object sender, SocketAsyncEventArgs args)
      {
          lock (_sync) // re-entrant lock
          {
              // Fast fail: should not be receiving data if the client
              // is not in a receiving state.
              if (_clientState == EClientState.Receiving)
              {
                  String host = (String)args.UserToken;
      
                  if (_asyncTask.Host == host && args.SocketError == SocketError.Success)
                  {
                      try
                      {
                          Encoding encoding = Encoding.ASCII;
                          _asyncTask.BytesReceived = args.BytesTransferred;
                          _asyncTask.TotalBytesReceived += _asyncTask.BytesReceived;
      
                          // ********************************************
                          // You will need to replace the following line:
                          _asyncTask.DocSource += encoding.GetString(_asyncTask.ReceiveBuffer, 0, _asyncTask.BytesReceived);
      
                          // Write the contents of the ReceiveBuffer to a Stream using a StreamWriter
      
                          // Use the StreamReader associated with the same Stream to read the next line(s)
                          // ********************************************
                          BeginReceive();
                      }
                      catch (SocketException e)
                      {
                          Console.WriteLine("Error receiving data from: " + host);
                          Console.WriteLine("SocketException: {0} Error Code: {1}", e.Message, e.NativeErrorCode);
      
                          ChangeState(EClientState.Failed);
                      }
                  }
                  else if (_asyncTask.Host != host)
                  {
                      Console.WriteLine("Warning: received a callback for {0}, but the client is currently working on {1}.",
                          host, _asyncTask.Host);
                  }
                  else
                  {
                      Console.WriteLine("Socket Error: {0} when receiving from {1}",
                         args.SocketError,
                         _asyncTask.Host);
                      ChangeState(EClientState.Failed);
                  }
              }
          }
      } 
      

答案 2 :(得分:0)

据我所知,您无法使用内置类完成此操作。

您可以在StreamReader上使用NetworkStream来读取整行,但您需要自己编写异步部分,因为StreamReader不会公开异步接口。或者,您可以使用异步的NetworkStream.BeginRead,但是您必须编写代码以使其识别基于行的输入。