時々(これまでのところビスタでのみ気づいた)を除いて、それは本当にうまく機能しています
堅牢な非同期Socket.BeginReceiveプロセスを作成しようとしています。現在、私がしているのはサーバーに接続することだけです。サーバーは接続を確認し、ファイルをクライアントに送信します。クライアントはプレフィックスを解析し、BinaryWriterを介してファイルを処理します。
TCP **** BufferSize = 1024 ****
編集:より堅牢にするために機能を作り直しました ワークフローは次のとおりです。
送信: -2つの整数である8バイトのプレフィックスパケットを送信します。(最初のIntは予想されるファイルサイズ、2番目のintは予想されるプレフィックスサイズ、次にファイル自体が次のサイズです。
受け取る:
間違いなく最初の8バイトを受け取った後、最初の4バイトを整数(ファイルサイズのバイト長)に変換するプレフィックスを処理し、次の4バイトを整数(プレフィックスサイズのバイト長)に変換します
次に、間違いなくバッファからプレフィックスサイズのバイト長を受け取り、プレフィックスを処理します。
次に、プレフィックスメッセージに格納されているファイルサイズのバイト長に基づいてファイルの受信を開始します。
問題:すべてがローカルで正常に機能します。送受信後にチェックサムとファイルデータをテストしましたが、すべて問題ないようです。
ただし、商用環境(ビスタで通知)では、時々(常にではありませんが、ほとんどの場合、送信が成功します)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);
}
お時間をいただき、誠にありがとうございます。