【通信协议及编码】实验4:MFC框架创建一个FTP和一个通用浏览器

【通信协议及编码】实验4:MFC框架创建一个FTP和一个通用浏览器,第1张

【通信协议及编码】实验4:MFC框架创建一个FTP和一个通用浏览器

文章目录

一、实验目的二、实验环境三、实验内容


一、实验目的
    掌握MFC Win32项目的创建方法创建一个Internet会话以及建立与FTP服务器的连接;用MFC应用程序向导创建文档/视图结构的框架程序;掌握CHtmlView类、CToolBarCtrl类、CReBar类等的用法。
二、实验环境
    *** 作系统:WINDOWS 7及以上开发工具:Microsoft VisualBasic6.0实验设备:PC
三、实验内容

  1.设计一个简易FTP客户机,可以选择登陆的FTP服务器,使用登录名和密码。一旦与服务器建立连接,即显示服务器当前目录列表,列表包括文件名、修改如期和大小,实现的 *** 作包括进入子目录、返回上一级目录、重命名文件、删除文件、上传文件和下载文件。

  2.搭建FT服务器,并与客户机进行联合测试

  3.利用CHtmlView类实现一个浏览器程序,用户可以使用这个自制浏览器访问任何网站、下载文件、提交表单、打开本地文件等,网址可以通过CReBar空间创建的工具条输入并导航到目标资源,可以在主窗口单机各种超链接。

FTP客户机:
FtpClient.h:

#pragma once
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif
#include "resource.h"	
// CFtpClientApp:
class CFtpClientApp : public CWinApp
{
public:
	CFtpClientApp();
	// 重写
public:
	virtual BOOL InitInstance();
	// 实现
	DECLARE_MESSAGE_MAP()
};
extern CFtpClientApp theApp;

FtpClient.cpp:

// FtpClient.cpp : 定义应用程序的类行为。
#include "stdafx.h"
#include "FtpClient.h"
#include "FtpClientDlg.h"
// CFtpClientApp
BEGIN_MESSAGE_MAP(CFtpClientApp, CWinApp)
	ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CFtpClientApp 构造
CFtpClientApp::CFtpClientApp()
{
	// 支持重新启动管理器
	m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
	// TODO: 在此处添加构造代码,
	// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CFtpClientApp 对象
CFtpClientApp theApp;
// CFtpClientApp 初始化
BOOL CFtpClientApp::InitInstance()
{
	INITCOMMonCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// 将它设置为包括所有要在应用程序中使用的
	// 公共控件类。
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);
	CWinApp::InitInstance();
	AfxEnableControlContainer();
	CShellManager *pShellManager = new CShellManager;
	SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
	CFtpClientDlg dlg;
	m_pMainWnd = &dlg;
	INT_PTR nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
		// TODO: 在此放置处理何时用
		//  “确定”来关闭对话框的代码
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: 在此放置处理何时用
		//  “取消”来关闭对话框的代码
	}
	// 删除上面创建的 shell 管理器。
	if (pShellManager != NULL)
	{
		delete pShellManager;
	}

	// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
	//  而不是启动应用程序的消息泵。
	return FALSE;
}

FtpClientDlg.h:

// FtpClientDlg.h : 头文件
#pragma once
#include "Afxinet.h"
// CFtpClientDlg 对话框
class CFtpClientDlg : public CDialogEx
{
public:
	CFtpClientDlg(CWnd* pParent = NULL);	// 标准构造函数
	// 对话框数据
	enum { IDD = IDD_FTPCLIENT_DIALOG };
protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持
protected:
	HICON m_hIcon;
	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedButtonLogin();
	afx_msg void OnBnClickedButtonUpload();
	CString m_strPassword;
	CString m_strServerName;
	CString m_strUserName;
	afx_msg void OnBnClickedButtonRename();
	afx_msg void OnBnClickedButtonQuery();
	afx_msg void OnBnClickedButtonDownload();
	afx_msg void OnBnClickedButtonLogout();

