// NewFtpClientDlg.h : header file
//
#if !defined(AFX_NEWFTPCLIENTDLG_H__7DE54983_D072_4771_946E_4AD8CCBE4BC6__INCLUDED_)
#define AFX_NEWFTPCLIENTDLG_H__7DE54983_D072_4771_946E_4AD8CCBE4BC6__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/
// CNewFtpClientDlg dialog
#include "Download.h"
class CNewFtpClientDlg : public CDialog
{
// Construction
public:
LRESULT OnUpdateFileStatus( WPARAM wParam, LPARAM lParam);
LRESULT OnAddServerFile( WPARAM wParam, LPARAM lParam);
//保存服务器ip的字符串
CString m_ServerIPStr;
//保存当前下载的文件在服务器端的路径的链表
CStringList m_ServerPathList;
//负责启动下载进程的类
CDownLoad m_DownLoad;
CNewFtpClientDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CNewFtpClientDlg)
enum { IDD = IDD_NEWFTPCLIENT_DIALOG };
CListCtrl m_ServerFileList;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CNewFtpClientDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CNewFtpClientDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnConnectbutton();
afx_msg void OnDblclkList1(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_NEWFTPCLIENTDLG_H__7DE54983_D072_4771_946E_4AD8CCBE4BC6__INCLUDED_)
// NewFtpClientDlg.cpp : implementation file
//
#include "stdafx.h"
#include "NewFtpClient.h"
#include "NewFtpClientDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CNewFtpClientDlg dialog
CNewFtpClientDlg::CNewFtpClientDlg(CWnd* pParent /*=NULL*/)
: CDialog(CNewFtpClientDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CNewFtpClientDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CNewFtpClientDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CNewFtpClientDlg)
DDX_Control(pDX, IDC_LIST1, m_ServerFileList);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CNewFtpClientDlg, CDialog)
//{{AFX_MSG_MAP(CNewFtpClientDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_CONNECTBUTTON, OnConnectbutton)
ON_MESSAGE( WM_ADDSERVERFILE, OnAddServerFile )
ON_NOTIFY(NM_DBLCLK, IDC_LIST1, OnDblclkList1)
ON_MESSAGE( WM_UPDATEFILESTATUS, OnUpdateFileStatus )
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CNewFtpClientDlg message handlers
BOOL CNewFtpClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//注意设置listcontrol的属性: styles 选项卡设置为Report!!!!!!
int iColumnWidth = 50;
m_ServerFileList.InsertColumn( 0, "文件名", LVCFMT_LEFT, 3 * iColumnWidth, -1 );
m_ServerFileList.InsertColumn( 1, "文件大小(字节)", LVCFMT_LEFT, 2 * iColumnWidth, -1 );
m_ServerFileList.InsertColumn( 2, "完成百分比", LVCFMT_LEFT, 2 * iColumnWidth, -1 );
m_ServerFileList.InsertColumn( 3, "下载速率", LVCFMT_LEFT, 2 * iColumnWidth, -1 );
m_ServerFileList.InsertColumn( 4, "下载状态", LVCFMT_LEFT, 2 * iColumnWidth, -1 );
m_ServerFileList.SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES );
m_ServerPathList.RemoveAll();
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CNewFtpClientDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CNewFtpClientDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CNewFtpClientDlg::OnConnectbutton()
{
//获得用户输入的ip地址
GetDlgItem (IDC_IPADDRESS)->GetWindowText( m_ServerIPStr );
//发送连接请求
m_DownLoad.SendListRequest( m_ServerIPStr );
}
//将wParam中指明的FileInfo中的文件加入到客户端的list控件中
LRESULT CNewFtpClientDlg::OnAddServerFile( WPARAM wParam, LPARAM lParam )
{
FileInfo* ReceivedFileInfo = (FileInfo*)wParam;
CString strPathName ( ReceivedFileInfo->filepath );
m_ServerPathList.AddTail ( strPathName );
CString strFileName ( ReceivedFileInfo->filename );
long lFileLength = ReceivedFileInfo->len;
//否则将文件加入到文件列表中
CString strFileSize;
_ltoa( lFileLength , strFileSize.GetBuffer(0), 10 );
int iItem = m_ServerFileList.GetItemCount();
LV_ITEM lvi;
lvi.mask = LVIF_TEXT|LVIF_PARAM;
lvi.iItem = iItem;
lvi.iSubItem = 0;
lvi.lParam = lFileLength;
lvi.pszText = strFileName.GetBuffer(0);
m_ServerFileList.InsertItem( &lvi );
//将用户选择的文件加入到文件链表控件中,设置文件大小和文件路径等信息
m_ServerFileList.SetItemText( iItem, 1, strFileSize.GetBuffer(0) );
//m_FileListCtrl.SetItemText( iItem, 2, strPathName.GetBuffer(0) );
return 0;
}
//双击一项后出现“另存为”的文件对话框
void CNewFtpClientDlg::OnDblclkList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLISTVIEW* pListView = (NMLISTVIEW*)pNMHDR;
int iSel = pListView->iItem;
if( iSel == -1 )
{
return;
}
//获取文件名
CString strFileName = m_ServerFileList.GetItemText(iSel, 0);
CString LengthStr = m_ServerFileList.GetItemText(iSel, 1);
//TRACE(" 获得的文件长度是: %s \n", LengthStr);
long FileLength = atol ( LengthStr );
//找到用户选择的文件在链表中的位置,获取该文件对应的服务器端路径
POSITION position = m_ServerPathList.GetHeadPosition();
CString currentServerPath = "";
while ( position != NULL )
{
currentServerPath = m_ServerPathList.GetAt( position );
if ( currentServerPath.Find( strFileName ) != -1 )
{
break;
}
m_ServerPathList.GetNext( position );
}
if ( currentServerPath == "" )
{
AfxMessageBox("错误!服务器路径链表中找不到您选中的文件!");
return;
}
//这里的currentServerPath保存了用户选中的文件在server端的物理路径
//出现一个另存为对话框,让用户选择文件保存路径
CFileDialog dlg ( FALSE, NULL, strFileName.GetBuffer(0),
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, "All Files(*.*)|*.*||", this);
if ( dlg.DoModal() != IDOK )
{
return;
}
//这里的strClientPath保存了用户要保存到本地的路径
CString strClientPath = dlg.GetPathName();
//创建该文件,初始大小为0
CFile file;
BOOL bOpen = file.Open(strClientPath, CFile::modeCreate|CFile::modeWrite, NULL);
if ( !bOpen )
{
MessageBox("文件创建或打开失败!请确认路径再重试!");
return;
}
file.Close();
//开4个线程下载用户选择的文件
m_DownLoad.StartDownLoad( currentServerPath, FileLength, strClientPath );
*pResult = 0;
}
//更新wParam参数中的
LRESULT CNewFtpClientDlg::OnUpdateFileStatus(WPARAM wParam, LPARAM lParam)
{
UpdateParam *up = (UpdateParam*)wParam;
CString ServerPathName ( up->ServerFilePath);
//根据wParam参数中的ServerPathName域,在list控件中找到对应的项
POSITION position = m_ServerPathList.GetHeadPosition();
CString currentServerPath = "";
int i = 0;
while ( position != NULL )
{
currentServerPath = m_ServerPathList.GetAt( position );
if ( currentServerPath == ServerPathName )
{
break;
}
m_ServerPathList.GetNext( position );
++i;
}
if ( i >= m_ServerFileList.GetItemCount() )
{
AfxMessageBox("错误!没有找到需要下载的文件!!");
return -1;
}
//TRACE(" 找到的list控件中的 i = %d \n", i );
//下载百分比
CString PercentStr;
PercentStr.Format( "%d %%", up->Percent );
//下载速率
CString VelocityStr;
VelocityStr.Format("%d k/s", up->Velocity );
//状态字符串
CString DownLoadStatusStr = "下载中......";
if ( up->Percent >= 100 )
{
DownLoadStatusStr = "下载完毕!";
}
//更新list控件
m_ServerFileList.SetItemText( i, 2, PercentStr );
m_ServerFileList.SetItemText( i, 3, VelocityStr );
m_ServerFileList.SetItemText( i, 4, DownLoadStatusStr );
return 0;
}
#ifndef CDOWNLOAD_H
#define CDOWNLOAD_H
//下载进程用到的数据结构
struct DownLoadThreadParam
{
FileInfo fileinfo;
//workno指示了当前是第几个任务
int workno;
};
//保存用户下载的任务的一个数据结构
struct WorkItem
{
int workno;
long finished[ DOWNLOAD_THREAD_NUMBER + 1 ];
};
//更新list控件时用到的数据结构
struct UpdateParam
{
char ServerFilePath[MAX_PATH];
int Percent;
int Velocity;
};
class CDownLoad
{
public:
CDownLoad();
~CDownLoad();
//向服务器发送文件列表的请求
int SendListRequest( CString IPStr );
int StartDownLoad( CString ServerPathName, long FileLength, CString LocalPathName);
private:
static int readn( SOCKET ClientSideSocket, char* buf, int len);
static int sendn ( SOCKET ClientSideSocket, char* bp, int len);
//使用类的函数创建thread是可以的,只要把这个函数按如下声明:
//static dword winapi funcname (LPVOID lparam )
static DWORD WINAPI RecvFileInfoThread( LPVOID lParam );
static DWORD WINAPI RecvFileThread( LPVOID lParam );
static DWORD WINAPI UpdateListThread( LPVOID lParam );
private:
//保存了最后下载的任务在WorkList中的位置
int TotalWorks;
};
#endif
#include "stdafx.h"
#include "newftpclient.h"
#include "Download.h"
//全局变量,保存当前下载的文件任务
WorkItem WorkList[ MAX_DOWNLOAD_FILES];
//服务器ip字符串
CString ServerIPStr;
CDownLoad::CDownLoad()
{
memset ( WorkList, 0, MAX_DOWNLOAD_FILES * sizeof( WorkItem) );
TotalWorks = 0;
}
CDownLoad::~CDownLoad()
{
}
int CDownLoad::SendListRequest ( CString IPStr )
{
sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons ( SERVERPORT );
local.sin_addr.S_un.S_addr = inet_addr( IPStr );
SOCKET ClientSideSocket = socket ( AF_INET, SOCK_STREAM, 0 );
int ret = connect ( ClientSideSocket, (LPSOCKADDR)&local, sizeof(local) );
if ( ret < 0 )
{
AfxMessageBox("服务器连接不上!");
return -1;
}
ServerIPStr = IPStr;
FileInfo ListRequest;
ListRequest.type = LIST_REQUEST;
//发送文件列表请求
sendn ( ClientSideSocket, (char*)&ListRequest, sizeof(FileInfo) );
//创建一个接受线程来接受服务器发来的文件名和文件大小
DWORD dwthread;
::CreateThread( NULL, 0, RecvFileInfoThread, (LPVOID)ClientSideSocket, 0, &dwthread );
return 0;
}
//接收服务器发来的文件的文件名和大小等信息,并在列表框中显示出来
DWORD WINAPI CDownLoad::RecvFileInfoThread( LPVOID lParam )
{
FileInfo ReceivedFileInfo;
SOCKET ClientSideSocket = (SOCKET)lParam;
int ret = 0;
for ( ; ; )
{
ret = recv( ClientSideSocket, (char*)&ReceivedFileInfo, sizeof(FileInfo), 0 );
if ( ret > 0 )
{
//在list控件中显示
::SendMessage( ::AfxGetMainWnd()->GetSafeHwnd(),
WM_ADDSERVERFILE, (WPARAM)&ReceivedFileInfo, NULL );
}
else if ( ret <= 0 )
{
return -1;
}
}
return 0;
}
//开4个线程下载文件
int CDownLoad::StartDownLoad( CString ServerPathName, long FileLength, CString LocalPathName)
{
FileInfo FileRequest;
FileRequest.type = FILE_REQUEST;
strcpy ( FileRequest.filepath, ServerPathName );
strcpy ( FileRequest.filename, LocalPathName );
DWORD dwthread;
//最大只能开50个任务,如果在开多了,就把原先的内容给清除了!!!!!!!!
if ( TotalWorks > MAX_DOWNLOAD_FILES )
{
TotalWorks = 0;
}
//将新下载任务加入到WorkList中
WorkItem wi;
wi.workno = TotalWorks;
memset ( wi.finished, 0 , DOWNLOAD_THREAD_NUMBER * sizeof( long ) );
wi.finished[ DOWNLOAD_THREAD_NUMBER ] = FileLength;
WorkList[TotalWorks] = wi;
// 如果文件长度小于1M,则开一个线程,否则开 DOWNLOAD_THREAD_NUMBER 个线程
if ( FileLength < FILE_SIZE_THRESHOLD )
{
FileRequest.seek = 0;
FileRequest.len = FileLength;
DownLoadThreadParam * pdp = new DownLoadThreadParam;
pdp->fileinfo = FileRequest;
pdp->workno = TotalWorks;
//创建一个接受线程来接受服务器发来的文件名和文件大小
::CreateThread( NULL, 0, RecvFileThread,
pdp, 0, &dwthread );
}
else // 如果文件长度大于1M
{
//将文件长度分成4份,每个线程下载 1 / 4
//SeekStartPos数组保存了每个线程请求的文件起始位置
//和剩余的大小
long SeekStartPos[ DOWNLOAD_THREAD_NUMBER * 2 ];
long FileSection = FileLength / DOWNLOAD_THREAD_NUMBER;
for ( int i = 0; i < DOWNLOAD_THREAD_NUMBER ; ++i )
{
SeekStartPos[ 2 * i] = FileSection * i;
SeekStartPos[ 2 * i + 1] = FileSection;
}
//最后一个的seeklength应该是剩余的所有长度
SeekStartPos[ DOWNLOAD_THREAD_NUMBER * 2 - 1 ]
= FileLength - FileSection * ( DOWNLOAD_THREAD_NUMBER - 1 );
//开DOWNLOAD_THREAD_NUMBER个线程
for ( i = 0; i < DOWNLOAD_THREAD_NUMBER; ++i )
{
FileRequest.seek = SeekStartPos[ 2 * i];
FileRequest.len = SeekStartPos[ 2 * i + 1];
DownLoadThreadParam * pdp = new DownLoadThreadParam;
pdp->fileinfo = FileRequest;
pdp->workno = TotalWorks;
//TRACE("创建线程第i个: i = %d \n", i );
::CreateThread( NULL, 0, RecvFileThread,
pdp, 0, &dwthread );
} // end of for
} // end of 文件长度大于1M
//创建更新list控件线程
DownLoadThreadParam * pUpdatedp = new DownLoadThreadParam;
strcpy( pUpdatedp->fileinfo.filepath, ServerPathName );
pUpdatedp->workno = TotalWorks;
::CreateThread( NULL, 0, UpdateListThread,
pUpdatedp, 0, &dwthread );
++TotalWorks;
return 0;
}
//接收服务器发来的文件
DWORD WINAPI CDownLoad::RecvFileThread( LPVOID lParam )
{
sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons ( SERVERPORT );
local.sin_addr.S_un.S_addr = inet_addr( ServerIPStr );
SOCKET ClientSideSocket = socket ( AF_INET, SOCK_STREAM, 0 );
int ret = connect ( ClientSideSocket, (LPSOCKADDR)&local, sizeof(local) );
if ( ret < 0 )
{
AfxMessageBox("服务器连接不上!");
return -1;
}
DownLoadThreadParam* dp = (DownLoadThreadParam*)lParam;
FileInfo RecvFileInfo = dp->fileinfo;
//发送文件下载的请求
sendn ( ClientSideSocket, (char*)&RecvFileInfo, sizeof(FileInfo) );
int workno = dp->workno;
CFile RecvFile;
RecvFile.Open( RecvFileInfo.filename,
CFile::modeWrite | CFile::typeBinary | CFile::shareDenyNone );
RecvFile.Seek( RecvFileInfo.seek , CFile::begin );
int section = WorkList[workno].finished[DOWNLOAD_THREAD_NUMBER]
/ DOWNLOAD_THREAD_NUMBER;
int sectionno = RecvFileInfo.seek / section ;
//TRACE("线程 %d 开始运行.\n", sectionno );
char* buffer = new char [BUFFER_SIZE];
int CharLeft = RecvFileInfo.len;
int CurrentCount = 0;
int rc = 0;
//一直读取数据,直到把1/4个文件大小都读取完了
while ( CharLeft > 0 )
{
CurrentCount = CharLeft > BUFFER_SIZE ? BUFFER_SIZE : CharLeft;
rc = readn ( ClientSideSocket, buffer, CurrentCount );
if ( rc < 0 )
{
closesocket( ClientSideSocket );
break;
}
//TRACE("线程 %d 写入文件%d个字符.\n", sectionno , rc );
RecvFile.Write( buffer, rc );
WorkList[workno].finished[sectionno] += rc;
CharLeft -= rc;
}
RecvFile.Close();
delete []buffer;
delete dp;
return 0;
}
//更新list控件中的下载百分比
DWORD WINAPI CDownLoad::UpdateListThread( LPVOID lParam )
{
DownLoadThreadParam* dp = (DownLoadThreadParam*)lParam;
int workno = dp->workno;
//DownLoadCount保存了已经下载的文件的大小
long DownLoadCount = 0;
//precount保存了1秒之前下载的文件的大小
long PreCount = 0;
//当前时间
long CurrentTick = 0;
//之前的时间,用来算是否超过了一秒钟
long PreTick = 0;
//完成百分比
double Percent = 0.0;
//下载速率
int Velocity = 0;
//一直更新直至文件读取完毕
for ( ; ; )
{
CurrentTick = GetTickCount();
//如果过了一秒钟,更新一下list控件
if ( CurrentTick - PreTick >= 1000 )
{
PreTick = CurrentTick;
DownLoadCount = 0;
for ( int i = 0; i < DOWNLOAD_THREAD_NUMBER; ++i )
{
//4个线程下载的数据之和
DownLoadCount += WorkList[workno].finished[i];
}
//速率以k为单位
Velocity = ( DownLoadCount - PreCount ) / 1024;
PreCount = DownLoadCount;
Percent = (double)DownLoadCount / WorkList[workno].finished[DOWNLOAD_THREAD_NUMBER];
//将上面算好的所有数据保存到UpdateParam中的相应数据里面
UpdateParam up;
up.Percent = (int) ( Percent * 100 ) ;
strcpy ( up.ServerFilePath, dp->fileinfo.filepath );
up.Velocity = Velocity;
//发送更新list控件消息,更新控件
::SendMessage( ::AfxGetMainWnd()->GetSafeHwnd(),
WM_UPDATEFILESTATUS, (WPARAM)&up, NULL );
//如果已经下载完了,则跳出循环
if ( DownLoadCount >= WorkList[workno].finished[DOWNLOAD_THREAD_NUMBER] )
{
break;
}
}
Sleep( 1000 );
}
delete dp;
return 0;
}
int CDownLoad::sendn(SOCKET ClientSideSocket, char *bp, int len)
{
int CharLeft = len;
int rc = 0;
while ( CharLeft > 0 )
{
rc = send ( ClientSideSocket, bp, CharLeft, 0 );
if ( rc < 0 )
{
AfxMessageBox("客户端发送数据失败!");
return -1;
}
if ( rc == 0 )
{
return len - CharLeft;
}
bp += rc;
CharLeft -= rc;
}
return len;
}
int CDownLoad::readn( SOCKET ClientSideSocket, char *buf, int len )
{
int CharLeft = len;
int rc = 0;
while ( CharLeft > 0 )
{
rc = recv ( ClientSideSocket, buf, CharLeft, 0 );
if ( rc < 0 )
{
AfxMessageBox("客户端接收数据失败!");
return -1;
}
if ( rc == 0 )
{
return len - CharLeft;
}
buf += rc;
CharLeft -= rc;
}
return len;
}
MFC:一个简单的多线程传送文件的实现 client端(2)
最新推荐文章于 2016-12-21 15:44:51 发布