工大后院

 找回密码
 加入后院

扫一扫,访问微社区

QQ登录

只需一步,快速开始

搜索
查看: 6976|回复: 21

《文件传输》课程设计(VC)

[复制链接]
发表于 2006-6-4 16:06 | 显示全部楼层 |阅读模式
坚决响应Wool王的号召,写个原创。
不过发现要专门为了发帖去写有点懒,那就把我上学期做的文件传输课程设计发出来吧。
不过,既然是课程设计,严谨度自然跟正式项目没得比,大家权且看看就行了。
还有,我水平有限,有错很正常,欢迎大家指正

实验要求:
   
  • 使用Socket实现点对点传输
  • 可以自定义监听端口
  • 可以对远程文件浏览并选择下载
  • 可以上传本地文件到远程
      


实验环境
Windows XP + Visual Studio .Net + C++
在Windows XP + Visual C++ 6.0 下亦调试通过


效果图


序列图


在上图中:
ServerDialog   是服务端的UI,但实际上,我把服务端跟客户端都合在一起了。
ListenSocket    这是服务端进行监听的Socket
ServerTransfer   这是服务端进行数据和命令传输的Socket
ClientTransfer     这是客户端进行数据和命令舆的Socket
ClientDialog     这是客户端UI。由于前面已经说了,这个各户中,服务端跟客户端合在一起了,所以虽然概念上ServerDialog和ClientDialog是两个不同的东西,但实际上是同一个.

未完待续
发表于 2006-6-4 17:40 | 显示全部楼层
期待……(虽然现在看不懂……)
回复

使用道具 举报

发表于 2006-6-4 19:12 | 显示全部楼层
楼主真给面子,哈哈。

期待ing。。。

暂时没看出楼主要实现什么,大概是一个类似飞鸽传书的软件吧。等楼主贴完再好好研究研究。
回复

使用道具 举报

发表于 2006-6-6 10:10 | 显示全部楼层
楼主终于有搞作了。good


PS:论坛的发展有赖师弟师妹的努力啦。我们老了,有空会回来看看的。
回复

使用道具 举报

发表于 2006-6-6 13:06 | 显示全部楼层
hjack :我们老了……

呵呵

师兄说说在QQ做些什么工作呀……  也让我们知道以后要用到什么……有个方向
回复

使用道具 举报

发表于 2006-6-6 16:26 | 显示全部楼层
ps:楼主开个头就走掉,快变水了......
回复

使用道具 举报

 楼主| 发表于 2006-6-6 21:53 | 显示全部楼层
在上一部分中,介绍了程序的功能,还有大致的实现思路,现在进入具体实现。

首先,我们需要一个东西来承载我们传输的东西。于是定义了一个CMessage类。以下是定义


  1.         virtual void Serialize( CArchive &ar );  //序列化到CARchive中,或从中读取
  2.         CString m_Content;                //包含内容,文件内容,命令内容
  3.         CString m_FilePath;                             //文件目录,仅在传输文件的时候使用
  4.         //0表示发送命令
  5.         //1表示文件列表
  6.         //2表示发送文件
  7.         //3表示取得文件
  8.         int m_Type;

复制代码

其中的CArchive是一个序列化的载体,它可以用于进行网络传输。也就是说,CMessage在传输的时候会被序列化为CArchive,然后传输。接收的时候也是从CArchive后序列化过来。


OK。有了载体后,需要有些东西来处理这个载体了。
首先当然是服务端的监听Socket了,监听Socket的第一个动作就是开始监听

  1. bool CListenSocket::Start( CMyFtp2Dlg* dlg )
  2. {
  3.         m_Dlg = dlg;
  4.         int port = dlg->GetDlgItemInt( IDC_PORT );
  5.        
  6.         if( !(this->Create( port )) )
  7.                 return false;
  8.         if( !(this->Listen() ))
  9.                 return false;
  10.         return true;
  11. }
复制代码

这个方法的作用就是接收来自Dialog的端口,然后监听这个端口。很简单的动作。
监听当然不可能什么事都不做了,监听到了需要有些反应,于是便需要一个反应动作

  1. void CListenSocket::OnAccept( int nErrorCode )
  2. {
  3.         m_Dlg->OnAccept();
  4.         CSocket::OnAccept( nErrorCode );
  5. }
复制代码

