C++编写FTP客户端解析LIST命令获取目录不完整,求解。

李迟
博客专家认证
2011-08-01 04:26:22
背景:
C++编写一FTP客户端,对FTP协议、文章等约了解一些。现在需要列出服务器某目录的文件(及目录),服务器已经完善,不需修改——准确说,服务器是一个硬盘,可以通过FTP访问、操作。

现在上传文件功能已经完成,但列表功能一直出现问题。

LIST命令通:
发送LIST命令(有回应信息)->创建、连接数据连接通路(其中也包括PASV命令),读取该通路上的数据(得到的即目录、文件名称),关闭->回应信息。
示例:
227 Entering Passive Mode (172,18,222,100,16,171).
150 Opening ASCII mode data connection for file list
此处为文件名称
226 Transfer complete.

但是,接收的数据不完整(有时完整)。目前目录文件不多,开辟空间为2048,暂时足够。单步调试时,发现该缓冲区中有完整数据,也能正常输出。但直接运行程序的话,接收不完整。

另外,关闭LIST数据连接后,再次接收命令回应信息(即上面出现的226 Transfer complete.之类的),出现阻塞,用select的话,超时。

向各位大牛请教,如果可以完整接收服务器的文件列表?后期可能会有很多文件列表在同一个目录中,不过目前连几个文件名称都显示不完整。

或许我的思路出路,大家可以说说自己的思路;
或许是代码某些问题,我可以贴代码;
或许是其它问题。

在下向各位请教了。

PS:我经常在Linux那边混的,这个版块才2条裤衩,能力有限,各位不要见怪。
...全文
441 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
李迟 2011-10-28
  • 打赏
  • 举报
回复
原理:FTP返回的文件列表放在缓冲区中,文件名称以'\r\n'区分来,一一查找,并放置到你想放到的地方就行了。
FTP列表命令使用NLST

伪代码:
//先接收一部分(一次不能肯定就完全接收到)
num = recv(buff, ..)
// 添加到string变量中,注意,一定要指定缓冲区的大小!
str.appen(buff, num)
// 先解析这部分的文件或目录
while (str.length())
{
// 解析文件或目录名称
// 我的做法是查找'\n'标志,找到的话,前这个标志前的字符串放到一个队列中(push_back),并且
// 删除已经存放好的字符(erase)
// 如果没找到,不用处理
}

// 这里重复接收,直到接收完毕
while (num != 0)
{
num = recv(buff, ...)
// 下面的代码同上
}

这样的话,那个队列中就是目录或文件的名称了。

由于某些原因,不能公开代码,上面的就是思路。
mimosazhao 2011-10-28
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 subfate 的回复:]

LIST这个问题已经解决了,不过这个程序远远未结束。

现在已经完成了LIST,并解析出其中的文件名称并保存之。由于使用C++的string及deque,目前测试2K个文件没问题(因为暂时还没有那么多文件)。

解决过程还是不写了吧。

多来几个人,好结账给分。

另外同一内容的帖子:http://topic.csdn.net/u/20110801/16/ca5f0841-e5e……
[/Quote]


楼主是怎么解决的呢?和你有同样的问题,求方法
Lliu123abc 2011-10-24
  • 打赏
  • 举报
回复
if(m_link==FALSE)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2,2);
err=WSAStartup(wVersionRequested,&wsaData);
if ( err != 0 )
{
return ;
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 )
{
WSACleanup( );
return ;
}
//创建套接字
control_sock = socket(AF_INET,SOCK_STREAM,0);
if(control_sock==INVALID_SOCKET)
{
AfxMessageBox("Init socket failed");
WSACleanup();
return;
}
//设置IP、端口
DWORD dwIP;
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
CString port;
GetDlgItemText(IDC_EDIT_Port,port);
int nport=atoi(port);

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(dwIP);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(nport);

int nconnect=connect(control_sock, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
Sleep(100);
if(nconnect==-1)
{
SetDlgItemText(IDC_EDIT_CONRESULT,"Order Connect Failed");
return;
}
else
{
m_link=TRUE;
memset(m_recv_buf,0,BUFFERSIZE);
recv(control_sock,m_recv_buf,BUFFERSIZE,0);//服务器会返回 220 的响应码和一些欢迎信息
}
}
//输入用户名
CString sUsername;
GetDlgItemText(IDC_EDIT_UserName,sUsername);
//输入密码
CString sPassword;
GetDlgItemText(IDC_EDIT_PassWord,sPassword);

