在C#中用异步套接字实现FTP客户端的问题

时间:2010-02-17 14:30:45

标签: .net asynchronous ftp sockets

(对不起我的英语不好))

我正在编写一个类,用于使用.NET类对FTP服务器的简单请求 System.Net.Sockets.Socket.Synchronous 函数(Socket.Connect,Socket.Send,Socket.Receive)工作正常。但我需要异步操作才能从外部中断进程。

我使用了MSDN中的一个示例: http://msdn.microsoft.com/en-us/library/bew39x2a%28VS.80%29.aspx与Socket.BeginConnect / EndConnect方法的异步连接顺利进行。连接后我立即使用方法Socket.BeginReceive / EndReceive从服务器收到响应字符串(参见下面的代码)。

接下来,我需要以FTP命令“USER anonymous”的形式发送用户名。 使用方法Socket.BeginSend / EndSend异步发送此字符串 进展顺利。 FTP服务器接受请求,并且应该向我发回字符串 “ 331用户名确定,需要密码 ”。 (为了调试我正在使用本地FTP服务器,从中我可以看到发送了答案的日志)但是由于某种原因使用方法Socket.BeginReceive / EndReceive接收服务器响应失败。

我调用Socket.BeginReceive方法,但是没有相应的回调函数 调用,程序“挂起”等待答案。以下是我用来接收服务器响应的代码片段(_ftp - 我的私有类FtpState的对象,用于在异步操作中存储数据):


...
// Begin receiving data
IAsyncResult ar = (IAsyncResult)_ftp.socket.BeginReceive(
    _ftp.buffer, 0, FtpState.BUFFER_SIZE, 0,
    new AsyncCallback(ReceiveCallback), _ftp);
// Waiting one of the following events:
// - receiving complete
// - external abort event
WaitHandle[] events = new WaitHandle[] { abortEvent, ar.AsyncWaitHandle };
i = WaitHandle.WaitAny(events);
...
// Callback-function on data receive
private static void ReceiveCallback(IAsyncResult ar)
{
    FtpState ftp = (FtpState)ar.AsyncState;
    try
    {
        int size = ftp.socket.EndReceive(ar);
        if (size > 0)
        {
            ftp.sb.Append(Encoding.ASCII.GetString(ftp.buffer, 0, size));
            ftp.socket.BeginReceive(ftp.buffer, 0, FtpState.BUFFER_SIZE, 0,
                new AsyncCallback(ReceiveCallback), ftp);
        }
        else
        {
            ... this code branch never executes at all ... 
            // In MSDN example ManualResetEvent is used to signal
            // the end of data receiving:
            // receiveDone.Set();
            // Instead i am using the event of synchronization object:
            // ar.AsyncWaitHandle (see code above)
            // Actually, i tried MSDN version too, but problem remains
            // as callback-function never calls.
         }
    }
    catch
    {
        ...
    }
}
...

同样,上面的代码可以在连接后立即得到答案。将任何FTP命令发送到服务器后,会出现接收应答的问题。我尝试组合同步/异步方法:使用异步方法连接/发送并使用同步方法接收 - 一切正常。

我还尝试使用类System.Net.Sockets.TcpClient(扩展到Socket类),但异步接收的类似问题仍然存在。搜索网络对我没有帮助。

也许有人遇到过类似的问题?我会很感激任何建议!

PS:使用FtpWebRequest不适合我。

2 个答案:

答案 0 :(得分:2)

是的,我正在回答一个非常老的帖子,但我在使用MSDN示例代码时遇到了同样的问题。

谢尔盖的原始代码:

if (size > 0)
        {
            ftp.sb.Append(Encoding.ASCII.GetString(ftp.buffer, 0, size));
            ftp.socket.BeginReceive(ftp.buffer, 0, FtpState.BUFFER_SIZE, 0,
                new AsyncCallback(ReceiveCallback), ftp);
        }

应更改为:

if (size > 0)
        {
            ftp.sb.Append(Encoding.ASCII.GetString(ftp.buffer, 0, size));
            // Check Available property before passing off control
            if (ftp.socket.Available > 0) 
              ftp.socket.BeginReceive(ftp.buffer, 0, FtpState.BUFFER_SIZE, 0,
                new AsyncCallback(ReceiveCallback), ftp);
            else
              {
                // send the finished result somewhere
                myResult = ftp.sb.ToString();
                // tell your ManualResetEvent you are finished receiving
                receiveDone.Set();
               }
        }

为什么,你问?好吧,正如谢尔盖所​​指出的那样,在服务器发送最终数据后没有调用ReceiveCallback - 如果没有任何东西可以接收,那么回调将永远不会被激活!

这是一个痛苦的发现......

答案 1 :(得分:-1)

It seems i have solved the problem.
I don't know how it should be in other cases, but when sending commands to
FTP-server and receiving answer you should not do as written in MSDN example.
Namely - callback-function does not need to initiate a new client.BeginReceive.
FTP-server answer on command is a string (or several, separated by "\r\n"),
which comes all at once, at a time (at least in my case).
Therefore, my callback-function now looks like this:


        private static void ReceiveCallback(IAsyncResult ar)
        {
            FtpState ftp = (FtpState)ar.AsyncState;
            try
            {
                int size = ftp.socket.EndReceive(ar);
                if (size > 0)
                {
                    ftp.reply = Encoding.ASCII.GetString(ftp.buffer, 0, size);
                }
            }
            catch (SocketException ex)
            {
                // ...
            }
            catch (Exception ex)
            {
                // ...
            }
        }
即我只是将收到的数据转换成一个字符串,认为阅读答案已经完成。