#PRagma once#include <afxsock.h>
#define WM_FILELIST WM_USER + 101 //接收文件列表#define WM_FILEUPDATE WM_USER + 102 //反映文件下载状态#define WM_FILEDOWNLOAD WM_USER + 103 //发送文件列表
#define FILELIST 1000 //请求文件列表#define FILEUPDATE 2000 //请求文件上传#define FILEDOWNLOAD 3000 //请求文件下载
#define RECVSIZE 1024*16 //一次性发送/接收的数据块大小
//与服务器通信语言结构typedef struct{ int iCommand; long lFileOffset; long lFileLength; char sFilePath[128];}MSGFILE;
//请求线程参数typedef struct{ char sip[15]; //IP地址 int iPort; //端口 MSGFILE msgFile; //通信语言结构 LPVOID ptr; //指针}MSGPARAM;
//文件列表结构typedef struct{ long lFileLength; char sFilePath[128];}MSGFILELIST;
//反映文件状态的结构typedef struct{ long lFileOffset;//偏移 char sFilePath[128];//路径}MSGFILESTATUS;
//接收线程参数typedef struct{ SOCKET sock; LPVOID ptr;}PARAMRECV;
//服务器端 类 头文件
// Ftp_Server.h: interface for the CFtp_Server class.////////////////////////////////////////////////////////////////////////
#if !defined(AFX_FTP_SERVER_H__DCFE7BE8_C212_475E_AAC8_811898047B46__INCLUDED_)#define AFX_FTP_SERVER_H__DCFE7BE8_C212_475E_AAC8_811898047B46__INCLUDED_
#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000
#include "basedef.h"#define WM_FINISHUPDATE WM_USER+401
class CFtp_Server {public: CFtp_Server(); virtual ~CFtp_Server();
BOOL StartListen(CWnd *pWnd, int nPort, char *sPath); void StopListen();
protected: //监听线程 static DWord WINAPI ListenThread(LPVOID lpParam); //接收线程 static DWORD WINAPI RecvThread(LPVOID lpParam);private: //监听线程结束符 BOOL m_bEndListenThread;
//监听端口 int m_iListenPort;
//窗口指针 CWnd* m_pWnd;
//服务器套接字 SOCKET m_socket;
//保存本机地址 SOCKADDR_IN m_sin;
//存储路径 char m_sPath[128];};
#endif // !defined(AFX_FTP_SERVER_H__DCFE7BE8_C212_475E_AAC8_811898047B46__INCLUDED_)
//服务器端 类 CPP
// Ftp_Server.cpp: implementation of the CFtp_Server class.////////////////////////////////////////////////////////////////////////
#include "stdafx.h"#include "Ftp_Server.h"
#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#define new DEBUG_NEW#endif
//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////
CFtp_Server::CFtp_Server(){}
CFtp_Server::~CFtp_Server(){}
BOOL CFtp_Server::StartListen(CWnd *pWnd, int nPort, char *sPath){ //初始化套接字 if (!AfxSocketInit()) { return FALSE; }
//获得端口号和窗口指针 m_iListenPort = nPort; m_pWnd = pWnd; strcpy(m_sPath, sPath);
m_bEndListenThread = FALSE;//开始监听
DWORD id; HANDLE h = CreateThread(NULL, 0, ListenThread, this, 0, &id);//创建句柄 //线程要执行的函数为ThreadListen,将this指针传递给线程,线程ID为id,创建成功,返回新线程的一个句柄 CloseHandle(h); //关闭句柄,并不终止线程
return TRUE;}
void CFtp_Server::StopListen(){ m_bEndListenThread = TRUE; Sleep(100); closesocket(m_socket);}
DWORD WINAPI CFtp_Server::ListenThread(LPVOID lpParam){ CFtp_Server* pServer = (CFtp_Server*)lpParam;
SOCKET sockListen = socket(AF_INET, SOCK_STREAM, 0);//创建socket //地址家族为internet,用于TCP和UDP,字节流,特定的协议
SOCKADDR_IN sin; //转换地址 sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; //ip地址,以网路字节顺序。 sin.sin_port = htons(pServer->m_iListenPort);
if (bind(sockListen, (SOCKADDR*)&sin, sizeof(sin)) == SOCKET_ERROR) { closesocket(sockListen); //关闭socket return FALSE; }
if (listen(sockListen, 5) == SOCKET_ERROR) {//若将socket置于监听失败 closesocket(sockListen); return FALSE; }
fd_set fdListen;//结构体,将socket放进一个集合 timeval seltime;//结构体,指定时间值(秒+毫秒) seltime.tv_sec = 0; //秒 seltime.tv_usec = 10000;//毫秒==10秒
while (!pServer->m_bEndListenThread) {//服务器在监听状态 FD_ZERO(&fdListen);//将集合清零 FD_SET(sockListen, &fdListen);//将socket添加到set中
if (select(0, &fdListen, NULL, NULL, &seltime) <= 0 || !FD_ISSET(sockListen, &fdListen)) continue; //如果在规定时间内,set中无socket可读出||socket不在set中,结束本次循环,继续下次循环
int len = sizeof(sin); SOCKET sock = accept(sockListen, (SOCKADDR*)&sin, &len); //接受socket上的连接
PARAMRECV* pParamRecv = new PARAMRECV;//结构体,接收线程参数 pParamRecv->sock = sock; pParamRecv->ptr = pServer;
DWORD id; HANDLE h = CreateThread(NULL, 0, RecvThread, pParamRecv, 0, &id); //创建ThreadRecv线程,将pParamRecv传递给线程 CloseHandle(h); }
return 0;}
DWORD WINAPI CFtp_Server::RecvThread(LPVOID lpParam){ PARAMRECV* pParam = (PARAMRECV*)lpParam;//指向接收线程的指针 CFtp_Server* pServer = (CFtp_Server*)pParam->ptr;//指向Server的指针
fd_set fdRecv;//结构体,接收线程集合 timeval seltime;//结构体,指定时间值==10s seltime.tv_sec = 0; seltime.tv_usec = 10000;
while (1) { FD_ZERO(&fdRecv);//集合清零 FD_SET(pParam->sock, &fdRecv);//将已连接的socket加入集合
if (select(0, &fdRecv, NULL, NULL, &seltime) <= 0 || !FD_ISSET(pParam->sock, &fdRecv)) continue; //如果在指定时间内,结构体内无可读接收线程||sock不在结构体内
MSGFILE msgFile; int iRecvCnt = recv(pParam->sock, (char*)&msgFile, sizeof(msgFile), 0); //成功,返回接收字节数;正常结束,返回0;否则<0 if (iRecvCnt <= 0) break;
//请求文件列表 if (msgFile.iCommand == FILELIST) { while (!pServer->m_bEndListenThread) { MSGFILELIST msgFileList; iRecvCnt = recv(pParam->sock, (char*)&msgFileList, sizeof(msgFileList), 0); if (iRecvCnt <= 0) break;
pServer->m_pWnd->SendMessage(WM_FILELIST, (WPARAM)&msgFileList, 0); } }
//请求文件下载 else if (msgFile.iCommand == FILEDOWNLOAD) { long lFileOffset = msgFile.lFileOffset;//文件偏移
CFile file; CString strPath, strName; strPath.Format("%s", msgFile.sFilePath); int iPos = strPath.ReverseFind('//'); strName = strPath.Right(strPath.GetLength() - iPos - 1); strPath.Format("%s", pServer->m_sPath); strPath += strName;
BOOL bResult = file.Open(strPath, CFile::modeRead | CFile::shareDenyNone, NULL); //打开指定路径的文件,只读||可共享,成功,返回非零;否则,0 if (!bResult) { break; }
char sSendBuf[RECVSIZE];//发送缓冲区,16K while (lFileOffset < msgFile.lFileLength) { int iSeek = file.Seek(lFileOffset, CFile::begin);//寻找新的文件偏移,距离开始处 int iReadCnt = file.Read(sSendBuf, RECVSIZE); if (iReadCnt == 0) break;
int iSendCnt = send(pParam->sock, sSendBuf, iReadCnt, 0); //如果发送失败就终止线程 if (iSendCnt == -1) break; else lFileOffset += iSendCnt;//修改偏移量 }
file.Close(); }
//请求文件上传 else if (msgFile.iCommand == FILEUPDATE) { char sRecvBuf[RECVSIZE] = "/0"; //接收缓冲区置空 long lFileOffset = msgFile.lFileOffset; CFile file;
//将客户端路径改为服务端保存路径 CString strPath, strName; strPath.Format("%s", msgFile.sFilePath); int iPos = strPath.ReverseFind('//'); strName = strPath.Right(strPath.GetLength() - iPos - 1); strPath.Format("%s", pServer->m_sPath); strPath += strName;
BOOL bResult = file.Open(strPath, CFile::modeCreate | CFile::modeWrite | CFile::modeNoTruncate, NULL); //打开接收端文件,写模式||若创建的文件已存在,不截短。 while (bResult && lFileOffset < msgFile.lFileLength) { int iRecvCnt = recv(pParam->sock, sRecvBuf, RECVSIZE, 0); if (iRecvCnt <= 0) break;
file.Seek(lFileOffset, CFile::begin); file.Write(sRecvBuf, iRecvCnt); lFileOffset += iRecvCnt;//修改偏移
MSGFILESTATUS msgFileStatus;//文件状态结构体 msgFileStatus.lFileOffset = lFileOffset; strcpy(msgFileStatus.sFilePath, msgFile.sFilePath); pServer->m_pWnd->SendMessage(WM_FILEUPDATE, (WPARAM)&msgFileStatus, 0); } file.Close(); pServer->m_pWnd->SendMessage(WM_FINISHUPDATE, 0, 0); }
break; } delete pParam;
return 0;}
//对话框 头文件
// Server_FTPDlg.h : header file//
#if !defined(AFX_SERVER_FTPDLG_H__492B251E_B713_4E0F_A5A4_C1BC9EB8EF08__INCLUDED_)#define AFX_SERVER_FTPDLG_H__492B251E_B713_4E0F_A5A4_C1BC9EB8EF08__INCLUDED_
#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000
#include "Ftp_Server.h"/////////////////////////////////////////////////////////////////////////////// CServer_FTPDlg dialog
class CServer_FTPDlg : public CDialog{// Constructionpublic: void InitListColumn(); CServer_FTPDlg(CWnd* pParent = NULL); // standard constructor
CFtp_Server *m_pFtp_Server;
BOOL m_bConnected; //已连接 int m_nUpdateNum; //已上传文件// Dialog Data //{{AFX_DATA(CServer_FTPDlg) enum { IDD = IDD_SERVER_FTP_DIALOG }; CListCtrl m_ListFile; int m_nPort; //}}AFX_DATA
// ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CServer_FTPDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL
// Implementationprotected: HICON m_hIcon;
// Generated message map functions //{{AFX_MSG(CServer_FTPDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnBtnListen(); afx_msg void OnBtnScan(); afx_msg void OnDestroy(); //}}AFX_MSG afx_msg LRESULT OnFileList(WPARAM wp, LPARAM lp); afx_msg LRESULT OnFileUpdate(WPARAM wp, LPARAM lp); afx_msg LRESULT OnFinishUpdate(WPARAM wp, LPARAM lp);
DECLARE_MESSAGE_MAP()};
//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_SERVER_FTPDLG_H__492B251E_B713_4E0F_A5A4_C1BC9EB8EF08__INCLUDED_)
//对话框 CPP文件
// Server_FTPDlg.cpp : implementation file//
#include "stdafx.h"#include "Server_FTP.h"#include "Server_FTPDlg.h"
#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif
/////////////////////////////////////////////////////////////////////////////// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog{public: CAboutDlg();
// Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA
// ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL
// Implementationprotected: //{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP()};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD){ //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT}
void CAboutDlg::DoDataExchange(CDataExchange* pDX){ CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAPEND_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////// CServer_FTPDlg dialog
CServer_FTPDlg::CServer_FTPDlg(CWnd* pParent /*=NULL*/) : CDialog(CServer_FTPDlg::IDD, pParent){ //{{AFX_DATA_INIT(CServer_FTPDlg) m_nPort = 7000; //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_bConnected = FALSE; m_nUpdateNum = 0;}
void CServer_FTPDlg::DoDataExchange(CDataExchange* pDX){ CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CServer_FTPDlg) DDX_Control(pDX, IDC_LIST_FILE, m_ListFile); DDX_Text(pDX, IDC_EDIT_PORT, m_nPort); //}}AFX_DATA_MAP}
BEGIN_MESSAGE_MAP(CServer_FTPDlg, CDialog) //{{AFX_MSG_MAP(CServer_FTPDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_ERASEBKGND() ON_BN_CLICKED(IDC_BTN_LISTEN, OnBtnListen) ON_BN_CLICKED(IDC_BTN_SCAN, OnBtnScan) ON_WM_DESTROY() //}}AFX_MSG_MAP ON_MESSAGE(WM_FILELIST, OnFileList) ON_MESSAGE(WM_FILEUPDATE, OnFileUpdate) ON_MESSAGE(WM_FINISHUPDATE, OnFinishUpdate)END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////// CServer_FTPDlg message handlers
BOOL CServer_FTPDlg::OnInitDialog(){ CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } }
// 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 InitListColumn();
m_pFtp_Server = new CFtp_Server(); // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control}
void CServer_FTPDlg::OnSysCommand(UINT nID, LPARAM lParam){ if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); }}
// 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 CServer_FTPDlg::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 CServer_FTPDlg::OnQueryDragIcon(){ return (HCURSOR) m_hIcon;}
BOOL CServer_FTPDlg::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default //更改背景颜色 CRect rect; GetClientRect(&rect); pDC->FillSolidRect(&rect, RGB(202, 225, 243)); return TRUE; // return CDialog::OnEraseBkgnd(pDC);}
void CServer_FTPDlg::OnBtnListen() { // TODO: Add your control notification handler code here if (m_bConnected) { m_pFtp_Server->StopListen(); SetDlgItemText(IDC_BTN_LISTEN, _T("启动监听")); GetDlgItem(IDC_EDIT_PORT)->EnableWindow(TRUE); m_bConnected = FALSE; return; } CString sPath; GetDlgItemText(IDC_EDIT_PATH, sPath); if (sPath.IsEmpty()) { AfxMessageBox(_T("请先设置文件存储路径!")); return; } int nPort = GetDlgItemInt(IDC_EDIT_PORT); if (m_pFtp_Server->StartListen(this, nPort, sPath.GetBuffer(0))) { SetDlgItemText(IDC_BTN_LISTEN, _T("停止监听")); GetDlgItem(IDC_EDIT_PORT)->EnableWindow(FALSE); m_bConnected = TRUE; return; }
AfxMessageBox(_T("服务器启动失败!"));}
void CServer_FTPDlg::OnBtnScan() { // TODO: Add your control notification handler code here BROWSEINFO bi; //定义后面将被SHBrowseForFolder调用参数 char buffer[MAX_PATH]; ZeroMemory(buffer,MAX_PATH); bi.hwndOwner = this->m_hWnd; bi.pidlRoot = NULL; bi.pszDisplayName=buffer; bi.lpszTitle = "指定文件夹"; bi.ulFlags = BIF_RETURNONLYFSDIRS; bi.lpfn = NULL; bi.lParam = 0; bi.iImage = 0; LPITEMIDLIST pList = NULL; if((pList=SHBrowseForFolder(&bi)) != NULL) { char path[MAX_PATH]; ZeroMemory(path,MAX_PATH); SHGetPathFromIDList(pList,path);//根据你设置的属性 打开 返回路径到path SetDlgItemText(IDC_EDIT_PATH, path); }}
void CServer_FTPDlg::InitListColumn(){ int iWidth = 50; m_ListFile.InsertColumn(0, "文件名", LVCFMT_LEFT, 3*iWidth, -1); m_ListFile.InsertColumn(1, "文件大小", LVCFMT_LEFT, 2*iWidth, -1); m_ListFile.InsertColumn(2, "上传速度", LVCFMT_LEFT, 2*iWidth, -1); m_ListFile.InsertColumn(3, "上传状态", LVCFMT_LEFT, 2*iWidth, -1); m_ListFile.InsertColumn(4, "上传时间", LVCFMT_LEFT, 2*iWidth, -1); m_ListFile.InsertColumn(5, "文件保存路径", LVCFMT_LEFT, 5*iWidth, -1); m_ListFile.InsertColumn(6, "时间标记", LVCFMT_LEFT, 2*iWidth, -1); //插入列:左对齐,无子列 m_ListFile.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);}
void CServer_FTPDlg::OnDestroy() { CDialog::OnDestroy(); // TODO: Add your message handler code here if(m_pFtp_Server != NULL) { m_pFtp_Server->StopListen(); delete m_pFtp_Server; }}
LRESULT CServer_FTPDlg::OnFileUpdate(WPARAM wp, LPARAM lp){ MSGFILESTATUS* pmsg = (MSGFILESTATUS*)wp; CString strPath; strPath.Format("%s", pmsg->sFilePath); int iTotal = m_ListFile.GetItemCount(); int iSel = 0; for(; iSel<iTotal; iSel++) { CString strListPath; strListPath = m_ListFile.GetItemText(iSel, 5); if(strPath.Compare(strListPath) == 0)//路径相同 break; } if(iSel == iTotal) return 1;
long lFileLength = m_ListFile.GetItemData(iSel); CString strStatus; strStatus.Format("%0.2f%%", (float)pmsg->lFileOffset/lFileLength*100); //显示状态 %,精确到小数点后两位 m_ListFile.SetItemText(iTotal-1, 3, strStatus.GetBuffer(0)); //显示时间 DWORD dwNowTick, dwStartTick;//毫秒 dwNowTick = GetTickCount(); sscanf(m_ListFile.GetItemText(iSel, 6), "%u", &dwStartTick); long iEllapsed = (dwNowTick-dwStartTick);//间隔:微秒 strStatus.Format("%dms", iEllapsed); m_ListFile.SetItemText(iSel, 4, strStatus.GetBuffer(0)); //显示速度 if(iEllapsed > 0) { strStatus.Format("%dk/s", (pmsg->lFileOffset/1024/iEllapsed)*1000); m_ListFile.SetItemText(iSel, 2, strStatus.GetBuffer(0)); }
return 0;}
LRESULT CServer_FTPDlg::OnFileList(WPARAM wp, LPARAM lp){ MSGFILELIST* pmsgList = (MSGFILELIST*)wp; int iItem = m_ListFile.GetItemCount(); for (int i = m_nUpdateNum; i < iItem; i++) { m_ListFile.DeleteItem(i); } iItem = m_nUpdateNum;
CString strServerPath, strFileName, strFileSize; strServerPath.Format("%s", pmsgList->sFilePath); int iPos = strServerPath.ReverseFind('//'); //设置文件名到缓冲区 strFileName = strServerPath.Right(strServerPath.GetLength()-iPos-1); strFileSize.Format("%0.2fk", (float)pmsgList->lFileLength/1024); LV_ITEM lvi; lvi.mask = LVIF_TEXT|LVIF_PARAM; lvi.iItem = iItem; lvi.iSubItem = 0; lvi.lParam = pmsgList->lFileLength; //显示 lvi.pszText = strFileName.GetBuffer(0); m_ListFile.InsertItem(&lvi); m_ListFile.SetItemText(iItem, 1, strFileSize.GetBuffer(0)); m_ListFile.SetItemText(iItem, 5, strServerPath.GetBuffer(0));
//设置初始时间点 DWORD dwStartTick = GetTickCount(); char sStartTick[20]; sprintf(sStartTick, "%u", dwStartTick); m_ListFile.SetItemText(iItem, 6, sStartTick);
return 0;}
LRESULT CServer_FTPDlg::OnFinishUpdate(WPARAM wp, LPARAM lp){ ++ m_nUpdateNum; return 0;}
新闻热点
疑难解答
图片精选