除了有时候(到目前为止只注意到Vista),它的工作非常好。
我正在尝试创建一个健壮的Async Socket.BeginReceive进程。目前,我所做的只是连接到服务器,服务器确认连接并将文件发送到客户端。客户端解析前缀,并通过BinaryWriter处理该文件。
TCP **** BufferSize = 1024 ****
编辑:重新设计功能以使其更强大 工作流程如下;
发送: - 我发送8字节前缀包,这是两个整数。 (First Int是预期的文件大小,第二个int是预期的前缀大小,然后文件本身是下一个。
接收
在我无疑收到前8个字节后,我处理前4个字节转换为整数(文件大小字节长度),然后将接下来的4个字节转换为整数(前缀大小字节长度)< / p>
然后我毫无疑问地从缓冲区接收前缀大小字节长度,并处理我的前缀。
然后我开始根据前缀消息中的文件大小字节长度存储接收我的文件。
问题:一切都在当地有效。我在发送和接收后测试了校验和和文件数据,一切看起来都不错。
然而,商业环境(注意到vista),偶尔(并非总是,大多数时候传输成功)我将得到一个System.IO.IOException:进程无法访问文件'C:\ TestReceived。 txt'...这是精确错误的屏幕截图。
我的想法是,因为beginRecieve
在一个单独的线程上被称为异步,有时两个线程都试图通过BinaryWriter
同时访问文件流。
我尝试使用FileShare.None初学二进制编写器,因为我读到它会锁定文件。
BinaryWriter writer = new BinaryWriter(File.Open(state.receivedPath, FileMode.Append,FileAccess.Write,FileShare.None));
它似乎没有按预期锁定文件,因为这不能解决问题。
问题:任何一位大师能指导我如何正确锁定FileStream吗?我在ReceiveCallback
表示我认为我出错了。
编辑:解决方案:所以我最终发现也许我没有清理用于创建/附加到文件的资源。我已经切换到using语句来初始化我的FileStream
对象和BinaryWriter
对象,希望它能更好地管理清理,并且它似乎正在工作:)没有一个失败的测试全部天。现在是时候处理服务器端的异常了!谢谢大家的帮助。
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
state.totalBytesRead += bytesRead;
if (bytesRead > 0)
{
if (state.flag == 0)
{
if (state.totalBytesRead >= 8)
{
// we know we put the msgLen / prefixLen as the first 8 bytes on the stream
state.msgLen = BitConverter.ToInt32(state.buffer, 0);
state.prefixLen = BitConverter.ToInt32(state.buffer, 4);
state.flag = 1;
// good to process the first 2 integer values on the stream
//state.sb.Append(Encoding.ASCII.GetString(state.buffer, 8, bytesRead));
int prefixRequestBytes = state.prefixLen;
if (prefixRequestBytes > StateObject.BufferSize)
prefixRequestBytes = StateObject.BufferSize;
state.lastSendByteCount = prefixRequestBytes;
state.totalBytesRead = 0;
// start re-writing to the begining of the buffer since we saved
client.BeginReceive(state.buffer, 0, prefixRequestBytes, 0, new AsyncCallback(ReceiveCallback), state);
return;
}
else
{
int bytesToSend = state.lastSendByteCount - bytesRead;
state.lastSendByteCount = bytesToSend;
// need to receive atleast first 8 bytes to continue
// Get the rest of the data.
client.BeginReceive(state.buffer, state.totalBytesRead, bytesToSend, 0, new AsyncCallback(ReceiveCallback), state);
return;
}
}
if (state.flag == 1)
{
// we are expexing to process the prefix
if (state.totalBytesRead >= state.prefixLen)
{
// we are good to process
// Lets always assume that our prefixMsg can fit into our prefixbuffer ( we wont send greater than prefixbuffer)
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,state.prefixLen));
string prefixMsg = state.sb.ToString();
state.receivedPath = @"C:\TestReceived.txt";
state.flag++;
int msgRequestBytes = state.msgLen;
if (msgRequestBytes > StateObject.BufferSize)
msgRequestBytes = StateObject.BufferSize;
state.lastSendByteCount = msgRequestBytes;
state.totalBytesRead = 0;
// should be good to process the msg now
// start re-writing to the begining of the buffer since we saved
client.BeginReceive(state.buffer, 0, msgRequestBytes, 0, new AsyncCallback(ReceiveCallback), state);
return;
}
else
{
int bytesToSend = state.lastSendByteCount - bytesRead;
state.lastSendByteCount = bytesToSend;
// request the rest of the prefix
// Get the rest of the data.
client.BeginReceive(state.buffer, state.totalBytesRead, bytesToSend, 0, new AsyncCallback(ReceiveCallback), state);
return;
}
}
// we are expecting to process the file
if (state.flag > 1)
{
// I think here, the binarywriter needs to be locked
if (state.totalBytesRead >= state.msgLen)
{
Console.WriteLine("Writing final {0} bytes to server", bytesRead);
BinaryWriter writer = new BinaryWriter(File.Open(state.receivedPath, FileMode.Append,FileAccess.Write,FileShare.None));
writer.Write(state.buffer, 0, bytesRead);
writer.Close();
Console.WriteLine("Finished reading file");
}
else
{
Console.WriteLine("Reading {0} bytes from server...", bytesRead);
// Padd these bytes
BinaryWriter writer = new BinaryWriter(File.Open(state.receivedPath, FileMode.Append, FileAccess.Write, FileShare.None));
writer.Write(state.buffer, 0, bytesRead);
writer.Close();
// get how many more bytes are left to read
int bytesToSend = state.msgLen - bytesRead;
if (bytesToSend > StateObject.BufferSize)
bytesToSend = StateObject.BufferSize;
client.BeginReceive(state.buffer, 0, bytesToSend, 0, new AsyncCallback(ReceiveCallback), state);
return;
}
}
}
else
{
// All the data has arrived;
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
我的发送很简单,因为我使用的是Socket.BeginSendFile(..);我在这里做的只是添加我的前缀,并发送文件。
private static void Send(Socket handler)
{
string msg = "Fetching...<EOF>";
byte[] prefixMsg = Encoding.ASCII.GetBytes(msg);
FileInfo fi = new FileInfo(@"C:\test.txt");
byte[] fileLen = BitConverter.GetBytes(fi.Length); // The length of the file msg, we will use this to determin stream len
byte[] prefixMsgLen = BitConverter.GetBytes(prefixMsg.Length); // the length of the prefix msg, we will use this to determin head len
// copy out prefix to a prefix byte array
byte[] prefix = new byte[ 4 + 4 + prefixMsg.Length];
fileLen.CopyTo(prefix, 0);
prefixMsgLen.CopyTo(prefix, 4);
prefixMsg.CopyTo(prefix, 8);
// *** Receive design requires prefixmsg.length to fit into prefix buffer
handler.BeginSendFile(fi.FullName, prefix, null, 0, new AsyncCallback(AsynchronousFileSendCallback), handler);
}
非常感谢你的时间。
答案 0 :(得分:2)
从你的问题不清楚 socket 的类型是什么,但我认为它是TCP。
您似乎遇到了TCP编程的经典错误。
当您发送 N 字节时,不能假设您将在第一次读取/接收 N 字节呼叫。 TCP将最终提供 N 字节,但可能需要更多的读/接收调用。调用handler.EndReceive(ar);
答案 1 :(得分:2)
您知道单个读取操作中接收的字节数可能低至1个字节,无论请求的数量是多少?
在这种情况下,您需要再次(异步)读取,直到收到您期望的字节数。
我还建议您在失败时收集更好的数据。考虑将http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx与日志库一起使用以捕获更多信息。同时,您是否看过受影响机器上的Windows应用程序日志?那里应该有一个基本的堆栈跟踪。