异步套接字在本地工作但不通过互联网工作

时间:2013-01-30 22:43:19

标签: c# sockets asyncsocket

除了有时候(到目前为止只注意到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对象,希望它能更好地管理清理,并且它似乎正在工作:)没有一个失败的测试全部天。现在是时候处理服务器端的异常了!谢谢大家的帮助。

enter image description here

    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);


    }

非常感谢你的时间。

2 个答案:

答案 0 :(得分:2)

从你的问题不清楚 socket 的类型是什么,但我认为它是TCP。

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

当您发送 N 字节时,不能假设您将在第一次读取/接收 N 字节呼叫。 TCP将最终提供 N 字节,但可能需要更多的读/接收调用。调用handler.EndReceive(ar);

时,检查收到的字节数( bytesRead

答案 1 :(得分:2)

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

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

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