CString sPassInfo = UserPass(sUsername,sPassword);//正常为 ”230 User logged in, proceed.”,错误时返回“530 ”
CString sPsscode=sPassInfo.Left(3);
if(sPsscode!="230")
{
SetDlgItemText(IDC_EDIT_CONRESULT,"User Or Password Error");
}
else
{
send(control_sock,"PASV \r\n",7,0);
memset(m_recv_buf,0,BUFFERSIZE);
recv(control_sock,m_recv_buf,BUFFERSIZE,0);//正常为:227 entering passive mode (127,0,0,1,4,18)
CString sPASVinfo=m_recv_buf;
AfxMessageBox(sPASVinfo);
send(control_sock,"LIST \r\n",7,0);

CString sPASVcode=sPASVinfo.Left(3);
if(sPASVcode!="227")
{
SetDlgItemText(IDC_EDIT_CONRESULT,"Enter PASV failed");
}
else
{
//计算数据接口值
CString ResultBuf=m_recv_buf;
int i=0,j=0;
if((i=ResultBuf.Find("("))==-1||(j=ResultBuf.Find(")"))==-1)
{
SetDlgItemText(IDC_EDIT_CONRESULT,"Receive Data Port Failed");
}
else
{
CString temp=ResultBuf.Mid(i+1,(j-i)-1);
i=temp.ReverseFind(',');
int data_port=atol(temp.Right(temp.GetLength()-(i+1)));
temp=temp.Left(i);
i=temp.ReverseFind(',');
data_port+=256*atol(temp.Right(temp.GetLength()-(i+1)));

//建立数据连接套接字
data_sock = socket(AF_INET,SOCK_STREAM,0);
DWORD dwIP;
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

SOCKADDR_IN addrData;
addrData.sin_addr.S_un.S_addr=htonl(dwIP);
addrData.sin_family=AF_INET;
addrData.sin_port=htons(data_port);

//连接ftp服务端
connect(data_sock, (SOCKADDR*)&addrData, sizeof(SOCKADDR));
Sleep(100);
}
}

memset(m_recv_buf,0,BUFFERSIZE);
recv(control_sock,m_recv_buf,BUFFERSIZE,0);
AfxMessageBox(m_recv_buf);
memset(m_recv_buf,0,BUFFERSIZE);
recv(data_sock,m_recv_buf,BUFFERSIZE,0);
AfxMessageBox(m_recv_buf);
}
为什么我的LIST收不到文件列表
松月 2011-08-16
  • 打赏
  • 举报
回复
说实话,我是来拿分的!呵呵!
kruskaluu 2011-08-15
  • 打赏
  • 举报
回复
KidLeaf2 2011-08-15
  • 打赏
  • 举报
回复
菜鸟来了
devin_jia 2011-08-15
  • 打赏
  • 举报
回复
只会Java的人帮顶.t
tom6103215 2011-08-15
  • 打赏
  • 举报
回复
只会Java的人帮顶
果-果 2011-08-15
  • 打赏
  • 举报
回复
不懂C++,友情up
李迟 2011-08-15
  • 打赏
  • 举报
回复
其实主要是小弟对网络编程不懂造成的。我这一两年没用过C++,没写过网络程序。
现在C++的话以及网络方面的书慢慢看,用到哪里看哪里。

结账给分。
saber_li 2011-08-15
  • 打赏
  • 举报
回复
我是来收分的呵呵
qq290032431 2011-08-15
  • 打赏
  • 举报
回复
不懂,帮顶,接分
q963181875060 2011-08-15
  • 打赏
  • 举报
回复
好复杂啊....牛人啊
云间雪影 2011-08-15
  • 打赏
  • 举报
回复
顶起。。。。。。
小基 2011-08-15
  • 打赏
  • 举报
回复
我知道你已经解决了。。。
李迟 2011-08-08
  • 打赏
  • 举报
回复
LIST这个问题已经解决了,不过这个程序远远未结束。

现在已经完成了LIST,并解析出其中的文件名称并保存之。由于使用C++的string及deque,目前测试2K个文件没问题(因为暂时还没有那么多文件)。

解决过程还是不写了吧。

多来几个人,好结账给分。

另外同一内容的帖子:http://topic.csdn.net/u/20110801/16/ca5f0841-e5ea-4e47-ae5f-0b4e45f5c4c1.html暂时没人回复,谁有空帮顶一下,好一起结贴。
至善者善之敌 2011-08-02
  • 打赏
  • 举报
回复
我来帮顶。。。。
luciferisnotsatan 2011-08-02
  • 打赏
  • 举报
回复
UDP效率高,但不健壮(可能丢包)。对于丢掉一些信息没问题的应用,比如在线视频,可以用UDP。
luciferisnotsatan 2011-08-02
  • 打赏
  • 举报
回复
可以先把数据压缩了,然后对端再解压缩。这样传送的信息量就少了,但相对的,要占用CPU时间。
healer_kx 2011-08-02
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 subfate 的回复:]

引用 1 楼 healer_kx 的回复:
唉,对FTP协议了解不深。LIST是多次传给你的吗?

应该说是一次,你发送LIST命令后,服务器就会在另一端口发送目录列表,读取就行了,而且,现在数据不量不大,一次收发就可以的。
文件红色字体就是我最奇怪的地方。我对网络编程了解不多,向接收、发送的过程不太了解。正在一边学一边用。
[/Quote]

哦,那理论上你多次同步的Recv就行了。
加载更多回复(7)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