	CInternetSession* m_pFTPSession;
	CFtpConnection* m_pConnection;
	CFtpFileFind* m_pFileFind;
	//遍历目录
	void DisplayContent(LPCTSTR lpctstr,CString currentDir=_T("/"));
	CListCtrl m_listDirectory;
	void Download(void);//下载文件
	void Upload(void);//上传文件
	void Rename(void);//文件改名
	afx_msg void OnBnClickedButtonSubdir();
	afx_msg void OnBnClickedButtonDelete();
	afx_msg void OnBnClickedButtonParentdir();
	void DeleteFile(void);//删除文件
	void DisplaySubDir(void);//显示子目录
	CString GetParentDirectory(CString str);//返回父目录
	void DisplayParentDir(void);//显示父目录
	//连接服务器
	BOOL Connect(CString serverName, CString userName, CString password);
};

FtpClientDlg.cpp:

// FtpClientDlg.cpp : 实现文件

#include "stdafx.h"
#include "FtpClient.h"
#include "FtpClientDlg.h"
#include "afxdialogex.h"
#include "NewNameDlg.h"

// 用于应用程序“关于”菜单项的 CaboutDlg 对话框

class CaboutDlg : public CDialogEx
{
public:
	CaboutDlg();

	// 对话框数据
	enum { IDD = IDD_aboutBOX };

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CaboutDlg::CaboutDlg() : CDialogEx(CaboutDlg::IDD)
{}

void CaboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CaboutDlg, CDialogEx)
END_MESSAGE_MAP()

// CFtpClientDlg 对话框

CFtpClientDlg::CFtpClientDlg(CWnd* pParent )
	: CDialogEx(CFtpClientDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINframe);
	m_strServerName = _T("127.0.0.1");
	m_strUserName = _T("anonymous");
	m_strPassword = _T("");
	m_pFTPSession=NULL;
	m_pConnection = NULL;
	m_pFileFind = NULL;
}

void CFtpClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_PASSWORD, m_strPassword);
	DDX_Text(pDX, IDC_EDIT_SERVERNAME, m_strServerName);
	DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUserName);
	DDX_Control(pDX, IDC_LIST_DIRECTORY, m_listDirectory);
}

BEGIN_MESSAGE_MAP(CFtpClientDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_LOGIN, &CFtpClientDlg::OnBnClickedButtonLogin)
	ON_BN_CLICKED(IDC_BUTTON_UPLOAD, &CFtpClientDlg::OnBnClickedButtonUpload)
	ON_BN_CLICKED(IDC_BUTTON_RENAME, &CFtpClientDlg::OnBnClickedButtonRename)
	ON_BN_CLICKED(IDC_BUTTON_QUERY, &CFtpClientDlg::OnBnClickedButtonQuery)
	ON_BN_CLICKED(IDC_BUTTON_DOWNLOAD, &CFtpClientDlg::OnBnClickedButtonDownload)
	ON_BN_CLICKED(IDC_BUTTON_LOGOUT, &CFtpClientDlg::OnBnClickedButtonLogout)
	ON_BN_CLICKED(IDC_BUTTON_SUBDIR, &CFtpClientDlg::OnBnClickedButtonSubdir)
	ON_BN_CLICKED(IDC_BUTTON_DELETE, &CFtpClientDlg::OnBnClickedButtonDelete)
	ON_BN_CLICKED(IDC_BUTTON_PARENTDIR, &CFtpClientDlg::OnBnClickedButtonParentdir)
END_MESSAGE_MAP()

// CFtpClientDlg 消息处理程序

BOOL CFtpClientDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_aboutBOX 必须在系统命令范围内。
	ASSERT((IDM_aboutBOX & 0xFFF0) == IDM_aboutBOX);
	ASSERT(IDM_aboutBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNamevalid;
		CString straboutMenu;
		bNamevalid = straboutMenu.LoadString(IDS_aboutBOX);
		ASSERT(bNamevalid);
		if (!straboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_aboutBOX, straboutMenu);
		}
	}

	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此 *** 作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	m_listDirectory.InsertColumn(0,_T("文件名"),LVCFMT_CENTER,200);
	m_listDirectory.InsertColumn(1,_T("修改日期"),LVCFMT_CENTER,100);
	m_listDirectory.InsertColumn(2,_T("大小"),LVCFMT_CENTER,200);

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CFtpClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_aboutBOX)
	{
		CaboutDlg dlgabout;
		dlgabout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CFtpClientDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		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;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CFtpClientDlg::OnQueryDragIcon()
{
	return static_cast(m_hIcon);
}

void CFtpClientDlg::OnBnClickedButtonLogin()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
	if (Connect(m_strServerName,m_strUserName,m_strPassword))
	{
		DisplayContent(_T("*"));
		GetDlgItem(IDC_EDIT_SERVERNAME)->EnableWindow(FALSE);
		GetDlgItem(IDC_EDIT_USERNAME)->EnableWindow(FALSE);
		GetDlgItem(IDC_EDIT_PASSWORD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BUTTON_LOGIN)->EnableWindow(FALSE);
	}else //连接失败
	{
		AfxMessageBox(_T("连接服务器失败!"),MB_OK|MB_ICONSTOP);
	}
}

