第10章_网络程序设计实例

上传人:汽*** 文档编号:590948224 上传时间:2024-09-16 格式:PPT 页数:200 大小:1.28MB
返回 下载 相关 举报
第10章_网络程序设计实例_第1页
第1页 / 共200页
第10章_网络程序设计实例_第2页
第2页 / 共200页
第10章_网络程序设计实例_第3页
第3页 / 共200页
第10章_网络程序设计实例_第4页
第4页 / 共200页
第10章_网络程序设计实例_第5页
第5页 / 共200页
点击查看更多>>
资源描述

《第10章_网络程序设计实例》由会员分享,可在线阅读,更多相关《第10章_网络程序设计实例(200页珍藏版)》请在金锄头文库上搜索。

1、第第10章章 网络程序设计实例网络程序设计实例 第第10章章 网络程序设计实例网络程序设计实例 10.1 使用使用Winsock API设计网络程序的实例设计网络程序的实例10.2 使用使用MFC类库进行网络程序设计的实例类库进行网络程序设计的实例 10.3 基于基于WinInet API的客户程序编写实例的客户程序编写实例10.4 原始套接口原始套接口(SOCK_RAW)程序设计实例程序设计实例10.5 广播通信与组播通信程序设计实例广播通信与组播通信程序设计实例习题习题 第第10章章 网络程序设计实例网络程序设计实例 10.1 使用Winsock API设计网络程序的实例10.1.1程序源

2、代码1UDP程序1的源代码/编程环境:VC6.0/程序说明:基于用户数据报协议UDP的程序实例/程序功能:在窗口中点击鼠标左键,则发送信息到端口号为6000的远程机上,并可以从端/口号6000的远程机接收数据第第10章章 网络程序设计实例网络程序设计实例 #includestdafx.h#include/说明全局量HINSTANCEhInst;LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);/intAPIENTRYWinMain(HINSTANCEhI,HINSTANCEhP,LPSTRlp,intnC)第第10章章 网络程序设计实例网络程序设计

