首先,我必须声明,我没有太多的C#背景,所以我可能会在这里找不到明显的东西。
我被限制为.net 3.5(所以没有异步/等待)。
我已经为NetworkStream做了一个包装器,它会读取"正好是N"字节(与传统的"最多N"字节相对)并且不阻止当前线程。我想以阻塞的方式做同样的事情(也就是说,阻塞直到接收到N个字节,将这些字节返回给调用者),但显然,如果没有编写完全独立的实现,则使用Read
,例如。
internal class ReadBytesContext
{
public Action<byte[]> Callback { get; private set; }
public byte[] Buffer { get; private set; }
public int ReadSoFar { get; set; }
public ReadBytesContext(
Action<byte[]> callback,
byte[] buffer,
int readSoFar)
{
Callback = callback;
Buffer = buffer;
ReadSoFar = readSoFar;
}
}
// Network-related exception handling omitted for brevity.
public class Connection
{
private NetworkStream _stream;
public Connection(NetworkStream stream)
{
_stream = stream;
}
public void ReadBytes(int numBytes, Action<byte[]> callback)
{
var buffer = new byte[numBytes];
_stream.BeginRead(
buffer,
0,
numBytes,
new AsyncCallback(ReadBytesMaybeDone),
new ReadBytesContext(callback, buffer, 0));
}
private void ReadBytesMaybeDone(IAsyncResult ar)
{
int bytesRead = _stream.EndRead(ar);
var context = (ReadBytesContext)ar.AsyncState;
context.ReadSoFar += bytesRead;
if (context.ReadSoFar < context.Buffer.Length)
{
_stream.BeginRead(
context.Buffer,
context.ReadSoFar,
context.Buffer.Length - context.ReadSoFar,
new AsyncCallback(ReadBytesMaybeDone),
context);
}
else
{
context.Callback(context.Buffer);
}
}
}
这通常是c#中网络内容的有效方法吗?
可能不相关的问题:我如何设置一个回调函数的catch-all处理程序,它在ThreadPool上运行(代表传递给Begin *的代理,右边),那么他们中未被捕获的例外不会让应用程序崩溃吗?
答案 0 :(得分:3)
不,这不是一个好方法,因为如果网络流被客户端过早关闭,可能会出现异常。
你看过Jeff Richter的AsyncEnumerator吗?我过去用过.net 3.5。在块中寻找Wintellect Powerthreading库。基本上,yield return之前的第一个代码块执行同步,但是一旦命中了yield,线程就会神奇地放回到线程池中,并且在异步操作完成之前执行不会恢复。这是老派异步/等待真的。
private IEnumerator<int> ReadBytesEnumerator(AsyncEnumerator<byte[]> ae, int numbytes)
{
byte [] buffer = new byte[numbytes];
int totalBytes = 0;
while(totalBytes < numbytes)
{
_stream.BeginRead(buffer , totalBytes , numbytes - totalBytes , ae.End(), null);
yield return 1;
totalBytes +=_stream.EndRead(ae.DequeueResult());
}
ae.Result = buffer;
}
public IAsyncResult BeginReadBytes(int numBytes, AsyncCallback callback, object state)
{
AsyncEnumerator<byte []> ae = new AsyncEnumerator<byte[]>();
return ae.BeginExecute(ReadBytesEnumerator(ae, numBytes), callback, state);
}
public byte [] EndReadBytes(IAsyncResult result)
{
return AsyncEnumerator<byte[]>.FromAsyncResult(result).EndExecute();
}
现在,只要调用EndReadBytes,就可以通过调用代码来处理任何异常。要使此方法同步,您只需调用EndReadBytes(BeginReadBytes(numBytes,null,null);或者甚至用ReadBytes方法包装该片段。
答案 1 :(得分:0)
如果要重用Connection
类,特别是ReadBytes
方法,可能的实现可能是,
public void ReadBytes(
int numBytes,
Action<byte[]> callback,
bool doAsync = true)
{
if (doAsync)
ReadBytesAsync(numBytes, callback);
else
ReadBytesSync(numBytes, callback);
}
private void ReadBytesSync(int numBytes, Action<byte[]> callback)
{
var buffer = new byte[numBytes];
var context = new ReadBytesContext(callback, buffer, 0);
while (context.ReadSoFar < context.Buffer.Length)
{
var bytesRead = _stream.Read(
context.Buffer,
context.ReadSoFar,
context.Buffer.Length - context.ReadSoFar);
context.ReadSoFar += bytesRead;
}
context.Callback(context.Buffer);
}
private void ReadBytesAsync(int numBytes, Action<byte[]> callback)
{
// Works as your current ReadBytes, which is async
}