void CFtpClientDlg::OnBnClickedButtonUpload()
{
	// TODO: 在此添加控件通知处理程序代码
	Upload();
}

void CFtpClientDlg::OnBnClickedButtonRename()
{
	// TODO: 在此添加控件通知处理程序代码
	Rename();
}

void CFtpClientDlg::OnBnClickedButtonQuery()
{
	// TODO: 在此添加控件通知处理程序代码

	DisplayContent(_T("*"));
}

void CFtpClientDlg::OnBnClickedButtonDownload()
{
	// TODO: 在此添加控件通知处理程序代码
	Download();
}

//退出
void CFtpClientDlg::OnBnClickedButtonLogout()
{
	// TODO: 在此添加控件通知处理程序代码
	delete m_pFTPSession;
	delete m_pConnection;
	delete m_pFileFind;
	m_pFTPSession=NULL;
	m_pConnection = NULL;
	m_pFileFind = NULL;
	DestroyWindow();
}

//显示服务器当前目下的所有文件与子目录
void CFtpClientDlg::DisplayContent(LPCTSTR lpctstr,CString currentDir)
{
	UpdateData(TRUE);
	Connect(m_strServerName,m_strUserName,m_strPassword);
	m_pConnection->SetCurrentDirectory(currentDir);
	m_listDirectory.DeleteAllItems();
	m_pFileFind=new CFtpFileFind(m_pConnection);
	BOOL bFound;
	bFound=m_pFileFind->FindFile(lpctstr);
	if (!bFound)
	{
		m_pFileFind->Close();
		m_pFileFind=NULL;
		AfxMessageBox(_T("没有找到文件!"),MB_OK | MB_ICONSTOP);
		return;
	}

	CString strFileName;
	CString strFileTime;
	CString strFileLength;

	while(bFound)
	{		
		bFound= m_pFileFind->FindNextFile();

		strFileName=m_pFileFind->GetFileName(); 
		FILETIME ft;
		m_pFileFind->GetLastWriteTime(&ft);   
		CTime FileTime(ft);
		strFileTime = FileTime.Format("%y-%m-%d");    
		if (m_pFileFind->IsDirectory())
		{
			//如果是目录用<子目录>代替
			strFileLength = "<子目录>";
		}	
		else
		{
			//得到文件大小
			ULonGLONG fileSize=m_pFileFind->GetLength();

			if (fileSize<1024)
			{
				strFileLength.Format(_T("%d Bytes"),fileSize);
			}
			else if (fileSize<(1024*1024))
			{
				strFileLength.Format(_T("%3.3f KB"),fileSize/1024.0); 
			}else if (fileSize<(1024*1024*1024)) 
			{
				strFileLength.Format(_T("%3.3f MB"),fileSize/(1024*1024.0));
			}else
			{
				strFileLength.Format(_T("%1.3f GB"),    
					fileSize/(1024.0*1024*1024));   
			}//end if fileSize
		}//end if
		int column=0;
		m_listDirectory.InsertItem(column,strFileName,0);
		m_listDirectory.SetItemText(column,1,strFileTime);
		m_listDirectory.SetItemText(column,2,strFileLength);
		column++;
	}//end while
	UpdateData(FALSE);
}