这里又再调用了Dialog里的反应方法,因为某些原因,反应的具体动作放在Dialog会比较好,以下是Dialog里的反应方法:

  1. void CMyFtp2Dlg::OnAccept()
  2. {
  3.         if( !m_Listen.Accept( m_ServerTransfer ) )                //应答连接,转接到m+ServerTransfer
  4.         {
  5.                 AfxMessageBox( "Accept Error" );
  6.                 return;
  7.         }

  8.         m_ServerTransfer.Init( this );                //服务端TransferSocket初始化

  9.         //取得来访者信息
  10.         CString cIP;
  11.         UINT cPort;
  12.         m_Listen.GetPeerName( cIP, cPort );
  13.         cIP.Format( "%s:%d连接成功", cIP, cPort );
  14.         AfxMessageBox( cIP );

  15.         //传输默认目录文件列表到客户端
  16.         CString files;
  17.         GetFiles( m_DefaultPath, m_MyFiles );              //取得默认目录的文件列表
  18.         ArrayToString( m_MyFiles, files );                     //把文件列表转化为字符串的形式
  19.         CMessage msg;
  20.         msg.m_Content = files;
  21.         msg.m_Type = 1;
  22.         m_ServerTransfer.SendMessage( &msg );        //将文件列表发送出去
  23. }
复制代码

这个方法做的任务就是把连接者转给传输Socket(ServerTransfer),相当说ListenSocket只是一个接线员,其实没人找它(可怜的家伙),这就是为什么要把实际的反应动作放在Dialog的原因啦,因为ListenSocket根本不知道ServerTransfer的存在。然后ServerTransfer进行初始化。再然后就是把本地文件列表发给客户端了。

        到这里,完成了让客户端得到本地文件列表的功能。


         先这样,我写实验报告先。

未完待续
回复

使用道具 举报

发表于 2006-6-7 13:06 | 显示全部楼层
顶一下……
回复

使用道具 举报

发表于 2006-6-21 14:04 | 显示全部楼层
楼主又匿了……

顶上来……
回复

使用道具 举报

发表于 2006-6-22 00:30 | 显示全部楼层
FTP的设计我们也做了,不过当时用的是命令行界面
回复

使用道具 举报

发表于 2006-6-22 01:16 | 显示全部楼层
顶!楼主半途而废!!!
回复

使用道具 举报

 楼主| 发表于 2006-6-22 16:18 | 显示全部楼层
哈哈,不用担心, 半途而废不是我的风格.只是最近比较忙,所以没时间顾上而已,今天又回来啦..

OK,在上一部分中,我讲客户端获得了远程的文件列表,接下来就进对远程文件列表的操作吧.

远程文件显示在一个列表控件里面,双击其中的一个项,作出反应.如果是目录,就进入目录,如果是文件就把文件下载到本地.


  1. void CMyFtp2Dlg::OnNMDblclkYourlist(NMHDR *pNMHDR, LRESULT *pResult)
  2. {
  3.         CString name;
  4.         CString size;
  5.         int row = m_YourList.GetNextItem(-1,LVNI_ALL | LVNI_SELECTED);  //取得被双击列表项
  6.         name = m_YourList.GetItemText( row, 0 );    //取得列表项的文件名字段
  7.         size = m_YourList.GetItemText( row, 1 );                 //取得列表项的长度字段

  8.         int pos;
  9.         int length = atoi( size );
  10.         CMessage msg;
  11.         msg.m_Type = 0;              //设置传输类型,
  12.         if( length == 0 )                //当长度为0时,判断为目录,单击了远程目录,当然是进入那个目录,浏览下面的文件.
  13.         {
  14.                 if( name == "." )
  15.                         m_RemotePath = "";
  16.                 else if( name == ".." )
  17.                 {
  18.                         m_RemotePath.TrimRight( '/' );
  19.                         pos = m_RemotePath.ReverseFind( '/' );
  20.                         if( pos > -1 )
  21.                                 m_RemotePath = m_RemotePath.Left( pos ) + "/";
  22.                         else
  23.                                 m_RemotePath = "";
  24.                 }
  25.                 else
  26.                         m_RemotePath += name + "/";
  27.                 msg.m_Content = "cd " + m_RemotePath;           //生成取得远程文件列表的命令
  28.         }
  29.         else
  30.                 msg.m_Content = "get " + m_RemotePath + name;                 //生成取得远程文件到本地的命令
  31.         if( !m_ClientTransfer.SendMessage( &msg ) )                                   //发送命令
  32.                 AfxMessageBox( "命令发送失败" );

  33.         *pResult = 0;
  34. }
