除了有时(仅在 vista 上注意到)之外,它真的很好用
我正在尝试创建一个强大的 Async Socket.BeginReceive 进程。目前我所做的只是连接到服务器,服务器确认连接并向客户端发送文件。客户端解析前缀,并通过 BinaryWriter 处理文件。
TCP ****BufferSize = 1024****
编辑:重新设计功能以使其更强大 工作流程如下;
发送: - 我发送 8 字节前缀数据包,这是两个整数。(第一个 Int 是预期的文件大小,第二个 int 是预期的前缀大小,然后是文件本身。
收到:
在毫无疑问地收到前 8 个字节后,我处理将前 4 个字节转换为整数(文件大小字节长度)的前缀,然后将接下来的 4 个字节转换为整数(前缀大小字节长度)
然后,我无疑会从缓冲区接收前缀大小字节长度,并处理我的前缀。
然后我开始接收基于文件大小字节长度存储在前缀消息中的文件。
问题:在本地一切正常。我在发送和接收后测试了校验和和文件数据,一切看起来都很好。
然而商业环境(在vista上注意到),偶尔(并不总是,大部分时间传输成功)我会得到一个 System.IO.IOException: The process cannot access the file '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);
}
非常感谢您的宝贵时间。