//下载
void CFtpClientDlg::Download(void)
{
	int index=m_listDirectory.GetNextItem(-1,LVNI_SELECTED); 
	if (index==-1)
	{
		AfxMessageBox(_T("请首先选择要下载的文件!"),MB_OK | MB_ICONQUESTION);
	}
	else
	{
		CString strType=m_listDirectory.GetItemText(index,2);   //得到选择项的类型
		if (strType!="<子目录>")   //选择的是文件
		{
			CString strDestName;
			CString strSourceName;
			strSourceName = m_listDirectory.GetItemText(index,0);//得到所要下载的文件名

			CFileDialog dlg(FALSE,_T(""),strSourceName);		
			if (dlg.DoModal()==IDOK)
			{
				//获得下载文件在本地机上存储的路径和名称
				strDestName=dlg.GetPathName();		
				//调用CFtpConnect类中的GetFile函数下载文件
				if (m_pConnection->GetFile(strSourceName,strDestName))
					AfxMessageBox(_T("下载成功!"),MB_OK|MB_ICONINFORMATION);
				else
					AfxMessageBox(_T("下载失败!"),MB_OK|MB_ICONSTOP);
			}
		}
		else
		{
			//选择的是目录
			AfxMessageBox(_T("不能下载目录!n请重选!"),MB_OK|MB_ICONSTOP);
		}
	}
}

//上传
void CFtpClientDlg::Upload(void)
{
	CString strSourceName;
	CString strDestName;
	CFileDialog dlg(TRUE,_T(""),_T("*.*"));
	if (dlg.DoModal()==IDOK)
	{
		//获得待上传的本地文件路径和文件名
		strSourceName = dlg.GetPathName();
		strDestName = dlg.GetFileName();

		//调用CFtpConnect类中的PutFile函数上传文件
		if (m_pConnection->PutFile(strSourceName,strDestName))
			AfxMessageBox(_T("上传成功"),MB_OK|MB_ICONINFORMATION);
		else
			AfxMessageBox(_T("上传失败"),MB_OK|MB_ICONSTOP);
	}
	DisplayContent(_T("*"));
}

//文件改名
void CFtpClientDlg::Rename(void)
{
	CString strNewName;
	CString strOldName;

	int index=m_listDirectory.GetNextItem(-1,LVNI_SELECTED); //得到CListCtrl被选中的项
	if (index==-1)
	{
		AfxMessageBox(_T("没有选择文件!"),MB_OK | MB_ICONQUESTION);
	}
	else
	{
		strOldName = m_listDirectory.GetItemText(index,0);//得到所选择的文件名
		CNewNameDlg dlg;		
		if (dlg.DoModal()==IDOK)
		{
			strNewName=dlg.m_NewFileName;
			if (m_pConnection->Rename(strOldName,strNewName))
				AfxMessageBox(_T("重命名成功!"),MB_OK|MB_ICONINFORMATION);
			else
				AfxMessageBox(_T("无法重命名!"),MB_OK|MB_ICONSTOP);
		}
	}	
	DisplayContent(_T("*"));//显示目录
}

void CFtpClientDlg::OnBnClickedButtonSubdir()
{
	// TODO: 在此添加控件通知处理程序代码
	DisplaySubDir();
}

void CFtpClientDlg::OnBnClickedButtonDelete()
{
	// TODO: 在此添加控件通知处理程序代码
	DeleteFile();
}

void CFtpClientDlg::OnBnClickedButtonParentdir()
{
	// TODO: 在此添加控件通知处理程序代码
	DisplayParentDir();
}

//删除文件
void CFtpClientDlg::DeleteFile(void)
{
	int index=m_listDirectory.GetNextItem(-1,LVNI_SELECTED); 
	if (index==-1)
	{
		AfxMessageBox(_T("没有选择文件!"),MB_OK | MB_ICONQUESTION);
	}
	else
	{
		CString  strFileName;
		strFileName = m_listDirectory.GetItemText(index,0);
		if (_T("<子目录>")==m_listDirectory.GetItemText(index,2))
		{
			AfxMessageBox(_T("不能删除目录!"),MB_OK | MB_ICONSTOP);
		}
		else
		{
			if (m_pConnection->Remove(strFileName))
				AfxMessageBox(_T("删除成功!"),MB_OK|MB_ICONINFORMATION);
			else
				AfxMessageBox(_T("无法删除!"),MB_OK|MB_ICONSTOP);
		}
	}
	DisplayContent(_T("*"));
}