复制代码



应该说,这个文件传输程序的下载功能就到这里了.
下载功能完成了,上传功能也就简单了,只是发送的方向不同而已,看代码:


  1. void CMyFtp2Dlg::OnNMDblclkMylist(NMHDR *pNMHDR, LRESULT *pResult)
  2. {
  3.         CString name;
  4.         CString size;
  5.         int row = m_MyList.GetNextItem(-1,LVNI_ALL | LVNI_SELECTED);
  6.         name = m_MyList.GetItemText( row, 0 );
  7.         size = m_MyList.GetItemText( row, 1 );
  8.        
  9.         int pos;
  10.         int length = atoi( size );
  11.         if( length == 0 )
  12.         {
  13.                 if( name == "." )
  14.                         m_CurrentPath = m_DefaultPath;
  15.                 else if( name == ".." )
  16.                 {
  17.                         m_CurrentPath.TrimRight( '/' );
  18.                         pos = m_CurrentPath.ReverseFind( '/' );
  19.                         m_CurrentPath = m_CurrentPath.Left( pos ) + "/";
  20.                 }
  21.                 else
  22.                 {
  23.                         m_CurrentPath += name + "/";
  24.                 }
  25.                 if( m_CurrentPath.GetLength() < m_DefaultPath.GetLength() )
  26.                         m_CurrentPath = m_DefaultPath;
  27.                 FillMyList( m_CurrentPath );
  28.         }
  29.         else
  30.         {
  31.                 if( m_ClientTransfer.SendFile( m_CurrentPath + name, m_RemotePath + name ) )
  32.                         AfxMessageBox( name + " 发送成功" );
  33.                 else
  34.                         AfxMessageBox( name + " 发送失败" );
  35.         }
  36.         *pResult = 0;
  37. }

复制代码


跟下载文件的差不多,就不多解释了.
好滴,整个框架就这么完成了.


问题是,现在还有一些细节没有提到,
1,如何响应命令
2,如何发送文件
3,如何接受文件

这三个问题, 留待下回分解.
回复

使用道具 举报

发表于 2006-6-23 14:37 | 显示全部楼层
这三个好像才是重点啊..........晕
回复

使用道具 举报

 楼主| 发表于 2006-7-3 00:12 | 显示全部楼层
版主在提醒我更新了...
呵呵,最近忙考试,考完再更新,
顺便发多我几个课程设计上来.
回复

使用道具 举报

发表于 2006-7-7 23:24 | 显示全部楼层
楼上的,新斑竹好看得起你哦。。。

居然置顶了。。。

别半途而废,,,share点自己的经验。。。
回复

使用道具 举报

发表于 2006-9-30 21:24 | 显示全部楼层
初接触SOCKET。。。

楼主介不介意把源代码所有文件上传上来共享?
回复

使用道具 举报

发表于 2007-1-3 09:26 | 显示全部楼层
强烈要求师兄放源码,师弟好学习下
回复

使用道具 举报

发表于 2007-1-3 11:02 | 显示全部楼层
我去请楼主过来

楼上两位耐心等一下
回复

使用道具 举报

 楼主| 发表于 2007-1-9 00:18 | 显示全部楼层
原帖由 pigcanfly 于 2007-1-3 09:26 发表
强烈要求师兄放源码,师弟好学习下


eggcanfly跟你是什么关系?
回复

使用道具 举报

 楼主| 发表于 2007-1-9 00:37 | 显示全部楼层

半年过去了,我继续。。。。差点忘了。。

截止到上回,我已经把这个软件的大体框架说明白了,接下来就是一点点细节,细节的东西,说白了就是核心代码啦。
所以,这部分大部分是代码了。
  这一部分要解决以下三个问题:
1,如何响应命令
2,如何发送文件
3,如何接受文件
  前面已经说了,CMessage是承载客户端与服务器端消息传送的类,其中包括命令和文件数据都是通过它来传送的。


  响应命令的过程是这样的:Server收到Message(并且这个Message承载的是命令,通过Message里的Type来判断)->解析命令->执行命令

