0

除了有时(仅在 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);


    }

非常感谢您的宝贵时间。

4

2 回答 2

2

您知道在单个读取操作中接收到的字节数可能低至 1 个字节,而不管请求的数量是多少?

在这种情况下,您需要再次(异步)读取,直到收到您期望的字节数。

我还建议您在故障点收集更好的数据。考虑使用带有日志库的http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx来捕获更多信息。同时,您是否查看过受影响机器上的 Windows 应用程序日志?那里应该有一个基本的堆栈跟踪。

于 2013-01-30T23:08:07.733 回答
2

从您的问题中不清楚它是什么类型的套接字,但我会假设它是 TCP。

您似乎遇到了 TCP 编程的经典错误。

例如,当您发送N个字节时,您不能假设您将在第一次调用时读取/接收N个字节。TCP 最终将传递N个字节,但它可能需要更多的读取/接收调用。检查调用时收到的字节数( bytesRead )handler.EndReceive(ar);

于 2013-01-30T23:07:43.347 回答