//显示下级子目录
void CFtpClientDlg::DisplaySubDir(void)
{
	static CString  strCurrentDirectory,strSub;

	m_pConnection->GetCurrentDirectory(strCurrentDirectory);
	strCurrentDirectory.Format(_T("%s/"),(strCurrentDirectory));

	//得到所选择的文本
	int index=m_listDirectory.GetNextItem(-1,LVNI_SELECTED); 
	strSub = m_listDirectory.GetItemText(index,0);

	if (index==-1)
	{
		AfxMessageBox(_T("没有选择目录!"),MB_OK | MB_ICONQUESTION);
	}
	else
	{   //判断是不是目录
		if (_T("<子目录>")!=m_listDirectory.GetItemText(index,2))
		{
			AfxMessageBox(_T("不是子目录!"),MB_OK | MB_ICONSTOP);
		}
		else
		{
			//设置当前目录
			strCurrentDirectory.Format(_T("%s%s"),strCurrentDirectory,strSub);
			m_pConnection->SetCurrentDirectory(strCurrentDirectory);
			//对当前目录进行查询
			DisplayContent(_T("*"),strCurrentDirectory);
		}
	}	
}

//返回上一级目录的字符串表示
CString CFtpClientDlg::GetParentDirectory(CString str)
{
	int LastIndex=0;
	for (int i=0; iGetCurrentDirectory(strCurrentDirectory);
	if (strCurrentDirectory ==_T("/"))
	{
		AfxMessageBox(_T("已经是根目录了!"),MB_OK | MB_ICONSTOP);
	}
	else
	{
		strCurrentDirectory=GetParentDirectory(strCurrentDirectory);
		//设置当前目录
		m_pConnection->SetCurrentDirectory(strCurrentDirectory);
		//对当前目录进行查询
		DisplayContent(_T("*"),strCurrentDirectory);
	}
}

BOOL CFtpClientDlg::Connect(CString serverName, CString userName, CString password)
{
	m_pFTPSession=new CInternetSession(AfxGetAppName(),1,PRE_CONFIG_INTERNET_ACCESS);		
	try
	{
		//试图建立FTP连接
		m_pConnection=m_pFTPSession->GetFtpConnection(serverName,userName,password);      
	}
	catch (CInternetException* e)
	{
		//错误处理
		e->Delete();
		m_pFTPSession=NULL;
		m_pConnection=NULL;
		return FALSE;
	}
	return TRUE;
}

NewNameDlg.h:

#pragma once
// CNewNameDlg 对话框
class CNewNameDlg : public CDialog
{
	DECLARE_DYNAMIC(CNewNameDlg)
public:
	CNewNameDlg(CWnd* pParent = NULL);   // 标准构造函数
	virtual ~CNewNameDlg();
	// 对话框数据
	enum { IDD = IDD_NEWNAMEDLG };
protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedOk();
	CString m_NewFileName;
};

NewNameDlg.cpp:

// NewNameDlg.cpp : 实现文件
#include "stdafx.h"
#include "FtpClient.h"
#include "NewNameDlg.h"
#include "afxdialogex.h"
// CNewNameDlg 对话框
IMPLEMENT_DYNAMIC(CNewNameDlg, CDialog)
	CNewNameDlg::CNewNameDlg(CWnd* pParent )
	: CDialog(CNewNameDlg::IDD, pParent)
{
	m_NewFileName = _T("");
}
CNewNameDlg::~CNewNameDlg()
{}
void CNewNameDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_NEWFILENAME, m_NewFileName);
}
BEGIN_MESSAGE_MAP(CNewNameDlg, CDialog)
	ON_BN_CLICKED(IDOK, &CNewNameDlg::OnBnClickedOk)
END_MESSAGE_MAP()
// CNewNameDlg 消息处理程序
void CNewNameDlg::OnBnClickedOk()
{
	// TODO: 在此添加控件通知处理程序代码
	CDialog::OnOK();
	UpdateData(TRUE);
}

运行结果:
简易FTP服务器与客户机:

浏览器程序:


欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5711607.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存