以下的方法是接收Message用的,然后判断是什么类型的函数,然后做出响应,我们后注释为“命令处理”那个方法

  1. // CTransferSocket 成员函数
  2. void CTransferSocket::OnReceive( int nErrorCode )
  3. {
  4. CMessage msg;
  5. msg.Serialize( *m_In );
  6. msg.m_Content.MakeLower();
  7. switch( msg.m_Type )
  8. {
  9. case 0:
  10.   PerformCommand( msg.m_Content );//命令处理
  11.   break;
  12. case 1:
  13.   m_Dlg->FileYourList( msg.m_Content );//填充文件列表
  14.   break;
  15. case 2:
  16. case 3:
  17.   if( !SaveFile( msg ) )//保存文件
  18.    AfxMessageBox( "文件保存失败" );
  19.   else
  20.    if( m_IsClient)
  21.     AfxMessageBox( "文件保存成功" );
  22.   break;
  23. }
  24. }
复制代码


下面的执行命令和发送文件到客户端的具体实现:

  1. void CTransferSocket::PerformCommand( const CString& cmd )
  2. {
  3. CMessage msg;
  4. CString cmdName;
  5. CString cmdPara;
  6. SplitCommand( cmd, cmdName, cmdPara );  //分解命令
  7. if( cmdName == "cd" )  //进入目录命令
  8. {
  9.   CString path = m_Dlg->m_DefaultPath + cmdPara;
  10.   if( path.GetLength() < m_Dlg->m_DefaultPath.GetLength())
  11.    path = m_Dlg->m_DefaultPath;
  12.   CString files;
  13.   CStringArray array;
  14.   m_Dlg->GetFiles( path, array );  //读取目录下的文件,把文件信息放到array中
  15.   m_Dlg->ArrayToString( array, files );//将array转化为字符串,方便传输
  16.   msg.m_Content = files;
  17.   msg.m_Type = 1;
  18.   SendMessage( &msg ); //发回客户端
  19. }
  20. else if( cmdName == "get" ) //客户端下载文件命令
  21. {
  22.   CString filePath = m_Dlg->m_DefaultPath + cmdPara;
  23.   SendFile( filePath ); //发回客户端
  24. }
  25. //可以继续增加"else if"来增加新的原语,但原语量大的时候,就不应该用Else If这么简单的策略了。
  26. else
  27. {
  28.   return;
  29. }
  30. }

复制代码


发送文件的具体代码,再次重申,这里只是简单实现,完全没有考虑性能:

  1. bool CTransferSocket::SendFile( CString filePath, CString remotePath )
  2. {
  3. CMessage msg;
  4. CFile file( filePath ,CFile::modeRead|CFile::shareDenyWrite);
  5. byte* buffer = NULL;
  6. long count = file.GetLength();
  7. buffer = new byte[count];
  8. file.Read( buffer, count );
  9. if( remotePath != "" )
  10. {
  11.   msg.m_FilePath = remotePath;
  12.   msg.m_Type = 2;
  13. }
  14. else
  15. {
  16.   int pos = filePath.ReverseFind( '/' );
  17.   if( pos == -1 )
  18.    pos = filePath.GetLength();
  19.   else
  20.    pos = filePath.GetLength() - pos - 1;
  21.   msg.m_FilePath = filePath.Right( pos );
  22.   msg.m_Type = 3;
  23. }
  24. msg.m_Content = buffer;
  25. if( this->SendMessage( &msg ) )
  26. {
  27.   file.Close();
  28.   return true;
  29. }
  30. else
  31. {
  32.   file.Close();
  33.   return false;
  34. }
  35. }
复制代码


再看保存文件的代码:

  1. bool CTransferSocket::SaveFile( const CMessage& msg )
  2. {
  3. CString filePath;
  4. if( msg.m_Type == 2 )
  5.   filePath = m_Dlg->m_DefaultPath + msg.m_FilePath;
  6. else
  7.   filePath = m_Dlg->m_CurrentPath + msg.m_FilePath;
  8. if( !m_File.Open( filePath ,CFile::modeWrite|CFile::modeCreate) )
  9. {
  10.   m_File.Close();
  11.   return false;
  12. }
  13. m_File.Write( msg.m_Content, msg.m_Content.GetLength() );
  14. m_File.Close();
  15. return true;
  16. }
复制代码


细节也讲完了。。。

打完收功。。。。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入后院

本版积分规则

QQ|Archiver|手机版|小黑屋|广告业务Q|工大后院 ( 粤ICP备10013660号 )

GMT+8, 2024-5-1 02:55

Powered by Discuz! X3.5

Copyright © 2001-2024 Tencent Cloud.

快速回复 返回顶部 返回列表