3、实例 MSGmsg;HWNDhWnd;/实例句柄存入全局量hInsthInst=hI;WNDCLASSwc;memset(&wc,0,sizeof(WNDCLASS);wc.lpfnWndProc=(WNDPROC)WndProc;wc.hInstance=hI;wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);第第10章章 网络程序设计实例网络程序设计实例 wc.hbrBackground=(HBRUSH)COLOR_WINDOW;wc.lpszClassName=W1;RegisterClass(&wc);hWnd=CreateWindowEx(WS_EX_P

4、ALETTEWINDOW,W1,UDP程序1端口:8000,WS_OVERLAPPEDWINDOW,100,100,200,100,NULL,NULL,hI,NULL);第第10章章 网络程序设计实例网络程序设计实例 if(!hWnd)returnFALSE; ShowWindow(hWnd,nC);/主消息循环while(GetMessage(&msg,NULL,0,0)TranslateMessage(&msg);DispatchMessage(&msg);第第10章章 网络程序设计实例网络程序设计实例 returnmsg.wParam;/定义WinsockAPI需要的量WSADATAws

5、;SOCKETs1,s2;structsockaddr_inCs1A,Cs2A;/发送数据缓冲charsendBuf200;/接收数据缓冲第第10章章 网络程序设计实例网络程序设计实例 charrecvBuf200;intlen;/存放接收到的字节数#defineCs1Port8000/本地端口号#defineCs2Port6000/远程端口号#defineCs1IP127.0.0.1/本地IP地址#defineCs2IP127.0.0.1/远程IP地址/消息处理LRESULTCALLBACKWndProc(HWNDhW,UINTmsg,WPARAMwP,LPARAMlP)第第10章章 网络程

6、序设计实例网络程序设计实例 switch(msg)caseWM_DESTROY:/关闭程序触发事件/向Windows注销Socket触发事件WSAAsyncSelect(s1,hW,0,0);/释放连接closesocket(s1);/卸载动态链接库WinsockDLLWSACleanup();PostQuitMessage(0);第第10章章 网络程序设计实例网络程序设计实例 break;caseWM_CREATE: /初始化触发事件/装载动态链接库WinsockDLLWSAStartup(0x0202,&ws);/创建UDP套接字s1=socket(AF_INET,SOCK_DGRAM,0

7、);/设置地址类型Cs1A.sin_family=AF_INET;/本地端口Cs1A.sin_port=htons(Cs1Port);第第10章章 网络程序设计实例网络程序设计实例 /本地IP地址Cs1A.sin_addr.s_addr=inet_addr(Cs1IP);/进行本地绑定len=bind(s1,(structsockaddr*)&Cs1A,sizeof(Cs1A);/远程地址类型Cs2A.sin_family=AF_INET;/远程端口Cs2A.sin_port=htons(Cs2Port);/远程IP地址第第10章章 网络程序设计实例网络程序设计实例 Cs2A.sin_addr

8、.s_addr=inet_addr(Cs2IP);/向Windows注册网络事件WSAAsyncSelect(s1,hW,WM_USER+1,FD_READ);Beep(2000,200);/发出报警声音break;caseWM_USER+1:/Socket触发事件switch(LOWORD(lP)第第10章章 网络程序设计实例网络程序设计实例 caseFD_READ:/接收远程信息len=recv(s1,recvBuf,sizeof(recvBuf),0);recvBuflen=0;MessageBox(0,recvBuf,UDP程序1从UDP程序2收到的数据,MB_OK);break;br

9、eak;第第10章章 网络程序设计实例网络程序设计实例 caseWM_LBUTTONDOWN:/点击鼠标左键/将发送数据放入缓冲区wsprintf(sendBuf,Hello,UDP程序2!);len=sizeof(Cs2A);/发送信息sendto(s1,sendBuf,strlen(sendBuf),0,(struct sockaddr *)&Cs2A,len);break;returnDefWindowProc(hW,msg,wP,lP);第第10章章 网络程序设计实例网络程序设计实例 2UDP程序2的源代码/编程环境:VC6.0/程序说明:基于用户数据报协议UDP的程序实例/程序功能:

10、在窗口里点击鼠标左键,则发送信息到端口为8000的远程机上,并可以从端口/为8000的远程机接收数据#includestdafx.h#include第第10章章 网络程序设计实例网络程序设计实例 /说明全局量HINSTANCEhInst;LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);/-intAPIENTRYWinMain(HINSTANCEhI,HINSTANCEhP,LPSTRlp,intnC)第第10章章 网络程序设计实例网络程序设计实例 MSGmsg;HWNDhWnd;/实例句柄存入全局量hInsthInst=hI;WNDCLASSwc

11、;memset(&wc,0,sizeof(WNDCLASS);wc.lpfnWndProc =(WNDPROC)WndProc;wc.hInstance=hI;第第10章章 网络程序设计实例网络程序设计实例 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);wc.hbrBackground=(HBRUSH)COLOR_WINDOW;wc.lpszClassName=W1;RegisterClass(&wc);hWnd=CreateWindowEx(WS_EX_PALETTEWINDOW,W1,UDP程序2端口:6000,WS_OVERLAPPEDWINDOW,40

12、0,200,200,100,NULL,NULL,hI,NULL);第第10章章 网络程序设计实例网络程序设计实例 if(!hWnd)returnFALSE; ShowWindow(hWnd,nC);/主消息循环while(GetMessage(&msg,NULL,0,0)TranslateMessage(&msg);DispatchMessage(&msg);returnmsg.wParam;第第10章章 网络程序设计实例网络程序设计实例 /定义WinsockAPI需要的量WSADATAws;SOCKETs1,s2;structsockaddr_inCs1A,Cs2A;/发送数据缓冲chars

13、endBuf200;/接收数据缓冲charrecvBuf200;intlen;/存放接收到的字节数第第10章章 网络程序设计实例网络程序设计实例 #defineCs1Port6000/本地端口#defineCs2Port8000/远程端口#defineCs1IP127.0.0.1/本地网址#defineCs2IP127.0.0.1/远程网址/-/消息处理LRESULTCALLBACKWndProc(HWNDhW,UINTmsg,WPARAMwP,LPARAMlP)第第10章章 网络程序设计实例网络程序设计实例 switch(msg)caseWM_DESTROY:/关闭程序触发事件/向Windo

14、ws注销Socket触发事件WSAAsyncSelect(s1,hW,0,0);/释放连接closesocket(s1);/卸载动态链接库WinsockDLL第第10章章 网络程序设计实例网络程序设计实例 WSACleanup();PostQuitMessage(0);break;caseWM_CREATE: /程序初始化触发事件/装载动态链接库WinsockDLLWSAStartup(0x0202,&ws);/创建UDP套接字s1=socket(AF_INET,SOCK_DGRAM,0);第第10章章 网络程序设计实例网络程序设计实例 /设置地址类型Cs1A.sin_family=AF_IN

15、ET;/本地端口Cs1A.sin_port=htons(Cs1Port);/本地IP地址Cs1A.sin_addr.s_addr=inet_addr(Cs1IP);/进行本地绑定len=bind(s1,(structsockaddr*)&Cs1A,sizeof(Cs1A);/远程地址类型Cs2A.sin_family=AF_INET;第第10章章 网络程序设计实例网络程序设计实例 /远程端口Cs2A.sin_port=htons(Cs2Port);/远程IP地址Cs2A.sin_addr.s_addr=inet_addr(Cs2IP);/向Windows注册网络事件WSAAsyncSelect

16、(s1,hW,WM_USER+1,FD_READ);break;caseWM_USER+1:/Socket触发事件第第10章章 网络程序设计实例网络程序设计实例 switch(LOWORD(lP)caseFD_READ:/接收远程信息len=recv(s1,recvBuf,sizeof(recvBuf),0);recvBuflen=0;MessageBox(0,recvBuf,UDP程序2从UDP程序1收到的数据,0);break;break;第第10章章 网络程序设计实例网络程序设计实例 caseWM_LBUTTONDOWN:/点击鼠标左键wsprintf(sendBuf,Hello,UDP

17、程序1!);len=sizeof(Cs2A);/发送信息sendto(s1,sendBuf,strlen(sendBuf),0,(struct sockaddr*)&Cs2A,len);break;returnDefWindowProc(hW,msg,wP,lP);第第10章章 网络程序设计实例网络程序设计实例 10.1.2程序运行结果在这两个程序调试、编译和链接正确后,就可以执行这两个程序了。这两个程序执行后的窗口如图10-1所示,其中,图(a)是UDP程序1执行后的窗口,图(b)是UDP程序2执行后的窗口。第第10章章 网络程序设计实例网络程序设计实例 (a)UDP程序1的窗口(b)UDP

18、程序2的窗口图10-1UDP程序执行后的窗口第第10章章 网络程序设计实例网络程序设计实例 程序执行后就可以检查它们所实现的功能了。在UDP程序1的窗口中点击鼠标左键,则可以发送数据给UDP程序2,UDP程序2在收到UDP程序1发送的数据后,弹出如图10-2(a)所示的窗口,并显示收到的数据。同样,如果在UDP程序2的窗口中点击鼠标左键,则可以发送数据给UDP程序1,UDP程序1在收到UDP程序2发送的数据后,弹出如图10-2(b)所示的窗口。第第10章章 网络程序设计实例网络程序设计实例 (a)UDP程序2收到的信息(b)UDP程序1收到的信息图10-2UDP程序收到数据后弹出的窗口第第10

19、章章 网络程序设计实例网络程序设计实例 在学习这两个程序的过程中,我们应该注意两个问题:(1) 在程序中,虽然使用的是数据报传输方式(SOCK_DGRAM),但却使用bind()函数进行了地址的绑定。(2)这两个程序的功能是一样的,所以代码几乎是相同的。第第10章章 网络程序设计实例网络程序设计实例 10.2 使用使用MFC类库进行网络程序类库进行网络程序设计的实例设计的实例 10.2.1创建客户端程序我们使用VC+提供的可视化集成编程环境来创建客户端程序。创建客户端程序的主要步骤介绍如下。1创建一个工程使 用 VC+的 应 用 程 序 生 成 向 导 (MFCAppWizardexe)创建一

20、个基于对话框的工程,该工程的名称为CSockClient。2设计程序对话框如图10-3所示。第第10章章 网络程序设计实例网络程序设计实例 图10-3添加类变量第第10章章 网络程序设计实例网络程序设计实例 3生成CAsyncSocket类的子类MySock4在MySock.ccp文件中添加文件包含信息在生成的MySock.cpp中添加如下文件包含信息:#include CSockClient.h#include CSockClientDlg.h“5向MySock.h文件中添加代码向MySock.h文件中添加如下代码:public: BOOL m_bConnected; /是否连接 UINT

21、m_nLength; /消息长度 char m_szBuffer4096; /消息缓冲区第第10章章 网络程序设计实例网络程序设计实例 6重载MySock.ccp文件中的各函数 (1)MySock()函数重载的代码如下:MySock:MySock()m_nLength=0;memset(m_szBuffer,0,sizeof(m_szBuffer);m_bConnected=FALSEMySock:MySock()/关闭套接字if(m_hSocket!=INVALID_SOCKET) Close();第第10章章 网络程序设计实例网络程序设计实例 (2)OnReceive()函数重载的代码如下

22、:voidMySock:OnReceive(intnErrorCode)m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);/下面两行代码用来获取对话框指针CCSockClientApp*pApp=(CCSockClientApp*)AfxGetApp();CCSockClientDlg*pDlg=(CCSockClientDlg*)pApp-m_pMainWnd;pDlg-m_MSGS.InsertString(0,m_szBuffer);memset(m_szBuffer,0,sizeof(m_szBuffer);CAsyncSocket:

23、OnReceive(nErrorCode);第第10章章 网络程序设计实例网络程序设计实例 (3)OnSend()函数重载的代码如下:voidMySock:OnSend(intnErrorCode)Send(m_szBuffer,m_nLength,0);m_nLength=0;memset(m_szBuffer,0,sizeof(m_szBuffer);/继续提请一个读的网络事件,接收Server消息AsyncSelect(FD_READ);CAsyncSocket:OnSend(nErrorCode);第第10章章 网络程序设计实例网络程序设计实例 (4)OnConnect()函数重载的代

24、码如下:voidMySock:OnConnect(intnErrorCode)if(nErrorCode=0)m_bConnected=TRUE;CCSockClientApp*pApp=(CCSockClientApp*)AfxGetApp();CCSockClientDlg*pDlg=(CCSockClientDlg*)pApp-m_pMainWnd;第第10章章 网络程序设计实例网络程序设计实例 memcpy(m_szBuffer,Connectedto,13);strncat(m_szBuffer,pDlg-m_szServerAdr,sizeof(pDlg-m_szServerAdr

25、);pDlg-m_MSGS.InsertString(0,m_szBuffer);/提请一个读的网络事件,准备接收AsyncSelect(FD_READ);CAsyncSocket:OnConnect(nErrorCode);第第10章章 网络程序设计实例网络程序设计实例 7新建一个输入地址的对话框新建一个输入地址信息的对话框IDD_Addr用来输入IP地址和端口号;生成一个新的基于对话框的类CAddrDlg。在该对话框中增加两个编辑(Edit)控件:一个为IDC_Addr,用来输入要连接的IP地址;另一个为IDC_Port,用来输入端口号。使用类向导(ClassWizard)为CAddrDl

26、g类添加m_Addr和m_Port两个类变量。它们的ID标识、类型和名称如下:ControlIDTypeMemberIDC_AddrCStringm_AddrIDC_PortIntm_Port第第10章章 网络程序设计实例网络程序设计实例 然后向CSockClientDlg类中添加如下代码:protected:intTryCount;MySockm_clientSocket;UINTm_szPort;public:charm_szServerAdr256;第第10章章 网络程序设计实例网络程序设计实例 10添加“连接”按钮的程序代码双击IDD_CSOCKCLIENT_DIALOG对话框中的“连

27、接”按钮,添加以下代码:voidCCSockClientDlg:OnConnect()m_clientSocket.ShutDown(2);m_clientSocket.m_hSocket=INVALID_SOCKET;m_clientSocket.m_bConnected=FALSE;CAddrDlgm_Dlg;/默认端口7m_Dlg.m_Port=7;if(m_Dlg.DoModal()=IDOK&!m_Dlg.m_Addr.IsEmpty()第第10章章 网络程序设计实例网络程序设计实例 memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServe

28、rAdr);m_szPort=m_Dlg.m_Port; /建立计时器,每1秒尝试连接一次,直到连上或TryCount10SetTimer(1,1000,NULL);TryCount=0;第第10章章 网络程序设计实例网络程序设计实例 11 添 加 Windows消 息 WM_TIMER的 响 应 函 数OnTimer voidCCSockClientDlg:OnTimer(UINTnIDEvent)if(m_clientSocket.m_hSocket=INVALID_SOCKET)BOOLbFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNEC

29、T);if(!bFlag)第第10章章 网络程序设计实例网络程序设计实例 AfxMessageBox(SocketError!);m_clientSocket.Close();PostQuitMessage(0);return;m_clientSocket.Connect(m_szServerAdr,m_szPort);TryCount+;if(TryCount=10|m_clientSocket.m_bConnected)第第10章章 网络程序设计实例网络程序设计实例 KillTimer(1);if(TryCount=10)AfxMessageBox(ConnectFailed!);retu

30、rn;CDialog:OnTimer(nIDEvent);第第10章章 网络程序设计实例网络程序设计实例 12添加“发送”按钮的程序代码双击IDD_CSOCKCLIENT_DIALOG对话框中的“发送”按钮,添加以下代码:voidCCSockClientDlg:OnSend()if(m_clientSocket.m_bConnected)m_clientSocket.m_nLength=m_MSG.GetWindowText(m_clientSocket.m_szBuffer,sizeof(m_clientSocket.m_szBuffer);m_clientSocket.AsyncSelec

31、t(FD_WRITE);m_MSG.SetWindowText();第第10章章 网络程序设计实例网络程序设计实例 13添加“关闭”按钮的程序代码双击IDD_CSOCKCLIENT_DIALOG对话框中的“关闭”按钮,添加以下代码:voidCCSockClientDlg:OnExit()/关闭Socketm_clientSocket.ShutDown(2);/关闭对话框EndDialog(0);第第10章章 网络程序设计实例网络程序设计实例 上述步骤完成后,调试程序并建立可执行的.EXE文件。当该项目运行时,弹出如图10-4所示的程序对话框。点击“Connect”按钮,弹出如图10-5所示的对

32、话框,输入要连接的服务器的IP地址和端口号,然后单击“OK”按钮。当连接建立后,就可以在客户程序对话框中输入回送信息,并按“Send”按钮进行程序测试了。第第10章章 网络程序设计实例网络程序设计实例 图10-4客户程序对话框第第10章章 网络程序设计实例网络程序设计实例 图10-5地址信息输入对话框第第10章章 网络程序设计实例网络程序设计实例 10.2.2创建服务器端程序1建立一个CAsyncSocket的子类在本例中,由于我们要实现的是一个回声程序,所以服务器程序应将收到的信息原封不动地发送给客户程序。因此,服务器程序的代码主要是重载CAsyncSocket类的接收和发送函数。建立一个C

33、AsyncSocket类的子类CNewSocket,并重载CAsyncSocket类的OnReceive()和OnSend()函数。程序代码如下:第第10章章 网络程序设计实例网络程序设计实例 CnewSocket:OnReceive(intnErrorCode)m_nLength=receive(m_szBuffer,sizeof(m_szBuffer),0);/直接转发消息AsyncSelect(FD_WRITE);CNewSocket:OnSend(intnErrorCode)send(m_szBuffer,m_nLength,0);第第10章章 网络程序设计实例网络程序设计实例 2创建

34、一个用于建立连接的类CMyServerSocket创建一个用于建立连接的类CMyServerSocket,并重载CAsyncSocket类的OnAccept()函数。在MyServerSocket.h中声明如下变量:public:CNewSocket*m_pSocket;OnAccept()程序的代码如下:voidCMyServerSocket:OnAccept(intnErrorCode)第第10章章 网络程序设计实例网络程序设计实例 /侦听到连接请求,调用Accept函数CNewSocket*pSocket=newCNewSocket();if(Accept(*pSocket)pSocke

35、t-AsyncSelect(FD_READ);m_pSocket=pSocket;elsedeletepSocket;第第10章章 网络程序设计实例网络程序设计实例 3为对话框添加一个“侦听”按钮为对话框添加一个“侦听”按钮,并添加如下代码。在CSockServerDlg.ccp中声明变量:public:CMyServerSocketm_srvrSocket;OnListen()函数的代码如下:voidCCSockServerDlg:OnListen()if(m_srvrSocket.m_hSocket=INVALID_SOCKET)BOOLbFlag=m_srvrSocket.Create(

36、UserPort,SOCK_STREAM,FD_ACCEPT);if(!bFlag)第第10章章 网络程序设计实例网络程序设计实例 AfxMessageBox(SocketError!);M_srvrSocket.Close();PostQuitMessage(0);Return;/侦听成功,等待连接请求if(!m_srvrSocket.Listen(1)第第10章章 网络程序设计实例网络程序设计实例 intnErrorCode=m_srvrSocket.GetLastError();if(nError!=WSAEWOULDBLOCK) AfxMessageBox(SocketError!);

37、M_srvrSocket.Close();PostQuitMessage(0);Return;第第10章章 网络程序设计实例网络程序设计实例 10.3 基于WinInet API的客户程序编写实例10.3.1应用程序说明编程环境:VC+6.0。程序功能:给定Internet中一台服务器的域名,客户机首先与该域名的服务器建立连接,然后查询该服务器提供的功能。当然用户也可以随便输入一个域名,看是否在Internet中有该域名的服务器。第第10章章 网络程序设计实例网络程序设计实例 10.3.2建立应用程序的用户操作界面编写该程序要求用户对VC的编程环境比较熟悉。在该程序中我们建立一个基于对话框的用

38、户界面。1使用AppWizard生成对话框程序框架(1)在VC启动后,从“文件”菜单中选择“新建”,在“新建”对话框中选择使用MFC的应用程序生成向导建立一个应用程序,即选择“MFCAppWizard(exe)”,然后在“工程”框内输入要新建工程的名称,在该例中我们取名为scan,再单击“确定”按钮,如图10-6所示。第第10章章 网络程序设计实例网络程序设计实例 图10-6创建一个WinInet应用程序第第10章章 网络程序设计实例网络程序设计实例 (2)在弹出的“MFCAppWizard-step1”对话框中,选择基于对话框的(DialogBased)应用程序类型,然后单击“下一步”按钮。

39、(3)在弹出的“MFCAppWizard-step2of4”对话框中,取消ActiveX Controls控件的选中标记,并且注意不要选择“Windows Sockets”,因为在该程序中我们不直接调用WindowsSockets函数;在对话框标题栏中输入“服务器功能扫描程序”,再单击“下一步”按钮。(4)在弹出的“MFCAppWizard-step3of4”对话框中,选择MFC所使用的链接库,一般选择静态链接库,再单击“下一步”按钮。第第10章章 网络程序设计实例网络程序设计实例 (5)在“MFCAppWizard-step4of4”对话框中,一般不需要改变MFCAppWiard自动生成的各

40、种类名,直接按“完成”按钮即可,这时弹出一个新建的有关工程和信息的对话框。2编程对话框(1) 添加一个显示输出结果的编辑框。使用VC的资源工具给对话框添加一个编辑框,然后右击编辑框,在弹出的下拉菜单中选择“Properties”,在弹出的编辑控件属性对话框中,将资源的ID标识符设置为IDC_EDIT1,再给该编辑框增加一些其他类型属性。如 在 “Styles”选 项 卡 中 , 选 中 图 10-7所 示 的“Mutiline”等属性。 第第10章章 网络程序设计实例网络程序设计实例 图10-7属性对话框第第10章章 网络程序设计实例网络程序设计实例 (2)添加一个输入网址的编辑框。用与上面类

41、似的方法再增加另一个编辑框,编辑框的ID标志设为IDC_EDIT2,然后在属性对话框中选择“Mutiline”和“Wantreturn”属性。(3)添加查询命令按钮。在对话框上的恰当位置添加5个按钮,这5个按钮分别用来实现HTTP、FTP、Gopher、FINGER和WHOIS查询功能,它们的ID标志分别设置为IDC_BUTTON_HTTP、IDC_BUTTON_FTP、IDC_BUTTON_GOPHER、IDC_BUTTON_FINGER和IDC_BUTTON_WHOIS。在上面的操作完成以后,适当调整这些资源的布局,使用户界面比较美观。最后的结果如图10-8所示。第第10章章 网络程序设计

42、实例网络程序设计实例 图10-8应用程序对话框第第10章章 网络程序设计实例网络程序设计实例 10.3.3应用程序代码及其说明1给编辑框添加变量为了完成程序的显示和输入功能,需要给编辑框添加对应的变量。为了简单起见,我们可以使用“ClassWizard”给 编 辑 框 添 加 变 量 。 编 辑 框IDC_EDIT1附加的变量为m_out,编辑框IDC_EDIT2附加的变量为m_host,它们都应为CString类型的变量。第第10章章 网络程序设计实例网络程序设计实例 2HTTP协议查询代码给对话框类CScanDlg添加一个函数,用于实现HTTP查询。函数原形为:voidTryURL(CSt

43、ringURL);其实现代码为:voidCScanDlg:TryURL(CStringURL)CInternetSessionsession;m_out+=正在链接+URL+rn;第第10章章 网络程序设计实例网络程序设计实例 UpdateData(FALSE);CInternetFile*file=NULL;tryfile=(CInternetFile*)session.OpenURL(URL);catch(CInternetException*pEx)file=NULL;pEx-Delete();第第10章章 网络程序设计实例网络程序设计实例 if(file)m_out+=已建立链接。rn

44、;CStringline;for(inti=0;iReadString(line);i+)m_out+=line+rn;file-Close();deletefile;第第10章章 网络程序设计实例网络程序设计实例 elsem_out+=本地址没有发现http主机rn;m_out+=-rn;UpdateData(FALSE);第第10章章 网络程序设计实例网络程序设计实例 下面,我们分析一下这个函数是如何实现HTTP链接的。首先,根据我们在第9章所学的知识,需要建立一个Internet会话,这就要定义一个CInternetSession的对象。其构造函数的原形如下:CInternetSessi

45、on(LPCTSTRpstrAgent=NULL,DWORDdwContext=1,DWORDdwAccessType=INTERNET_OPEN_TYPE_PRECONFIG,LPCTSTRpstrProxyName=NULL,LPCTSTRpstrProxyBypass=NULL,DWORDdwFlags=0);第第10章章 网络程序设计实例网络程序设计实例 这个函数需要定义很多参数,但本程序都使用缺省值,即“=”号后的值。CInternetSession构造函数参数说明如下:LPCTSTRpstrAgent:应用程序名,如果为NULL,将填入在AppWizard中给定的程序名。DWORD

46、dwContext:本操作的设备关联符定义。DWORDdwAccessType:访问类型,可以为以下参数之一:INTERNET_OPEN_TYPE_PRECONFIG(default),INTERNET_OPEN_TYPE_DIRECT或INTERNET_OPEN_TYPE_PROXY。第第10章章 网络程序设计实例网络程序设计实例 LPCTSTR pstrProxyName: 如 访 问 类 型 为INTERNET_OPEN_TYPE_PROXY,则给该参数赋予协议名称。LPCTSTR pstrProxyBypass: 如 访 问 类 型 为INTERNET_OPEN_TYPE_PROXY,

47、则该参数为不通过协议服务器而直接链接的一系列地址。DWORDdwFlags: 可 为 以 下 参 数 :INTERNET_FLAG_DONT_CACHE,INTERNET_FLAG_ASYNC和INTERNET_FLAG_OFFLINE。第第10章章 网络程序设计实例网络程序设计实例 DWORDdwAccessType:值缺省时将使用系统注册簿定义的值。显然,程序允许使用者定义访问类型将比由程序内部直接定义要好。因此,要正确使用本程序,必须先在Windows系统中定义好网络访问类型,步骤如下:(1)双击桌面上“我的电脑”图标。(2)双击“控制面板”。(3)点击“Internet”。(4)在随后

48、弹出的对话框中选择“连接”,然后填写网络连接属性。如果是通过拨号上网的,选中“拨号设置”选择项,并填写相关属性;如果是局域网(LAN)上网的,则要进入局域网(LAN)设置代理服务器的地址和端口号。第第10章章 网络程序设计实例网络程序设计实例 注意:如果所使用的计算机已经接入Internet,则不需要进行如上设置。本程序在构造CInternetSession对象时使用缺省值,因此,构造函数将不带任何参数,如下所示:CInternetSessionsession;在构造对象session后,如下两行程序进行输出提示,表示程序已开始工作:m_out+=正在链接+URL+rn;UpdateData(

49、FALSE);第第10章章 网络程序设计实例网络程序设计实例 接下来使用session对象的成员函数OpenURL()来打开一个URL资源。该函数返回一个文件的指针,文件类型为以下四种之一:file:/如果访问的是本地机器,函数返回一个CStudioFile类对象的指针ftp:/如果访问的是FTP地址,函数返回一个CInternetFile类对象的指针gopher:/如果访问的是Gopher地址,函数返回一个CGopherFile类对象的指针http:/如果访问的是HTTP地址,函数返回一个CHttpFile类对象的指针第第10章章 网络程序设计实例网络程序设计实例 本程序用于访问远程机器,因

50、此,函数不会返回一个file:/类型的本地文件。而CGopherFile和CHttpFile均派生自CInternetFile,所以将该函数返回值赋给CInternetFile类的指针是安全的。当OpenURL()不能正常打开URL资源时,该函数将会抛出一个异常(exception),从而导致程序运行时错误。由于本程序用于探查未知的网址,该网址可能不提供相应服务或根本不存在,因此OpenURL()有时可能不会正常运行。为了避免程序异常终止,我们需使用try-catch结构来处理异常。本段程序代码如下:第第10章章 网络程序设计实例网络程序设计实例 CInternetFile*file=NULL

51、;tryfile=(CInternetFile*)session.OpenURL(URL);catch(CInternetException*pEx)file=NULL;/如果发生运行时错误,给file赋空值,程序将继续运行pEx-Delete();第第10章章 网络程序设计实例网络程序设计实例 通过以上代码,程序将使用用户指定的地址来试图打开HTTP网址,如果失败,返回的文件为空,程序将继续运行,尝试用其他协议打开网址。随后,无论网址是否成功打开,我们都应作相应输出以提示用户。如果成功,用一个for循环读出返回文件的头20句并输出;如果失败,也要给出相应的提示。程序代码如下:if(file)

52、/判定链接是否成功m_out+=已建立链接。rn;CStringline;for(inti=0;iReadString(line);i+)第第10章章 网络程序设计实例网络程序设计实例 m_out+=line+rn;file-Close();deletefile;elsem_out+=本地址没有发现http主机rn;m_out+=-rn;UpdateData(FALSE);第第10章章 网络程序设计实例网络程序设计实例 用于查询的函数编写完后,还要给按钮的单击事件增加代码,以便按钮按下时程序开始查询。给按钮IDC_BUTTON_HTTP增加BN_CLICKED消息的代码如下:voidCScan

53、Dlg:OnScanHttp()constCStringhttp=http:/;UpdateData(TRUE);m_out=;UpdateData(FALSE);TryURL(http+m_host);TryURL(http+www.+m_host);第第10章章 网络程序设计实例网络程序设计实例 这段程序代码中,UpdateData(TRUE)的调用将给m_host赋予用户定义的值。UpdateData(FALSE)语句将清空输出编辑框变量m_out的内容,接下来调用两次TryURL()。例如,当用户输入时,程序将首先试一试http:/,然后试一试http:/。到此为止,就可以编译程序并执

54、行了。在该例中,我们在执行时输入后,再单击“HTPP”按钮。在连接到Internet后,程序输出主页的头20行,如图10-9所示。第第10章章 网络程序设计实例网络程序设计实例 图10-9服务器功能扫描程序对话框第第10章章 网络程序设计实例网络程序设计实例 3FTP协议查询代码下面我们来探讨如何实现FTP链接。首先给对话框类增加一个实现链接的函数TryFTP,代码如下:voidCScanDlg:TryFTP(CStringhost)CInternetSessionsession;m_out+=正在链接FTP地址+host+rn;UpdateData(FALSE);CFtpConnection

55、*connection=NULL;第第10章章 网络程序设计实例网络程序设计实例 tryconnection=session.GetFtpConnection(host);catch(CInternetException*pEx)connection=NULL;pEx-Delete();第第10章章 网络程序设计实例网络程序设计实例 if(connection)m_out+=已建立链接。rn;CStringline;connection-GetCurrentDirectory(line);m_out+=缺省目录为+line+rn;connection-Close();deleteconnect

56、ion;elsem_out+=本地址没有发现ftp主机。rn;m_out += -rn;UpdateData(FALSE);第第10章章 网络程序设计实例网络程序设计实例 本 函 数 和 TryURL()很 相 似 , 不 过 它 不 使 用OpenURL()来打开一个文件,而用GetFtpConnection()与FTP服务器建立链接。如果链接成功,将使用GetCurrentDirectory()来得到服务器的缺省目录。大多数FTP地址前有前缀ftp.,一些早期的地址没有此前缀。现在,给IDC_BUTTON_FTP链接单击消息的处理函数,并编写如下程序代码:第第10章章 网络程序设计实例网络

57、程序设计实例 voidCScanDlg:OnButtonFtp()UpdateData(TRUE);m_out=;UpdateData(FALSE);TryFTP(m_host);TryFTP(ftp.+m_host);第第10章章 网络程序设计实例网络程序设计实例 现在已完成了FTP链接的实现代码。重新编译程序并运行,键入,可以发现在处提供ftp服务。从程序开始查询到输出结果将有一段较长的时间,因为程序要等结果全部探查到后再显示,但实际上现在的商业Internet程序都是实时显示的。如果我们希望结果实时显示,就要使用异步套接口(AsynchronousSockets)或多线程编程。第第10章

58、章 网络程序设计实例网络程序设计实例 4Gopher协议查询代码Gopher是一种基于文本的协议,它和WWW相似,可以通过点击链接点实现网络的链接和浏览。但是,Gopher是通过逐级文字菜单来组织链接和内容的,它不像WWW那样有丰富的多媒体页面。要实现Gopher查询,我们应给CScanDlg类增加另一个成员函数voidTryGopher(CStringhost)。如果查询成功,该函数将返回查询地址的第一个Gopher位置(locator)。位置是Gopher协议的概念,任何Gopher客户程序必须先得到一个Gopher位置,然后才能进行相应的Gopher操作。TryGopher()函数如下:

59、第第10章章 网络程序设计实例网络程序设计实例 voidCScanDlg:TryGopher(CStringhost)CInternetSessionsession;m_out+=正在链接gopher地址+host+rn;UpdateData(FALSE);CGopherConnection*connection=NULL;tryconnection=session.GetGopherConnection(host);第第10章章 网络程序设计实例网络程序设计实例 catch(CInternetException*pEx)connection=NULL;pEx-Delete();if(conn

60、ection)m_out+=已建立链接。rn;CStringline;CGopherLocatorlocator=connection-CreateLocator(NULL,NULL,GOPHER_TYPE_DIRECTORY);第第10章章 网络程序设计实例网络程序设计实例 line=locator;m_out+=第一个Gopher位置是+line+rn;connection-Close();deleteconnection;elsem_out+=本地址没有发现gopher主机。rn;m_out+=-rn;UpdateData(FALSE);第第10章章 网络程序设计实例网络程序设计实例 本

61、函数和前两个函数大致相似,下面对主要语句进行一些说明。(1)建立Gopher链接的语句是:connection=session.GetGopherConnection(host);(2)建立一个Gopher位置(locator)的语句是:CGopherLocatorlocator=connection-CreateLocator(NULL,NULL,GOPHER_TYPE_DIRECTORY);第第10章章 网络程序设计实例网络程序设计实例 注意:函数CreateLocator()有多个版本,这里使用的是含有三个参数的版本。其函数原形如下:CGopherLocatorCreateLocator

62、(LPCTSTRpstrDisplayString,LPCTSTRpstrSelectorString,DWORDdwGopherType);其中,参数pstrDisplayString指明要查询的服务器上的具体文件或目录名,如果它为NULL,则返回服务器缺省目录名;参数pstrSelectorString是发送给服务器的字符命令,以便检索某个项目,它可设为空;参数dwGopherType指明Gopher访问的类型,本例中定义为GOPHER_TYPE_DIRECTORY,指明要访问的是目录,它的其他可取值请参考VC+6.0文档。Gopher位置建立后,我们把它强制转换为CString类型,并把

63、该位置显示出来。第第10章章 网络程序设计实例网络程序设计实例 现在,给IDC_BUTTON_GOPHER链接单击消息的处理函数,并编写如下程序:voidCScanDlg:OnButtonGopher()UpdateData(TRUE);m_out=;UpdateData(FALSE);TryGopher(m_host);TryGopher(gopher.+m_host);重新编译程序,输入地址harvard.edu,程序将会探查出它是一个Gopher地址,并显示出第一个Gopher位置。第第10章章 网络程序设计实例网络程序设计实例 5FINGER协议查询代码FINGER协议的作用是提供一个

64、网址的具体情况,它是Internet上最古老的协议之一。在一个FINGER服务器上,可以查询它的某一个用户或整个网址的情况。当然,这对网络的安全是不利的。为了安全起见,许多网络服务器不提供FINGER服务,但当它接收到FINGER查询请求时,仍然会返回一些有用信息。第第10章章 网络程序设计实例网络程序设计实例 在MFC和Win32API中,没有提供直接实现FINGER查询的函数,但可以使用变通的办法来实现它。所有的Internet链接都需要一个宿主名和端口号,所有的著名的服务都有其特定的端口号。例如,HTTP服务使用远程宿主机上的端口80,FTP服务使用端口21,Gopher服务使用端口70

65、。对于FINGER服务来说,它使用端口79。FINGER是一种简单的协议,如果向远程宿主机的端口79发送字符串,FINGER服务器在端口79侦听到后,将会发送出一个FINGER回答。如果发送的字符串仅仅包含rn,服务器通常会把本服务器上所有用户的列表及相关信息(如用户真实姓名等)作为应答返回。因此,如果我们不使用缺省的端口70,而是使用端口79来建立Gopher链接,就能发出FINGER查询。第第10章章 网络程序设计实例网络程序设计实例 给 CScanDlg类 增 加 一 个 如 下 的 成 员 函 数 voidTryFinger(CStringhost):voidCScanDlg:TryF

66、inger(CStringhost)CInternetSessionsession;m_out+=正在链接finger地址+host+rn;UpdateData(FALSE);CGopherConnection*connection=NULL;tryconnection=session.GetGopherConnection(host,NULL,NULL,79);第第10章章 网络程序设计实例网络程序设计实例 catch(CInternetException*pEx)connection=NULL;pEx-Delete();if(connection)m_out+=已建立链接.rn;CGoph

67、erLocatorlocator=connection-CreateLocator(NULL,NULL,GOPHER_TYPE_TEXT_FILE);第第10章章 网络程序设计实例网络程序设计实例 CGopherFile*file=NULL;tryfile=connection-OpenFile(locator);catch(CInternetException*pEx)file=NULL;pEx-Delete();第第10章章 网络程序设计实例网络程序设计实例 if(file)CStringline;for(inti=0;iReadString(line);i+)m_out+=line+rn

68、;file-Close();deletefile;第第10章章 网络程序设计实例网络程序设计实例 elsem_out+=finger查询失败。rn;connection-Close();deleteconnection;第第10章章 网络程序设计实例网络程序设计实例 elsem_out+=本地址没有发现finger主机。rn;m_out+=-rn;UpdateData(FALSE);第第10章章 网络程序设计实例网络程序设计实例 本函数中,语句connection=session.GetGopherConnection(host,NULL,NULL,79)用于建立FINGER链接。随后,创建一

69、个文本文件类型的Gopher位置,用来操作服务器返回的信息,该语句为CGopherLocatorlocator=connection-CreateLocator(NULL,NULL,GOPHER_TYPE_TEXT_FILE)。使用该Gopher位置打开文件并使用一个for循环来读出该文件的头20行,随后将它显示出来。现在,给IDC_BUTTON_FINGER按钮链接单击消息的处理函数,并编写如下代码:第第10章章 网络程序设计实例网络程序设计实例 voidCScanDlg:OnButtonFinger()UpdateData(TRUE);m_out=;UpdateData(FALSE);Tr

70、yFinger(m_host);编译程序,输入地址whitehouse.gov,程序将会返回该服务器的E-mail地址,从返回的信息可知,出于安全考虑,该服务器的其他FINGER服务已被取消了。如果你输入的网址没有提供FINGER服务,程序将有较长一段时间没有反应,最后弹出一个消息框,通知用户链接出现超时错误。第第10章章 网络程序设计实例网络程序设计实例 6WHOIS协议查询代码还有一个协议也能提供网址的相关信息,它也是一种古老的协议,MFC并不直接支持它,这就是WHOIS协议。在整个Internet上,只有少数服务器提供WHOIS服务。WHOIS服务建立了Internet上的域名数据库。如

71、果对某个域名进行WHOIS查询,服务器将会返回拥有该域的机构或个人的实际姓名、地址、电话号码等信息。国际上的域名注册机构拥有WHOIS服务器,例如,域名结尾为.com的域都在一个称为InterNIC的机构中注册,该机构拥有一个WHOIS服 务 器 , 称 为 。 WHOIS协 议 和FINGER协议一样,是一种简单的协议,它使用端口43。如果向WHOIS服务器的端口43发送包含域名的字符串,则WHOIS服务器将会返回该域拥有者的信息。第第10章章 网络程序设计实例网络程序设计实例 给CScanDlg类增加一个如下的成员函数voidTryWhois(CStringhost):voidCScanD

72、lg:TryWhois(CStringhost)CInternetSessionsession;m_out+=正在链接Whois地址+host+rn;UpdateData(FALSE);CGopherConnection*connection=NULL;tryconnection=session.GetGopherConnection(,NULL,NULL,43);第第10章章 网络程序设计实例网络程序设计实例 catch(CInternetException*pEx)connection=NULL;pEx-Delete();if(connection)m_out+=已建立链接。rn;CGop

73、herLocatorlocator=connection-CreateLocator(NULL,host,GOPHER_TYPE_TEXT_FILE);第第10章章 网络程序设计实例网络程序设计实例 CGopherFile*file=NULL;tryfile=connection-OpenFile(locator);catch(CInternetException*pEx)file=NULL;pEx-Delete();第第10章章 网络程序设计实例网络程序设计实例 if(file)CStringline;for(inti=0;iReadString(line);i+)m_out+=line+r

74、n;file-Close();deletefile;第第10章章 网络程序设计实例网络程序设计实例 elsem_out+=Whois查询失败。rn;connection-Close();deleteconnection;elsem_out+=Whois查询失败。rn;m_out+=-rn;UpdateData(FALSE);第第10章章 网络程序设计实例网络程序设计实例 在本函数中,语句connection=session.GetGopherConnection(,NULL,NULL,43)使程序链接到了WHOIS服务器。随后,建立一个Gopher位置来查询用户输入的域:CGopherLoca

75、torlocator=connection-CreateLocator(NULL,host,GOPHER_TYPE_TEXT_FILE),由于链接的域为,所以本函数只能查询结尾为.com的域。读者可以对本段程序进行扩充,链接到其他相关的WHOIS服务器,以查询其他类型的域。第第10章章 网络程序设计实例网络程序设计实例 现在,给IDC_BUTTON_WHOIS按钮链接单击消息的处理函数,并编写如下代码:voidCScanDlg:OnButtonWhois()UpdateData(TRUE);m_out=;UpdateData(FALSE);TryWhois(m_host);重新编译程序,现在,

76、我们可以对结尾为.com的域进行WHOIS查询了。第第10章章 网络程序设计实例网络程序设计实例 10.4 原始套接口(SOCK_RAW)程序设计实例10.4.1原始套接口简介要在程序中使用原始套接口,首先也要创建一个原始套接口。原始套接口只能使用SOCK_RAW套接口类型来创建。在创建SOCK_RAW套接口时,socket()函 数 的 第 三 个 参 数 , 即 协 议 字 段 通 常 使 用IPPROTO_ICMP或 IPPROTO_IGMP; 而 在 创 建SOCK_STREAM和SOCK_DGRAM类型的套接口时,socket()函 数 的 第 三 个 参 数 通 常 为 0。 创

77、建 一 个SOCK_RAW套接口的典型代码如下:第第10章章 网络程序设计实例网络程序设计实例 SOCKETs;s=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);/也可以使用如下的代码s=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);if(s=INVALID_SOCKET)/输出创建套接口错误的提示信息第第10章章 网络程序设计实例网络程序设计实例 以上代码表示创建SOCK_RAW套接口时指定的协议为ICMP,即在IP协议的数据区中承载的是ICMP协议。在SOCK_RAW套接

78、口创建完成后,就可以进行数据的发送和接收了,只是IP数据报头会包含在收到的数据中。在套接口使用完毕后,同样要关闭套接口。第第10章章 网络程序设计实例网络程序设计实例 10.4.2原始套接口程序设计实例具体Ping程序的实现步骤如下:(1)定义IP和ICMP协议头。(2)定义回送请求和应答数据包的结构。(3) 在 主 程 序 中 初 始 化 Winsock协 议 栈 (使 用WSAStartup()函数)。(4)在主程序中调用Ping函数。(5)释放Winsock协议栈。第第10章章 网络程序设计实例网络程序设计实例 Ping函数的主要功能如下:(1)创建一个原始套接口。(2)根据主机名查询主

79、机地址。(3)输出Ping程序要测试的目标主机地址。(4)控制Ping程序进行4次发送与接收的测试(发送和接收分别使用SendEcho-Request()和WaitForEchoReply()函数来完成)。(5)计算传输时间,并输出提示信息。(6)关闭原始套接口。第第10章章 网络程序设计实例网络程序设计实例 SendEchoRequest()函数的功能是:填写回送请求信息,计算校验和,使用sendto发送回送请求数据。WaitForEchoReply()和其他几个函数的定义比较简单。可以查看程序中的说明。下面是在头文件ping.h中定义的IP和ICMP协议头的结构。/ping.h/在该头文件

80、中定义了IP和ICMP协议头的结构#pragmapack(1)/指定结构成员使用紧凑对齐#defineICMP_ECHOREPLY0#defineICMP_ECHOREQ8/定义符合RFC791的IP协议头第第10章章 网络程序设计实例网络程序设计实例 typedefstructtagIPHDRu_charVIHL;/VersionandIHLu_char TOS;/TypeOfServiceshortTotLen;/TotalLengthshortID;/IDentificationshortFlagOff;/FlagsandFragmentOffsetu_char TTL;/TimeToL

81、iveu_char Protocol;/Protocolu_shortChecksum;/Checksum第第10章章 网络程序设计实例网络程序设计实例 structin_addriaSrc; /InternetAddress-Sourcestruct in_addriaDst; /InternetAddress-DestinationIPHDR,*PIPHDR;/定义符合RFC792的ICMP协议头typedefstructtagICMPHDRu_char Type;/Typeu_char Code;/Code第第10章章 网络程序设计实例网络程序设计实例 u_shortChecksum;/

82、Checksumu_shortID;/IDentificationu_shortSeq;/SequencecharData;/DataICMPHDR,*PICMPHDR;/请求回送的数据长度#defineREQ_DATASIZE32/ICMP回送请求的数据结构第第10章章 网络程序设计实例网络程序设计实例 typedefstructtagECHOREQUESTICMPHDRicmpHdr;/ICMP协议头DWORDdwTime;/数据传输时间charcDataREQ_DATASIZE;/传输的数据ECHOREQUEST,*PECHOREQUEST;/ICMP回送应答typedefstructt

83、agECHOREPLYIPHDRipHdr;ECHOREQUESTechoRequest;charcFiller256;ECHOREPLY,*PECHOREPLY;#pragmapack()第第10章章 网络程序设计实例网络程序设计实例 下面是Ping程序的源代码:/PING.C#include#include#include#includePing.h/函数声明voidPing(LPCSTRpstrHost);voidReportError(LPCSTRpstrFrom);intWaitForEchoReply(SOCKETs);u_shortin_cksum(u_short*addr,in

84、tlen);第第10章章 网络程序设计实例网络程序设计实例 /ICMP回送请求和应答函数声明intSendEchoRequest(SOCKET,LPSOCKADDR_IN);DWORDRecvEchoReply(SOCKET,LPSOCKADDR_IN,u_char*);/主程序voidmain(intargc,char*argv)WSADATAwsaData;WORDwVersionRequested=MAKEWORD(1,1);/Winsock1.1第第10章章 网络程序设计实例网络程序设计实例 intnRet;/命令行参数检查if(argc!=2)fprintf(stderr,nUsag

85、e:pinghostnamen);return;/初始化WinsocknRet=WSAStartup(wVersionRequested,&wsaData);if(nRet)fprintf(stderr,nErrorinitializingWinsockn);return;第第10章章 网络程序设计实例网络程序设计实例 /Winsock版本检查if(wsaData.wVersion!=wVersionRequested)fprintf(stderr,nWinsockversionnotsupportedn);return;/调用Ping函数Ping(argv1);/释放WinsockWSACl

86、eanup();第第10章章 网络程序设计实例网络程序设计实例 /Ping()函数/调用SendEchoRequest()和RecvEchoReply()函数/并输出结果voidPing(LPCSTRpstrHost)SOCKETrawSocket;LPHOSTENTlpHost;structsockaddr_insaDest;structsockaddr_insaSrc;DWORDdwTimeSent;DWORDdwElapsed;第第10章章 网络程序设计实例网络程序设计实例 u_charcTTL;intnLoop;intnRet;/创建一个原始套接口rawSocket=socket(AF

87、_INET,SOCK_RAW,IPPROTO_ICMP);if(rawSocket=SOCKET_ERROR)ReportError(socket();return;第第10章章 网络程序设计实例网络程序设计实例 /根据主机名查询主机地址lpHost=gethostbyname(pstrHost);if(lpHost=NULL)fprintf(stderr,nHostnotfound:%sn,pstrHost);return;第第10章章 网络程序设计实例网络程序设计实例 /设置目标套接口地址saDest.sin_addr.s_addr=*(u_longFAR*)(lpHost-h_addr)

88、;saDest.sin_family=AF_INET;saDest.sin_port=0;/输出Ping程序的提示信息,例如进行本机Ping时,输出:/Pinging localhost 127.0.0.1 with 32 bytes ofdata:printf(nPinging%s%swith%dbytesofdata:n,pstrHost,inet_ntoa(saDest.sin_addr),REQ_DATASIZE);第第10章章 网络程序设计实例网络程序设计实例 /控制Ping执行的次数for(nLoop=0;nLoop4;nLoop+)/发送ICMP回送请求SendEchoReque

89、st(rawSocket,&saDest);/使用select()等待接收回送的数据nRet=WaitForEchoReply(rawSocket);if(nRet=SOCKET_ERROR)ReportError(select();break;第第10章章 网络程序设计实例网络程序设计实例 if(!nRet)printf(nTimeOut);/输出超时提示break;/接收应答dwTimeSent=RecvEchoReply(rawSocket,&saSrc,&cTTL);/计算传输时间,并输出提示信息。如:/Replyfrom:127.0.0.1:bytes=32time=0msTTL=1

90、28dwElapsed=GetTickCount()-dwTimeSent;第第10章章 网络程序设计实例网络程序设计实例 printf(nReply from: %s: bytes=%d time=%ldmsTTL=%d,inet_ntoa(saSrc.sin_addr),REQ_DATASIZE,dwElapsed,cTTL);第第10章章 网络程序设计实例网络程序设计实例 printf(n);nRet=closesocket(rawSocket);if(nRet=SOCKET_ERROR)ReportError(closesocket();/SendEchoRequest()/给目标站点

91、发送回送请求intSendEchoRequest(SOCKETs,LPSOCKADDR_INlpstToAddr)第第10章章 网络程序设计实例网络程序设计实例 staticECHOREQUESTechoReq;staticnId=1;staticnSeq=1;intnRet;/填写回送请求信息echoReq.icmpHdr.Type=ICMP_ECHOREQ;echoReq.icmpHdr.Code=0;echoReq.icmpHdr.Checksum =0;echoReq.icmpHdr.ID =nId+;echoReq.icmpHdr.Seq=nSeq+;第第10章章 网络程序设计实例网

92、络程序设计实例 /填写要发送的数据for(nRet=0;nRet1)sum+=*w+;nleft=2;第第10章章 网络程序设计实例网络程序设计实例 if(nleft=1)u_shortu=0;*(u_char*)(&u)=*(u_char*)w;sum+=u;sum=(sum16)+(sum&0xffff);/*addhi16tolow16*/sum+=(sum16);/*addcarry*/answer=sum;/*truncateto16bits*/return(answer);第第10章章 网络程序设计实例网络程序设计实例 程序编译通过后,就可以执行了。下面是程序运行后的输出结果。在命

93、令行输入如下信息,进行本机测试:Ping127.0.0.1程序的输出结果为:Pinginglocalhost127.0.0.1with32bytesofdata:Replyfrom:127.0.0.1:bytes=32time=6msTTL=128Replyfrom:127.0.0.1:bytes=32time=0msTTL=128Replyfrom:127.0.0.1:bytes=32time=0msTTL=128Replyfrom:127.0.0.1:bytes=32time=0msTTL=128第第10章章 网络程序设计实例网络程序设计实例 10.5 广播通信与组播通信程序设计实例10.

94、5.1广播通信程序设计1广播通信概述2广播通信程序设计步骤(1) 创建套接口。 (2) 进行地址绑定。 第第10章章 网络程序设计实例网络程序设计实例 程序如下:SOCKADDR_INaddr;addr.sin_family=AF_INET;addr.sin_port=htons(nPort);/nPort为端口地址addr.sin_addr.s_adds=INADDR_BROADCAST;if(bind(s,(LPSOCKADDR)&addr,sizeof(adds)=SOCKET_ERROR)AfxMessageBox(sendto()failed:%d,WSAGetLastError()

95、;第第10章章 网络程序设计实例网络程序设计实例 (3) 将 套 接 口 设 置 为 广 播 通 信 方 式 。 使 用setsockopt()函数将上面创建的套接口设置为广播通信方式,程序如下:bBroadcast=TRUE;setsockopt(sendSocket,SOL_SOCKET,SO_BROADCAST,(char*)&bBroadcast,sizeof(bBroadcast);(4)发送数据。使用sendto()函数发送广播数据,目标地址必须为INADDR_BROADCAST。第第10章章 网络程序设计实例网络程序设计实例 (5)关闭套接口。使用closesocket()函数关

96、闭套接口,程序如下:closesocket(s);接收端的UDP程序与点到点通信时的程序类似。下面我们举一个简单的实例,说明广播通信程序的设计方法。3广播通信程序设计举例在VC+中,我们设计一个非常简单的基于对话框的广播通信程序,该程序可以把编辑框中输入的内容广播发送出去。(1)创建工程。(2)编辑对话框。如图10-10所示。第第10章章 网络程序设计实例网络程序设计实例 图10-10广播发送程序对话框第第10章章 网络程序设计实例网络程序设计实例 (3)为编辑框添加一个类变量如下:IDC_EDIT1CStringm_strSend(4)添加程序代码。为“广播发送”按钮添加如下代码:voidC

97、BroadCastDlg:OnOK()UpdateData(TRUE);SOCKETsendSocket;SOCKADDR_INrecv;BOOLbBroadcast;char*chFileData;chFileData=m_strSend.GetBuffer(0);第第10章章 网络程序设计实例网络程序设计实例 m_strSend.ReleaseBuffer();/m_strSend为与编辑框联系的变量WSADATAwsd;if(WSAStartup(MAKEWORD(2,2),&wsd)!=0)AfxMessageBox(WSAStartupfailed!);sendSocket=sock

98、et(AF_INET,SOCK_DGRAM,0);if(sendSocket=INVALID_SOCKET)AfxMessageBox(socket()failed:%d,WSAGetLastError();第第10章章 网络程序设计实例网络程序设计实例 bBroadcast=TRUE;setsockopt(sendSocket,SOL_SOCKET,SO_BROADCAST,(char*)&bBroadcast,sizeof(TRUE);recv.sin_family=AF_INET;recv.sin_port=htons(1031);recv.sin_addr.s_addr=INADDR_

99、BROADCAST;int ret = sendto(sendSocket, chFileData, 1024, 0,(SOCKADDR*)&recv,sizeof(recv);第第10章章 网络程序设计实例网络程序设计实例 if(ret=SOCKET_ERROR)AfxMessageBox(sendto()failed:%d,WSAGetLastError();elseclosesocket(sendSocket);WSACleanup();第第10章章 网络程序设计实例网络程序设计实例 广播信息接收程序与发送程序建立的过程类似,下面只给出其主要部分的代码:voidCBroadCastRec

100、vDlg:OnOK()SOCKETrecvSocket;SOCKADDR_INlocal;SOCKADDR_INsendaddr;BOOLbBroadcast;char*recvbuf=newchar1024;intsendsize;recvSocket=socket(AF_INET,SOCK_DGRAM,0);第第10章章 网络程序设计实例网络程序设计实例 if(recvSocket=INVALID_SOCKET)AfxMessageBox(socket()failed:%d,WSAGetLastError();bBroadcast=TRUE;setsockopt(recvSocket,SO

101、L_SOCKET,SO_BROADCAST,(char*)&bBroadcast,sizeof(TRUE);local.sin_family=AF_INET;local.sin_port=htons(1031);local.sin_addr.s_addr=inet_addr(192.168.0.9);第第10章章 网络程序设计实例网络程序设计实例 if(bind(recvSocket,(SOCKADDR*)&local,sizeof(local)=SOCKET_ERROR)AfxMessageBox(bind()failed:%d,WSAGetLastError();sendsize=size

102、of(sendaddr);int ret = recvfrom(recvSocket, recvbuf, 1024, 0,(SOCKADDR*)&sendaddr,&sendsize);if(ret=SOCKET_ERROR)第第10章章 网络程序设计实例网络程序设计实例 AfxMessageBox(sendto()failed:%d,WSAGetLastError();elsem_recvData=recvbuf;/m_recvData为与编辑框联系的变量deleterecvbuf;recvbuf=NULL;closesocket(recvSocket);WSACleanup();Updat

103、eData(FALSE);第第10章章 网络程序设计实例网络程序设计实例 当两个程序执行后,在发送程序的编辑框中输入要广播发送的信息,如“Iamastudent.”,按下广播信息接收程序的“接收信息”按钮。当按下广播信息发送程序的“广播发送”按钮后,在广播信息接收程序的编辑框中就可以收到广播程序发送来的信息,如图10-11所示。第第10章章 网络程序设计实例网络程序设计实例 图10-11广播通信程序执行结果第第10章章 网络程序设计实例网络程序设计实例 10.5.2组播通信程序设计1什么是组播组播又叫多址广播(Multicast)或多点传送,它也是一种一对多的传输方式,传输发起者通过一次传输就

104、能将信息传送给一组接收者。它与单点传送(Unicast)和广播(Broadcast)通信有明显的区别,组播将一个数据报发送给一个主机组,该主机组是由零个或多个用同一IP目的地址标识的主机集合。一个主机组具有如下特点: 主机组的成员是动态的,即没有数量上的限制。也就是说,主机可以在任何时间加入或离开主机组,这由主机上的应用程序决定。第第10章章 网络程序设计实例网络程序设计实例 主机组中的成员在位置上没有限制。也就是说,主机组中的成员可以是同一个局域网中的主机,也可以是不同局域网中的主机。一个主机可以同时是多个组的成员。任何主机,不管它是否属于一个主机组中的成员,都可以向一个主机组发送多址广播信

105、息。第第10章章 网络程序设计实例网络程序设计实例 2组播应用程序的设计在有关的RFC文档中已经规定了组播应用程序应该进行的一些操作: 当向一个组发送数据报时,应用程序可以不是该组播组中的成员。 一个应用程序若想接收某个多播组的数据,则必须加入该多播组。 一个应用程序加入一个多播组,仅需加入一个本地接口即可。 当应用程序向一个多播组发送数据报时,数据报仅从一个接口发出。第第10章章 网络程序设计实例网络程序设计实例 当应用程序向一个多播组发送数据报时,数据报的TTL值为1。 当应用程序向一个多播组发送数据报时,它只能从同一个接口接收数据报。这些是组播应用程序的缺省操作,为了使应用程序完成这些操

106、作,RFC中定义了一组专门用于组播的API,如表10-1所示。第第10章章 网络程序设计实例网络程序设计实例 表10-1Winsock2的多址广播API第第10章章 网络程序设计实例网络程序设计实例 这通过在WSASocket()函数的参数dwFlags中使用下面四个标志位来实现:WSA_FLAG_MULTIPOINT_C_ROOT,用来创建一个作为c_root节点的套接口,并且只在相应的WSAPROTOCOL_INFO入口中指示使用rooted控制平面时才允许。 WSA_FLAG_MULTIPOINT_C_LEAF,用来创建一个作为c_leaf节点的套接字,并且只在相应的WSAPROTOCO

107、L_INFO入口中指示XP1_SUPPORT_MULTIPOINT时才允许。第第10章章 网络程序设计实例网络程序设计实例 WSA_FLAG_MULTIPOINT_D_ROOT,用来创建一个作为d_root节点的套接口,并且只在相应的WSAPROTOCOL_INFO入口中指示使用rooted数据平面时才允许。 WSA_FLAG_MULTIPOINT_D_LEAF,用来创建一个作为d_leaf节点的套接口,并且只在相应的WSAPROTOCOL_INFO入口中指示了XP1_SUPPORT_MULTIPOINT时才允许。第第10章章 网络程序设计实例网络程序设计实例 WSAIoctl()函数设置多址

108、广播数据报传播范围(TTL)的典型调用如下:nRet=WSAIoctl(hSock,/*socket*/SIO_MULTICAST_SCOPE,/*IPTimeToLive*/&nIP_TTL,/*input*/sizeof(nIP_TTL),/*size*/NULL,/*output*/0,/*size*/&cbRet,/*bytesreturned*/NULL,/*overlapped*/NULL);/*completionroutine*/第第10章章 网络程序设计实例网络程序设计实例 WSAJoinLeaf()函数用来加入一个叶子节点到多址广播会话,其函数原型为:SOCKETWSAAP

109、IWSAJoinLeaf(SOCKETs,conststructsockaddrFAR*name,intnamelen,LPWSABUFlpCallerData,LPWSABUFlpCalleeData,LPQOSlpSQOS,LPQOSlpGQOS,DWORDdwFlags);第第10章章 网络程序设计实例网络程序设计实例 WSAJoinLeaf()具有与WSAConnect()相同的参数和语法 , 但 它 还 返 回 一 个 套 接 口 描 述 符 (这 和 函 数WSAAccept()一样)及一个dwFlags参数。参数dwFlags用来指示套接口是用来作为发送者还是接收者或是两者兼顾。

110、在此函数中,只有多址广播套接口可以用来作为输入参数s。如果此多址广播套接口处于非阻塞模式,则返回的套接口描述符只有在接收到相应的FD_CONNECT指示后才能使用。参数name指示套接口将加入的多址广播的地址。下面是加入一个多播组时的典型调用:第第10章章 网络程序设计实例网络程序设计实例 hNewSock=WSAJoinLeaf(hSock,/*socket*/(PSOCKADDR)&stDestAddr, /*multicast address*/sizeof(stDestAddr),/*lengthofaddrstruct*/NULL,/*callerdatabuffer*/NULL,/

111、*calleedatabuffer*/NULL,/*socketQOSsetting*/NULL,/*socketgroupQOS*/JL_BOTH);/*doboth:send*and*receive*/第第10章章 网络程序设计实例网络程序设计实例 3组播应用程序设计实例下面是一个简单的多址广播程序实例,也是一个典型的多址广播程序。#include#include#defineBUFSIZE 1024#defineMAXADDRSTR16#defineLOOPCOUNT100第第10章章 网络程序设计实例网络程序设计实例 /*检查系统中是否安装了合适版本的WinsockDLL。*/intC

112、heckWinsockVersion(VOID)WORDwVersionRequested;WSADATAwsaData; interr; /异步I/O和多址广播只有在Winsock2.0以上版本才支持wVersionRequested=MAKEWORD(2,2);err=WSAStartup(wVersionRequested,&wsaData); if(err=0)第第10章章 网络程序设计实例网络程序设计实例 if(LOBYTE(wsaData.wVersion)=2)&(HIBYTE(wsaData.wVersion)=2)/确认WinsockDLL支持2.0return0;/*Win

113、sockDLL可接受,成功返回*/WSACleanup();err=WSAVERNOTSUPPORTED;/*不支持,失败返回*/*TelltheuserthatwecouldntfindausableWinsockDLL.*/printf(Winsock DLL does not support requested APIversion.n);returnerr;第第10章章 网络程序设计实例网络程序设计实例 intmain()intnRet,i;intnIP_TTL=1;/在本子网中传播BOOLbFlag;DWORDdFlag,cbRet;intiLen=MAXADDRSTR;charst

114、rDestMultiMAXADDRSTR=224.1.1.1;/多址广播地址第第10章章 网络程序设计实例网络程序设计实例 SOCKADDR_INstSrcAddr,stDestAddr;SOCKEThSock,hNewSock;u_shortnDestPort=6666;WSABUFstWSABuf;charachInBufBUFSIZE;charachOutBuf=Messagenumber:;nRet=CheckWinsockVersion();/检查Winsock版本号if(nRet)printf(WSAStartupfailed:%drn,nRet);exit(1);第第10章章 网

115、络程序设计实例网络程序设计实例 /将字符串地址转换为套接字地址nRet=WSAStringToAddress(strDestMulti,/*addressstring*/AF_INET,/*addressfamily*/NULL,/*protocolinfostructure*/(LPSOCKADDR)&stDestAddr,/*socketaddressstring*/&iLen);/*lengthofsocketstructure*/第第10章章 网络程序设计实例网络程序设计实例 if(nRet)printf(WSAStringToAddress(%s)failed,Err:%dn,str

116、DestMulti,WSAGetLastError();exit(1); /创建一个多址广播套接字hSock=WSASocket(AF_INET,SOCK_DGRAM,IPPROTO_UDP,(LPWSAPROTOCOL_INFO)NULL,0,WSA_FLAG_OVERLAPPED第第10章章 网络程序设计实例网络程序设计实例 |WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF);if(hSock=INVALID_SOCKET)printf(WSASocket()failed,Err:%dn,WSAGetLastError();exi

117、t(1);bFlag=TRUE;/设置套接口为可重用端口地址第第10章章 网络程序设计实例网络程序设计实例 nRet=setsockopt(hSock,/*socket*/SOL_SOCKET,/*socketlevel*/SO_REUSEADDR,/*socketoption*/(char*)&bFlag,/*optionvalue*/sizeof(bFlag);/*sizeofvalue*/if(nRet=SOCKET_ERROR)printf(setsockopt()SO_REUSEADDRfailed,Err:%dn,WSAGetLastError();第第10章章 网络程序设计实例网

118、络程序设计实例 /将套接口绑定到用户指定端口及默认的接口stSrcAddr.sin_family=PF_INET;stSrcAddr.sin_port=htons(nDestPort);stSrcAddr.sin_addr.s_addr=INADDR_ANY;nRet=bind(hSock,(structsockaddrFAR*)&stSrcAddr,sizeof(structsockaddr);if(nRet=SOCKET_ERROR)printf(bindfailed,Err:%dn,WSAGetLastError();第第10章章 网络程序设计实例网络程序设计实例 /设置多址广播数据报传

119、播范围(TTL)nRet=WSAIoctl(hSock,/*socket*/SIO_MULTICAST_SCOPE,/*IPTimeToLive*/&nIP_TTL,/*input*/sizeof(nIP_TTL),/*size*/NULL,/*output*/0,/*size*/&cbRet,/*bytesreturned*/NULL,/*overlapped*/NULL);/*completionroutine*/第第10章章 网络程序设计实例网络程序设计实例 if(nRet)printf(WSAIoctl(SIO_MULTICAST_SCOPE)failed,Err:%dn,WSAGet

120、LastError();/允许内部回送(LOOPBACK)。Windows95不支持该选项bFlag=TRUE;nRet=WSAIoctl(hSock,/*socket*/SIO_MULTIPOINT_LOOPBACK,/*LoopBackonoroff*/&bFlag,/*input*/sizeof(bFlag),/*size*/NULL,/*output*/第第10章章 网络程序设计实例网络程序设计实例 0,/*size*/&cbRet,/*bytesreturned*/NULL,/*overlapped*/NULL);/*completionroutine*/if(nRet)printf

121、(WSAIoctl(SIO_MULTIPOINT_LOOPBACK)failed,Err:%dn,SAGetLastError();stDestAddr.sin_family=PF_INET;nRet=WSAHtons(hSock,/*socket*/第第10章章 网络程序设计实例网络程序设计实例 nDestPort,/*hostordervalue*/&(stDestAddr.sin_port);/*networkordervalue*/If(nRet=SOCKET_ERROR)printf(WSAHtons()failed,Err:%dn,WSAGetLastError();/加入到指定多

122、址广播组,指定为既作发送者又作接收者/在IPMulticast中,返回的套接字描述符和输入的套接字描述符相同第第10章章 网络程序设计实例网络程序设计实例 hNewSock=WSAJoinLeaf(hSock,/*socket*/(PSOCKADDR)&stDestAddr, /*multicastaddress*/sizeof(stDestAddr),/*lengthofaddrstruct*/NULL,/*callerdatabuffer*/NULL,/*calleedatabuffer*/NULL,/*socketQOSsetting*/NULL,/*socketgroupQOS*/JL

123、_BOTH);/*doboth:send*and*receive*/第第10章章 网络程序设计实例网络程序设计实例 if(hNewSock=INVALID_SOCKET)printf (WSAJoinLeaf() failed, Err: %dn,WSAGetLastError();/在循环中发送/接收数据。测试时可以改为无限循环for(i=0;iLOOPCOUNT;i+)staticiCounter=1;stWSABuf.buf=achOutBuf;stWSABuf.len=lstrlen(achOutBuf);第第10章章 网络程序设计实例网络程序设计实例 cbRet=0;itoa(iCo

124、unter+,&achOutBuf16,10);nRet=WSASendTo(hSock, /*socket*/&stWSABuf,/*outputbufferstructure*/1,/*buffercount*/&cbRet,/*numberofbytessent*/0,/*flags*/(structsockaddr*)&stDestAddr,/*destinationaddress*/第第10章章 网络程序设计实例网络程序设计实例 sizeof(structsockaddr),/*sizeofaddrstructure*/NULL,/*overlappedstructure*/NULL

125、);/*overlappedcallbackfunction*/if(nRet=SOCKET_ERROR)printf(WSASendTo()failed,Err:%dn,WSAGetLastError();第第10章章 网络程序设计实例网络程序设计实例 stWSABuf.buf=achInBuf;stWSABuf.len=BUFSIZE;cbRet=0;iLen=sizeof(stSrcAddr);dFlag=0;nRet=WSARecvFrom(hSock,/*socket*/&stWSABuf,/*inputbufferstructure*/第第10章章 网络程序设计实例网络程序设计实例

126、 1,/*buffercount*/&cbRet,/*numberofbytesrecvd*/&dFlag, /*flags*/(structsockaddr*)&stSrcAddr/*sourceaddress*/&iLen, /*sizeofaddrstructure*/NULL,/*overlappedstructure*/NULL);/*overlappedcallbackfunction*/第第10章章 网络程序设计实例网络程序设计实例 if(nRet=SOCKET_ERROR)printf(WSARecvFrom() failed, Err:%dn,WSAGetLastError(

127、);elseu_shortnPort=0;charachAddrMAXADDRSTR+3=0;/如果长度太小则返回Windows错误122iLen=MAXADDRSTR+3;nRet=WSAAddressToString(/将地址转换为字符串第第10章章 网络程序设计实例网络程序设计实例 (structsockaddr*)&stSrcAddr,/*sourceaddress*/sizeof(stSrcAddr),/*sizeofaddrstruct*/NULL,/*protocolinfo*/achAddr,/*addressstring*/&iLen);/*addrstringbuflen*

128、/if(nRet=SOCKET_ERROR)printf(WSAAddressToString()failed,Err:%dn,WSAGetLastError();第第10章章 网络程序设计实例网络程序设计实例 nRet=WSANtohs(hSock,/*socket*/stSrcAddr.sin_port,/*hostordervalue*/&nPort);/*networkordervalue*/if(nRet=SOCKET_ERROR)printf(WSANtohs() failed, Err: %dn,WSAGetLastError();printf(WSARecvFrom()rece

129、ived%dbytesfrom%s,port%d:%srn,cbRet,achAddr0?achAddr:?,nPort,achInBuf);第第10章章 网络程序设计实例网络程序设计实例 /*endfor(;)*/closesocket(hNewSock);closesocket(hSock);/卸载WinsockDLL WSACleanup(); return(0);/*endmain()*/第第10章章 网络程序设计实例网络程序设计实例 习题习题 1把10.1节中基于UDP的程序改写成一个基于TCP的程序。2按照10.2节中给出的使用MFC设计网络程序的步骤调试该实例程序,观察运行结果。3调试10.3节中基于WinInetAPI的Internet程序。4阅读10.4节中基于原始套接口的程序和10.5节中的广播通信程序,并上机调试。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 商业/管理/HR > 营销创新

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号