版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
目录TOC\o"1-4"1需求分析 31.1课程设计目的 31.2课程设计内容 31.3课程设计要求 32概要设计 32.1原理概述 32.2运行环境 32.3基本设计思路 32.4功能模块设计 33详细设计 33.1程序流程 33.2主要算法 53.3主要数据结构 63.4主要函数说明 64用户使用手册 75项目分析与总结 75.1项目分析 75.2总结与建议 8附录A源程序代码文件说明 8附录B参考文献 20需求分析1.1课程设计目的加深对TCP/IP的理解,熟悉socket编程。课程设计内容实现一个扫描器,使用TCPconnect进行端口扫描,并把扫描到的结果记录下来。课程设计要求Windows或Linux环境下,程序在单机上运行;使用端口扫描对一台主机进行扫描,并显示出结果;对一个网段进行IP扫描,显示出结果;提供友好的用户界面。概要设计2.1原理概述TCPconnect扫描是最基本的扫描,操作系统提供的connect()系统调用,用来与每一个感兴趣的目标计算机的端口进行连接。如果端口处于侦听状态,那么connect()就能成功。否则,这个端口是不能用的,即没有提供服务。2.2运行环境Windowsxp,VC++6.02.3基本设计思路创建一个CSocket套接字,通过CSocket的Connect函数测试该主机的某个端口是否能够连通,获得该端口的打开状态。2.4功能模块设计(1)测试主机某个端口是否打开;(2)“扫描结果”标题栏;(3)扫描单个或多个端口的单选按钮;(4)“扫描”,“停止”,“保存”按钮。3.详细设计3.1程序流程开始扫描开始扫描初始化扫描单个端口?Tm_bSinglePort=true;m_cSinglePort.EnableWindow();m_cPortFrom.EnableWindow(false);m_cPortTo.EnableWindow(false);m_cBtnStop.EnableWindow(false);Fm_bSinglePort=false;m_cSinglePort.EnableWindow(false);m_cPortFrom.EnableWindow();m_cPortTo.EnableWindow();m_cBtnStop.EnableWindow(false);设置端口开始侦听端口有错误?显示结果FT设置下一端口扫描结束3.2主要算法(1)测试主机某个端口是否打开的函数TestConnection():BOOLCMyDlg::TestConnection(CStringIP,UINTnPort){CSocket*pSocket; pSocket=newCSocket; ASSERT(pSocket); if(!pSocket->Create()) {deletepSocket; pSocket=NULL; returnfalse; } while(!pSocket->Connect(IP,nPort)) {deletepSocket; pSocket=NULL; returnfalse; } pSocket->Close(); deletepSocket; returntrue;}(2)响应单选按钮“扫描单个端口”和“扫描多个端口”的单击消息:voidCMyDlg::OnRadio1Single(){m_bSinglePort=true; m_cSinglePort.EnableWindow(); m_cPortFrom.EnableWindow(false); m_cPortTo.EnableWindow(false); m_cBtnStop.EnableWindow(false);}voidCMyDlg::OnRadio2Range(){m_bSinglePort=false; m_cSinglePort.EnableWindow(false); m_cPortFrom.EnableWindow(); m_cPortTo.EnableWindow(); m_cBtnStop.EnableWindow(false);}(3)显示列表框标题栏的成员函数://增加列表框标题栏的某一列BOOLCMyDlg::AddColumn(LPCTSTRstrItem,intnItem,intnSubItem,intnMask,intnFmt){LV_COLUMNlvc; lvc.mask=nMask; lvc.fmt=nFmt; lvc.pszText=(LPTSTR)strItem; lvc.cx=m_cResult.GetStringWidth(lvc.pszText)+25; if(nMask&LVCF_SUBITEM) {if(nSubItem!=-1) lvc.iSubItem=nSubItem; else lvc.iSubItem=nItem; } returnm_cResult.InsertColumn(nItem,&lvc);}//在列表框中加一条BOOLCMyDlg::AddItem(intnItem,intnSubItem,LPCTSTRstrItem,intnImageIndex){LV_ITEMlvItem; lvItem.mask=LVIF_TEXT; lvItem.iItem=nItem; lvItem.iSubItem=nSubItem; lvItem.pszText=(LPTSTR)strItem; if(nImageIndex!=-1) {lvItem.mask|=LVIF_IMAGE; lvItem.iImage|=LVIF_IMAGE; } if(nSubItem==0) returnm_cResult.InsertItem(&lvItem); returnm_cResult.SetItem(&lvItem);}//加一列标题栏字符voidCMyDlg::AddHeader(LPTSTRhdr){if(m_pColumns) m_pColumns->AddTail(hdr);}//显示列表框标题栏voidCMyDlg::ShowHeaders(){intnIndex=0; POSITIONpos=m_pColumns->GetHeadPosition(); while(pos) {CStringhdr=(CString)m_pColumns->GetNext(pos); AddColumn(hdr,nIndex++); }}(4)“扫描”,“停止”,“保存”按钮,见附录程序。3.3主要数据结构及主要函数说明(1)测试主机某个端口是否打开——TestConnection函数;(2)“扫描结果”标题栏成员函数——AddHeader,AddColumn,ShowHeaders;在列表框中添加字符串的函数——AddItem;(3)扫描单个或多个端口的单选按钮——OnRadioSingle()和OnRadioRange();(4)“扫描”,“停止”,“保存”按钮——OnButton1Start(),OnButton2Stop(),OnButton3Save();4.用户使用手册输入要扫描的主机IP地址,选择“扫描单个端口”或“扫描多个端口”,若选择前者,则输入单个端口号;若选择后者,则输入端口范围。再输入扫描次数(默认值为1),单击“扫描”按钮,扫描结果列表框中将会显示所输入端口的扫描结果。单击“停止”时停在那一刻的状态,并显示已扫描的结果。单击“保存”时,可将结果保存在文本文件中。5.项目分析与总结5.1项目分析扫描单个端口的结果:扫描多个端口:通过观察,主机IP为10.5.104.36的25号端口是打开的。5.2总结与建议通过这次实验,加深了对端口的理解,也对socket编程有了进一步的认识。实验中遇到许多函数和定义需要上网查阅资料,而且调试的过程也比较困难,需要向同学请教。所以,今后在编程和查阅资料方面的能力有待于进一步去提高。附录A源程序代码文件说明:(1)端口扫描Dlg.h:#if!defined(AFX_DLG_H__37F5060C_6D8B_42F0_90D9_FBA754BAD52B__INCLUDED_)#defineAFX_DLG_H__37F5060C_6D8B_42F0_90D9_FBA754BAD52B__INCLUDED_#if_MSC_VER>1000#pragmaonce#endif//_MSC_VER>1000#include<fcntl.h>#include<sys/types.h>#include<sys/stat.h>#include<io.h>#include<afxsock.h> //扫描结果typedefstruct{intnAttempts; TCHARIPAddress[16]; TCHARport[5]; BOOLbStatus;//1=open,0=close}DATA;///////////////////////////////////////////////////////////////////////////////CMyDlgdialogclassCMyDlg:publicCDialog{//Constructionpublic: CMyDlg(CWnd*pParent=NULL); //standardconstructor//DialogData //{{AFX_DATA(CMyDlg) enum{IDD=IDD_MY_DIALOG}; CProgressCtrl m_cProgress; CListCtrl m_cResult; CIPAddressCtrl m_cIP; CEdit m_cAttempts; CEdit m_cPortTo; CEdit m_cPortFrom; CEdit m_cSinglePort; CButton m_cBtnStop; CButton m_cBtnScan; //}}AFX_DATA //ClassWizardgeneratedvirtualfunctionoverrides //{{AFX_VIRTUAL(CMyDlg) protected: virtualvoidDoDataExchange(CDataExchange*pDX); //DDX/DDVsupport //}}AFX_VIRTUAL//Implementationprotected: HICONm_hIcon;BOOLTestConnection(CStringIP,UINTnPort);//测试主机某个端口是否打开 voidShowHeaders(void);//显示列表框标题栏 voidAddHeader(LPTSTRhdr);//增加一列标题栏字符 AddItem(intnItem,intnSubItem,LPCTSTRstrItem,intnImageIndex=-1);//向m_cResult输出一个结果 //向输出结构列表控件增加一列 BOOLAddColumn(LPCTSTRstrItem,intnItem,intnSubItem=-1, intnMask=LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM, intnFmt=LVCFMT_LEFT); //变量 UINTm_nMaxAttempts; //试图连接次数的最大值 BOOLm_bSinglePort; //是否只扫描单个端口 UINTm_minPort,m_maxPort; //扫描端口的范围 UINTm_nCounter; //端口的个数 CStringList*m_pColumns; //列表框标题栏 CPtrList*m_pStatusList;//保存扫描结果的链表 //Generatedmessagemapfunctions //{{AFX_MSG(CMyDlg) virtualBOOLOnInitDialog(); afx_msgvoidOnSysCommand(UINTnID,LPARAMlParam); afx_msgvoidOnPaint(); afx_msgHCURSOROnQueryDragIcon(); afx_msgvoidOnButton1Start(); afx_msgvoidOnButton2Stop(); afx_msgvoidOnButton3Save(); afx_msgvoidOnRadio1Single(); afx_msgvoidOnRadio2Range(); //}}AFX_MSG DECLARE_MESSAGE_MAP()};//{{AFX_INSERT_LOCATION}}//MicrosoftVisualC++willinsertadditionaldeclarationsimmediatelybeforethepreviousline.#endif//!defined(AFX_DLG_H__37F5060C_6D8B_42F0_90D9_FBA754BAD52B__INCLUDED_)(2)端口扫描Dlg.cpp:#include"stdafx.h"#include"端口扫描.h"#include"端口扫描Dlg.h"#ifdef_DEBUG#definenewDEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[]=__FILE__;#endif///////////////////////////////////////////////////////////////////////////////CAboutDlgdialogusedforAppAboutclassCAboutDlg:publicCDialog{public: CAboutDlg();//DialogData //{{AFX_DATA(CAboutDlg) enum{IDD=IDD_ABOUTBOX}; //}}AFX_DATA //ClassWizardgeneratedvirtualfunctionoverrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtualvoidDoDataExchange(CDataExchange*pDX);//DDX/DDVsupport //}}AFX_VIRTUAL//Implementationprotected: //{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg():CDialog(CAboutDlg::IDD){ //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT}voidCAboutDlg::DoDataExchange(CDataExchange*pDX){ CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CAboutDlg,CDialog) //{{AFX_MSG_MAP(CAboutDlg) //Nomessagehandlers //}}AFX_MSG_MAPEND_MESSAGE_MAP()///////////////////////////////////////////////////////////////////////////////CMyDlgdialogCMyDlg::CMyDlg(CWnd*pParent/*=NULL*/) :CDialog(CMyDlg::IDD,pParent)//在对话框的构造函数中初始化成员变量{ //{{AFX_DATA_INIT(CMyDlg) //}}AFX_DATA_INIT //NotethatLoadIcondoesnotrequireasubsequentDestroyIconinWin32 m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);m_pColumns=newCStringList;ASSERT(m_pColumns);m_bSinglePort=true;m_nMaxAttempts=1;m_pStatusList=newCPtrList;ASSERT(m_pStatusList);}voidCMyDlg::DoDataExchange(CDataExchange*pDX){CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMyDlg) DDX_Control(pDX,IDC_PROGRESS1,m_cProgress); DDX_Control(pDX,IDC_LIST1,m_cResult); DDX_Control(pDX,IDC_IPADDRESS1,m_cIP); DDX_Control(pDX,IDC_EDIT4_ATTEMPTS,m_cAttempts); DDX_Control(pDX,IDC_EDIT3_SINGLE_PORT_TO,m_cPortTo); DDX_Control(pDX,IDC_EDIT2_SINGLE_PORT_FROM,m_cPortFrom); DDX_Control(pDX,IDC_EDIT1_SINGLE_PORT,m_cSinglePort); DDX_Control(pDX,IDC_BUTTON2_STOP,m_cBtnStop); DDX_Control(pDX,IDC_BUTTON1_START,m_cBtnScan); //}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CMyDlg,CDialog) //{{AFX_MSG_MAP(CMyDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON1_START,OnButton1Start) ON_BN_CLICKED(IDC_BUTTON2_STOP,OnButton2Stop) ON_BN_CLICKED(IDC_BUTTON3_SAVE,OnButton3Save) ON_BN_CLICKED(IDC_RADIO1_SINGLE,OnRadio1Single) ON_BN_CLICKED(IDC_RADIO2_RANGE,OnRadio2Range) //}}AFX_MSG_MAPEND_MESSAGE_MAP()///////////////////////////////////////////////////////////////////////////////CMyDlgmessagehandlersBOOLCMyDlg::OnInitDialog()//设置按钮的初始状态和列表框的风格{ CDialog::OnInitDialog(); //Add"About..."menuitemtosystemmenu. //IDM_ABOUTBOXmustbeinthesystemcommandrange. ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX<0xF000); CMenu*pSysMenu=GetSystemMenu(FALSE); if(pSysMenu!=NULL) { CStringstrAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if(!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX,strAboutMenu); } } //Settheiconforthisdialog.Theframeworkdoesthisautomatically //whentheapplication'smainwindowisnotadialog SetIcon(m_hIcon,TRUE); //Setbigicon SetIcon(m_hIcon,FALSE); //Setsmallicon //TODO:Addextrainitializationhere //给列表框加标题栏AddHeader(_T("IP地址"));//加一列标题栏字符AddHeader(_T("端口号"));AddHeader(_T("端口状态"));AddHeader(_T("尝试连接次数"));AddHeader(_T("标记"));ShowHeaders();//设置扫描单个端口CheckRadioButton(IDC_RADIO1_SINGLE,IDC_RADIO2_RANGE,IDC_RADIO1_SINGLE);//设置控件的默认状态m_cSinglePort.EnableWindow();m_cPortFrom.EnableWindow(false);m_cPortTo.EnableWindow(false);m_cBtnStop.EnableWindow(false);m_cAttempts.SetWindowText(_T("1")); returnTRUE;//returnTRUEunlessyousetthefocustoacontrol}voidCMyDlg::OnSysCommand(UINTnID,LPARAMlParam){if((nID&0xFFF0)==IDM_ABOUTBOX) {CAboutDlgdlgAbout; dlgAbout.DoModal(); } else {CDialog::OnSysCommand(nID,lParam); }}//Ifyouaddaminimizebuttontoyourdialog,youwillneedthecodebelow//todrawtheicon.ForMFCapplicationsusingthedocument/viewmodel,//thisisautomaticallydoneforyoubytheframework.voidCMyDlg::OnPaint(){ if(IsIconic()) { CPaintDCdc(this);//devicecontextforpainting SendMessage(WM_ICONERASEBKGND,(WPARAM)dc.GetSafeHdc(),0); //Centericoninclientrectangle intcxIcon=GetSystemMetrics(SM_CXICON); intcyIcon=GetSystemMetrics(SM_CYICON); CRectrect; GetClientRect(&rect); intx=(rect.Width()-cxIcon+1)/2; inty=(rect.Height()-cyIcon+1)/2; //Drawtheicon dc.DrawIcon(x,y,m_hIcon); } else { CDialog::OnPaint(); }}//Thesystemcallsthistoobtainthecursortodisplaywhiletheuserdrags//theminimizedwindow.HCURSORCMyDlg::OnQueryDragIcon(){return(HCURSOR)m_hIcon;}voidCMyDlg::OnButton1Start()//“扫描”按钮{//TODO:Addyourcontrolnotificationhandlercodehere CStringbtnTxt,IP; UINTnSinglePort; BYTEf1,f2,f3,f4; TCHARtemp[10]="\0";m_cProgress.SetPos(0);m_cResult.DeleteAllItems();POSITIONp=m_pStatusList->GetHeadPosition();while(p){POSITIONtemp=p;DATA*pNode=(DATA*)m_pStatusList->GetNext(p);m_pStatusList->RemoveAt(temp);if(pNode)//pNode!=nulldeletepNode;}if(m_cIP.IsBlank()){MessageBox("请输入主机IP地址");return;}if(m_cIP.GetAddress(f1,f2,f3,f4)<4){MessageBox("请输入有效的IP地址");return;}IP=_itoa(f1,temp,10);IP+=_T('.');IP+=_itoa(f2,temp,10);IP+=_T('.');IP+=_itoa(f3,temp,10);IP+=_T('.');IP+=_itoa(f4,temp,10);m_cBtnStop.EnableWindow();m_cBtnScan.EnableWindow(false);if(m_bSinglePort){CStringport;m_cSinglePort.GetWindowText(port);m_minPort=m_maxPort=nSinglePort=atoi(port);}else{CStringport1,port2;m_cPortFrom.GetWindowText(port1);m_cPortTo.GetWindowText(port2);m_minPort=atoi(port1);m_maxPort=atoi(port2);m_cProgress.SetRange32(0,m_maxPort-m_minPort+1);m_cProgress.SetStep(1);}if(!m_bSinglePort&&m_maxPort<m_minPort){MessageBox(_T("您输入的端口范围有误,请重新输入"));return;}UINTm_nMaxAttempts=GetDlgItemInt(IDC_EDIT4_ATTEMPTS);for(m_nCounter=m_minPort;m_nCounter<=m_maxPort;m_nCounter++){BOOLbIsOpen=false;UINTnAttempt=1;while(nAttempt<=m_nMaxAttempts&&!bIsOpen){TCHARtemp[10]="\0";CStringstr=_T("尝试连接端口:");str+=itoa(m_nCounter,temp,10);str+=_T(",IP地址为=");str+=IP;str+=_T(",尝试次数为=");str+=itoa(nAttempt,temp,10);CStatic*pStatic=(CStatic*)GetDlgItem(IDC_STATIC_STATUS);pStatic->SetWindowText(str);str.Empty();bIsOpen=TestConnection(IP,m_nCounter);if(bIsOpen){DATA*pNode=newDATA;ASSERT(pNode);strcpy(pNode->IPAddress,IP.GetBuffer(IP.GetLength()));strcpy(pNode->port,_itoa(m_nCounter,temp,10));pNode->bStatus=1;//openpNode->nAttempts=nAttempt;m_pStatusList->AddTail(pNode);}nAttempt++;}if(!bIsOpen){DATA*pNode=newDATA;ASSERT(pNode);strcpy(pNode->IPAddress,IP.GetBuffer(IP.GetLength()));strcpy(pNode->port,_itoa(m_nCounter,temp,10));pNode->bStatus=0;//closepNode->nAttempts=nAttempt-1;m_pStatusList->AddTail(pNode);}MSGmessage;if(::PeekMessage(&message,NULL,0,0,PM_REMOVE)){::TranslateMessage(&message);::DispatchMessage(&message);}m_cProgress.StepIt();}CStatic*pStatic=(CStatic*)GetDlgItem(IDC_STATIC_STATUS);pStatic->SetWindowText((CString)_T("当前扫描状态"));m_cBtnScan.EnableWindow();m_cBtnStop.EnableWindow(false);UINTnIndex=0;POSITIONpos=m_pStatusList->GetHeadPosition();while(pos){DATA*pNode=(DATA*)m_pStatusList->GetNext(pos);AddItem(nIndex,0,pNode->IPAddress);AddItem(nIndex,1,pNode->port);if(pNode->bStatus){AddItem(nIndex,2,_T("Open"));AddItem(nIndex,4,_T("*"));}else{AddItem(nIndex,2,_T("Close"));AddItem(nIndex,4,_T(""));}AddItem(nIndex++,3,_itoa(pNode->nAttempts,temp,10));}}voidCMyDlg::OnButton2Stop()//“停止”按钮{//TODO:Addyourcontrolnotificationhandlercodehere m_nCounter=m_maxPort+1; m_cBtnStop.EnableWindow(false); m_cBtnScan.EnableWindow(); CStatic*pStatic=(CStatic*)GetDlgItem(IDC_STATIC_STATUS); pStatic->SetWindowText((CString)_T("当前扫描状态"));}voidCMyDlg::OnButton3Save()//“保存”按钮{//TODO:Addyourcontrolnotificationhandlercodehere CFileDialog*pDlg=newCFileDialog(FALSE, _T("txt"),NULL, OFN_OVERWRITEPROMPT| OFN_EXPLORER|OFN_LONGNAMES, _T("Scannedportsfiles(*.txt)"),this); ASSERT(pDlg); if(pDlg->DoModal()==IDOK) { intnHandle,retVal; nHandle=_open(pDlg->GetPathName(),_O_BINARY|_O_CREAT|_O_TRUNC|_O_RDWR); if(nHandle==-1) { MessageBox(_T("Unabletoopenoutputfiletowrite."), _T("Error"), MB_OK|MB_ICONEXCLAMATION); deletepDlg; return; } POSITIONpos=m_pStatusList->GetHeadPosition(); while(pos) { CStringbuffer; DATA*pNode=(DATA*)m_pStatusList->GetNext(pos); buffer=pNode->IPAddress; buffer+=_T(','); buffer+=pNode->port; buffer+=_T(','); if(pNode->bStatus) buffer+=_T("Open"); else buffer+=_T("Close"); buffer+=_T("\r\n\0"); retVal=_write(nHandle, (void*)buffer.GetBuffer(buffer.GetLength()), buffer.GetLength()); if(retVal!=buffer.GetLength()) { MessageBox(_T("Anerroroccuredwhilewritingrecords."), _T("Error"),MB_OK|MB_ICONEXCLAMATION); deletepDlg; return; } buffer.Empty(); } _close(nHandle); } deletepDlg;}voidCMyDlg::OnRadio1Single(){//TODO:Addyour
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 深圳2024年度设备租赁合同规定与说明
- 统编人教版六年级语文上册口语交际《意见不同怎么办》精美课件
- 房屋租赁合同终止协议书
- 钛矿行业市场调研合同04
- 夏季清理杂草合同书
- 知识产权战略合作协议书范本3篇
- 我和毛巾是朋友课件
- 聘用员工合同协议书
- 心理健康教育教学课件
- 艺术品购买权转让合同(2024版)
- 厂家方案比较
- 机械职业生涯规划书
- 国家中小学智慧教育平台培训专题讲座
- 生物医药技术与创新培训资料
- 《房地产销售》课件
- 矿山生态修复施工组织设计
- 2024年新疆能源集团有限责任公司招聘笔试参考题库含答案解析
- PDCA降低护士针刺伤发生率
- 我们如何做课题研究课件
- 供应链管理系统升级
- c4 水稻的研究现状及机制
评论
0/150
提交评论