《TCP IP协议与网络编程》课件第10章 网络程序设计实例_第1页
《TCP IP协议与网络编程》课件第10章 网络程序设计实例_第2页
《TCP IP协议与网络编程》课件第10章 网络程序设计实例_第3页
《TCP IP协议与网络编程》课件第10章 网络程序设计实例_第4页
《TCP IP协议与网络编程》课件第10章 网络程序设计实例_第5页
已阅读5页,还剩195页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

1.UDP程序1的源代码

//编程环境:VC6.0 //程序说明:基于用户数据报协议UDP的程序实例

//程序功能:在窗口中点击鼠标左键,则发送信息到端口号为6000的远程机上,并可以从端

//口号6000的远程机接收数据 #include"stdafx.h" #include<Winsock.h> /////////////////////////////////////////////////////////////////////////////////////////////////////// //说明全局量

HINSTANCEhInst; LRESULTCALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); ////////////////////////////////////////////////////////////////////////////////////////////////////// intAPIENTRYWinMain(HINSTANCEhI,HINSTANCEhP,LPSTRlp,intnC) { MSGmsg;HWNDhWnd; //实例句柄存入全局量hInsthInst=hI; WNDCLASSwc; memset(&wc,0,sizeof(WNDCLASS)); wc.lpfnWndProc =(WNDPROC)WndProc;wc.hInstance =hI;wc.hIcon =LoadIcon(NULL,IDI_APPLICATION); wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszClassName ="W1"; RegisterClass(&wc); hWnd=CreateWindowEx(WS_EX_PALETTEWINDOW,"W1","UDP程序1[端口:8000]", WS_OVERLAPPEDWINDOW, 100,100,200,100, NULL,NULL,hI,NULL); if(!hWnd) returnFALSE; ShowWindow(hWnd,nC); //主消息循环

while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } returnmsg.wParam; } //////////////////////////////////////////////////////////////////////////////////////////////// //定义WinsockAPI需要的量

WSADATAws; SOCKETs1,s2; structsockaddr_inCs1A,Cs2A; //发送数据缓冲

charsendBuf[200]; //接收数据缓冲charrecvBuf[200];intlen; //存放接收到的字节数#defineCs1Port8000 //本地端口号#defineCs2Port6000 //远程端口号#defineCs1IP"" //本地IP地址#defineCs2IP"" //远程IP地址////////////////////////////////////////////////////////////////////////////////////////////////////////////////消息处理LRESULTCALLBACKWndProc(HWNDhW,UINTmsg,WPARAMwP,LPARAMlP) { switch(msg) { caseWM_DESTROY: //关闭程序触发事件

//向Windows注销Socket触发事件

WSAAsyncSelect(s1,hW,0,0); //释放连接

closesocket(s1); //卸载动态链接库WinsockDLL WSACleanup(); PostQuitMessage(0); break; caseWM_CREATE: //初始化触发事件

//装载动态链接库WinsockDLL WSAStartup(0x0202,&ws); //创建UDP套接字

s1=socket(AF_INET,SOCK_DGRAM,0); //设置地址类型

Cs1A.sin_family=AF_INET; //本地端口

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; //远程端口

Cs2A.sin_port=htons(Cs2Port); //远程IP地址 Cs2A.sin_addr.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)) { caseFD_READ: //接收远程信息

len=recv(s1,recvBuf,sizeof(recvBuf),0); recvBuf[len]=0; MessageBox(0,recvBuf,"UDP程序1从UDP程序2收到的数据",MB_OK); break; } break; caseWM_LBUTTONDOWN: //点击鼠标左键

//将发送数据放入缓冲区

wsprintf(sendBuf,"Hello,UDP程序2!"); len=sizeof(Cs2A); //发送信息

sendto(s1,sendBuf,strlen(sendBuf),0,(structsockaddr*)&Cs2A,len); break; } returnDefWindowProc(hW,msg,wP,lP); } 2.UDP程序2的源代码

//编程环境:VC6.0 //程序说明:基于用户数据报协议UDP的程序实例

//程序功能:在窗口里点击鼠标左键,则发送信息到端口为8000的远程机上,并可以从端口

//为8000的远程机接收数据

#include"stdafx.h" #include<Winsock.h> ////////////////////////////////////////////////////////////////////////////////////// //说明全局量

HINSTANCEhInst; LRESULTCALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); //-------------------------------------------------------------- intAPIENTRYWinMain(HINSTANCEhI,HINSTANCEhP,LPSTRlp,intnC) { MSGmsg; HWNDhWnd;//实例句柄存入全局量hInst hInst=hI; WNDCLASSwc; memset(&wc,0,sizeof(WNDCLASS)); wc.lpfnWndProc =(WNDPROC)WndProc; wc.hInstance =hI; 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, 400,200,200,100, NULL,NULL,hI,NULL); if(!hWnd) returnFALSE; ShowWindow(hWnd,nC); //主消息循环

while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } returnmsg.wParam;} ////////////////////////////////////////////////////////////////////////////////////////////// //定义WinsockAPI需要的量

WSADATAws; SOCKETs1,s2; structsockaddr_inCs1A,Cs2A; //发送数据缓冲

charsendBuf[200]; //接收数据缓冲

charrecvBuf[200]; intlen; //存放接收到的字节数 #defineCs1Port6000 //本地端口

#defineCs2Port8000 //远程端口

#defineCs1IP"" //本地网址

#defineCs2IP"" //远程网址

//-------------------------------------------------------------- //消息处理

LRESULTCALLBACKWndProc(HWNDhW,UINTmsg,WPARAMwP,LPARAMlP) { switch(msg) { caseWM_DESTROY: //关闭程序触发事件

//向Windows注销Socket触发事件

WSAAsyncSelect(s1,hW,0,0); //释放连接

closesocket(s1); //卸载动态链接库WinsockDLL WSACleanup(); PostQuitMessage(0); break; caseWM_CREATE: //程序初始化触发事件

//装载动态链接库WinsockDLL WSAStartup(0x0202,&ws); //创建UDP套接字

s1=socket(AF_INET,SOCK_DGRAM,0); //设置地址类型

Cs1A.sin_family=AF_INET; //本地端口

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; //远程端口

Cs2A.sin_port=htons(Cs2Port); //远程IP地址

Cs2A.sin_addr.s_addr=inet_addr(Cs2IP); //向Windows注册网络事件

WSAAsyncSelect(s1,hW,WM_USER+1,FD_READ); break; caseWM_USER+1: //Socket触发事件

switch(LOWORD(lP)) { caseFD_READ: //接收远程信息

len=recv(s1,recvBuf,sizeof(recvBuf),0); recvBuf[len]=0; MessageBox(0,recvBuf,"UDP程序2从UDP程序1收到的数据",0); break; } break; caseWM_LBUTTONDOWN: //点击鼠标左键

wsprintf(sendBuf,"Hello,UDP程序1!"); len=sizeof(Cs2A); //发送信息

sendto(s1,sendBuf,strlen(sendBuf),0,(structsockaddr*)&Cs2A,len); break; } returnDefWindowProc(hW,msg,wP,lP); } 10.1.2程序运行结果 在这两个程序调试、编译和链接正确后,就可以执行这两个程序了。这两个程序执行后的窗口如图10-1所示,其中,图(a)是UDP程序1执行后的窗口,图(b)是UDP程序2执行后的窗口。(a) UDP程序1的窗口(b) UDP程序2的窗口 图10-1

UDP程序执行后的窗口

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

UDP程序收到数据后弹出的窗口

在学习这两个程序的过程中,我们应该注意两个问题:

(1)在程序中,虽然使用的是数据报传输方式(SOCK_DGRAM),但却使用bind( )函数进行了地址的绑定。

(2)这两个程序的功能是一样的,所以代码几乎是相同的。10.2使用MFC类库进行网络程序

设计的实例

10.2.1创建客户端程序 我们使用VC++提供的可视化集成编程环境来创建客户端程序。创建客户端程序的主要步骤介绍如下。

1.创建一个工程 使用VC++的应用程序生成向导(MFCAppWizard[exe])创建一个基于对话框的工程,该工程的名称为CSockClient。

2.设计程序对话框 如图10-3所示。图10-3添加类变量 3.生成CAsyncSocket类的子类MySock 4.在MySock.ccp文件中添加文件包含信息 在生成的MySock.cpp中添加如下文件包含信息:

#include"CSockClient.h" #include"CSockClientDlg.h“ 5.向MySock.h文件中添加代码 向MySock.h文件中添加如下代码:

public: BOOLm_bConnected; //是否连接

UINTm_nLength; //消息长度

charm_szBuffer[4096]; //消息缓冲区 6.重载MySock.ccp文件中的各函数

(1) MySock( )函数重载的代码如下:

MySock::MySock( ) { m_nLength=0; memset(m_szBuffer,0,sizeof(m_szBuffer)); m_bConnected=FALSE } MySock::~MySock( ) { //关闭套接字if(m_hSocket!=INVALID_SOCKET) Close( ); } (2) OnReceive( )函数重载的代码如下:

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::OnReceive(nErrorCode); } (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); } (4) OnConnect( )函数重载的代码如下:

voidMySock::OnConnect(intnErrorCode) { if(nErrorCode==0) { m_bConnected=TRUE; CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp( ); CCSockClientDlg*pDlg=(CCSockClientDlg*)pApp->m_pMainWnd; memcpy(m_szBuffer,"Connectedto",13);strncat(m_szBuffer,pDlg->m_szServerAdr,sizeof(pDlg->m_szServerAdr));pDlg->m_MSGS.InsertString(0,m_szBuffer);//提请一个''读''的网络事件,准备接收AsyncSelect(FD_READ); } CAsyncSocket::OnConnect(nErrorCode); } 7.新建一个输入地址的对话框 新建一个输入地址信息的对话框IDD_Addr用来输入IP地址和端口号;生成一个新的基于对话框的类CAddrDlg。在该对话框中增加两个编辑(Edit)控件:一个为IDC_Addr,用来输入要连接的IP地址;另一个为IDC_Port,用来输入端口号。 使用类向导(ClassWizard)为CAddrDlg类添加m_Addr和m_Port两个类变量。它们的ID标识、类型和名称如下:

ControlID Type Member IDC_Addr CString m_Addr IDC_Port Int m_Port

然后向CSockClientDlg类中添加如下代码:

protected: intTryCount; MySockm_clientSocket; UINTm_szPort; public: charm_szServerAdr[256]; 10.添加“连接”按钮的程序代码 双击IDD_CSOCKCLIENT_DIALOG对话框中的“连接”按钮,添加以下代码:

voidCCSockClientDlg::OnConnect( ) { m_clientSocket.ShutDown(2); m_clientSocket.m_hSocket=INVALID_SOCKET; m_clientSocket.m_bConnected=FALSE; CAddrDlgm_Dlg; //默认端口7 m_Dlg.m_Port=7; if(m_Dlg.DoModal( )==IDOK &&!m_Dlg.m_Addr.IsEmpty( )) { memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr)); m_szPort=m_Dlg.m_Port; //建立计时器,每1秒尝试连接一次,直到连上或 TryCount>10 SetTimer(1,1000,NULL); TryCount=0; } } 11.添加Windows消息WM_TIMER的响应函数OnTimer voidCCSockClientDlg::OnTimer(UINTnIDEvent) { if(m_clientSocket.m_hSocket==INVALID_SOCKET) BOOL bFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNECT); if(!bFlag) { AfxMessageBox("SocketError!"); m_clientSocket.Close( ); PostQuitMessage(0); return; } } m_clientSocket.Connect(m_szServerAdr,m_szPort); TryCount++; if(TryCount>=10||m_clientSocket.m_bConnected) { KillTimer(1);if(TryCount>=10) AfxMessageBox("ConnectFailed!");return; } CDialog::OnTimer(nIDEvent); } 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.AsyncSelect(FD_WRITE); m_MSG.SetWindowText(""); } } 13.添加“关闭”按钮的程序代码 双击IDD_CSOCKCLIENT_DIALOG对话框中的“关闭”按钮,添加以下代码:

voidCCSockClientDlg::OnExit( ) { //关闭Socket m_clientSocket.ShutDown(2); //关闭对话框

EndDialog(0); }

上述步骤完成后,调试程序并建立可执行的 .EXE文件。当该项目运行时,弹出如图10-4所示的程序对话框。点击“Connect”按钮,弹出如图10-5所示的对话框,输入要连接的服务器的IP地址和端口号,然后单击“OK”按钮。当连接建立后,就可以在客户程序对话框中输入回送信息,并按“Send”按钮进行程序测试了。图10-4客户程序对话框图10-5地址信息输入对话框 10.2.2创建服务器端程序

1.建立一个CAsyncSocket的子类 在本例中,由于我们要实现的是一个回声程序,所以服务器程序应将收到的信息原封不动地发送给客户程序。因此,服务器程序的代码主要是重载CAsyncSocket类的接收和发送函数。 建立一个CAsyncSocket类的子类CNewSocket,并重载CAsyncSocket类的OnReceive( )和OnSend( )函数。程序代码如下:

CnewSocket::OnReceive(intnErrorCode){ m_nLength=receive(m_szBuffer, sizeof(m_szBuffer), 0);

//直接转发消息AsyncSelect(FD_WRITE);}

CNewSocket::OnSend(intnErrorCode){ send(m_szBuffer,m_nLength,0);} 2.创建一个用于建立连接的类CMyServerSocket

创建一个用于建立连接的类CMyServerSocket,并重载CAsyncSocket类的OnAccept( )函数。 在MyServerSocket.h中声明如下变量:

public:

CNewSocket*m_pSocket;

OnAccept( )程序的代码如下:

voidCMyServerSocket::OnAccept(intnErrorCode){ //侦听到连接请求,调用Accept函数

CNewSocket*pSocket=newCNewSocket( );

if(Accept(*pSocket)) {pSocket->AsyncSelect(FD_READ);m_pSocket=pSocket;

} elsedeletepSocket;} 3.为对话框添加一个“侦听”按钮 为对话框添加一个“侦听”按钮,并添加如下代码。 在CSockServerDlg.ccp中声明变量:

public:

CMyServerSocketm_srvrSocket; OnListen( )函数的代码如下:

voidCCSockServerDlg::OnListen( ) { if(m_srvrSocket.m_hSocket==INVALID_SOCKET) { BOOLbFlag=m_srvrSocket.Create (UserPort,SOCK_STREAM,FD_ACCEPT);

if(!bFlag) { AfxMessageBox(''SocketError!'');

M_srvrSocket.Close( );

PostQuitMessage(0);

Return;

} } // ''侦听''成功,等待连接请求

if(!m_srvrSocket.Listen(1)) { intnErrorCode=m_srvrSocket.GetLastError( );

if(nError!=WSAEWOULDBLOCK) { AfxMessageBox(''SocketError!'');

M_srvrSocket.Close( );

PostQuitMessage(0);

Return;

} } }10.3基于WinInetAPI的

客户程序编写实例 10.3.1应用程序说明 编程环境:VC++6.0。 程序功能:给定Internet中一台服务器的域名,客户机首先与该域名的服务器建立连接,然后查询该服务器提供的功能。当然用户也可以随便输入一个域名,看是否在Internet中有该域名的服务器。 10.3.2建立应用程序的用户操作界面 编写该程序要求用户对VC的编程环境比较熟悉。在该程序中我们建立一个基于对话框的用户界面。

1.使用AppWizard生成对话框程序框架

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

(3)在弹出的“MFCAppWizard-step2of4”对话框中,取消ActiveXControls控件的选中标记,并且注意不要选择“WindowsSockets”,因为在该程序中我们不直接调用WindowsSockets函数;在对话框标题栏中输入“服务器功能扫描程序”,再单击“下一步”按钮。

(4)在弹出的“MFCAppWizard-step3of4”对话框中,选择MFC所使用的链接库,一般选择静态链接库,再单击“下一步”按钮。 (5)在“MFCAppWizard-step4of4”对话框中,一般不需要改变MFCAppWiard自动生成的各种类名,直接按“完成”按钮即可,这时弹出一个新建的有关工程和信息的对话框。

2.编程对话框

(1)添加一个显示输出结果的编辑框。使用VC的资源工具给对话框添加一个编辑框,然后右击编辑框,在弹出的下拉菜单中选择“Properties”,在弹出的编辑控件属性对话框中,将资源的ID标识符设置为IDC_EDIT1,再给该编辑框增加一些其他类型属性。如在“Styles”选项卡中,选中图10-7所示的“Mutiline”等属性。

图10-7属性对话框 (2)添加一个输入网址的编辑框。用与上面类似的方法再增加另一个编辑框,编辑框的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-8应用程序对话框 10.3.3应用程序代码及其说明

1.给编辑框添加变量 为了完成程序的显示和输入功能,需要给编辑框添加对应的变量。为了简单起见,我们可以使用“ClassWizard”给编辑框添加变量。编辑框IDC_EDIT1附加的变量为m_out,编辑框IDC_EDIT2附加的变量为m_host,它们都应为CString类型的变量。 2.HTTP协议查询代码 给对话框类CScanDlg添加一个函数,用于实现HTTP查询。函数原形为:

voidTryURL(CStringURL);

其实现代码为:

voidCScanDlg::TryURL(CStringURL) { CInternetSessionsession; m_out+="正在链接"+URL+"\r\n";UpdateData(FALSE);CInternetFile*file=NULL;try{file=(CInternetFile*)session.OpenURL(URL);}catch(CInternetException*pEx){file=NULL;pEx->Delete( );}if(file){m_out+="已建立链接。\r\n";CStringline;for(inti=0;i<20&&file->ReadString(line);i++){m_out+=line+"\r\n";}file->Close( );deletefile;}else{m_out+="本地址没有发现http主机\r\n";}m_out+="------------------------------------------------------\r\n";UpdateData(FALSE);}

下面,我们分析一下这个函数是如何实现HTTP链接的。首先,根据我们在第9章所学的知识,需要建立一个Internet会话,这就要定义一个CInternetSession的对象。其构造函数的原形如下:

CInternetSession(LPCTSTRpstrAgent=NULL,DWORDdwContext=1,DWORDdwAccessType=INTERNET_OPEN_TYPE_PRECONFIG,LPCTSTRpstrProxyName=NULL,LPCTSTR pstrProxyBypass=NULL,DWORDdwFlags=0);

这个函数需要定义很多参数,但本程序都使用缺省值,即“=”号后的值。CInternetSession构造函数参数说明如下: ● LPCTSTRpstrAgent:应用程序名,如果为NULL,将填入在AppWizard中给定的程序名。 ● DWORDdwContext:本操作的设备关联符定义。 ● DWORDdwAccessType:访问类型,可以为以下参数之一:INTERNET_OPEN_ TYPE_PRECONFIG(default),INTERNET_OPEN_TYPE_DIRECT或INTERNET_OPEN_ TYPE_PROXY。 ●LPCTSTRpstrProxyName:如访问类型为INTERNET_OPEN_TYPE_PROXY,则给该参数赋予协议名称。 ●LPCTSTRpstrProxyBypass:如访问类型为INTERNET_OPEN_TYPE_PROXY,则该参数为不通过协议服务器而直接链接的一系列地址。 ●DWORDdwFlags:可为以下参数:INTERNET_FLAG_DON'T_CACHE,INTERNET_ FLAG_ASYNC和INTERNET_FLAG_OFFLINE。 ●DWORDdwAccessType:值缺省时将使用系统注册簿定义的值。显然,程序允许使用者定义访问类型将比由程序内部直接定义要好。因此,要正确使用本程序,必须先在Windows系统中定义好网络访问类型,步骤如下:

(1)双击桌面上“我的电脑”图标。

(2)双击“控制面板”。

(3)点击“Internet”。

(4)在随后弹出的对话框中选择“连接”,然后填写网络连接属性。如果是通过拨号上网的,选中“拨号设置”选择项,并填写相关属性;如果是局域网(LAN)上网的,则要进入局域网(LAN)设置代理服务器的地址和端口号。

注意:如果所使用的计算机已经接入Internet,则不需要进行如上设置。 本程序在构造CInternetSession对象时使用缺省值,因此,构造函数将不带任何参数,如下所示:

CInternetSessionsession;

在构造对象session后,如下两行程序进行输出提示,表示程序已开始工作:m_out+="正在链接"+URL+"\r\n";UpdateData(FALSE);

接下来使用session对象的成员函数OpenURL( )来打开一个URL资源。该函数返回一个文件的指针,文件类型为以下四种之一:

file: //如果访问的是本地机器,函数返回一个CStudioFile类对象的指针

ftp: //如果访问的是FTP地址,函数返回一个CInternetFile类对象的指针

gopher://如果访问的是Gopher地址,函数返回一个CGopherFile类对象的指针

http://如果访问的是HTTP地址,函数返回一个CHttpFile类对象的指针

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

pEx->Delete( ); }

通过以上代码,程序将使用用户指定的地址来试图打开HTTP网址,如果失败,返回的文件为空,程序将继续运行,尝试用其他协议打开网址。随后,无论网址是否成功打开,我们都应作相应输出以提示用户。如果成功,用一个for循环读出返回文件的头20句并输出;如果失败,也要给出相应的提示。程序代码如下:

if(file)//判定链接是否成功

{ m_out+="已建立链接。\r\n"; CStringline; for(inti=0;i<20&&file->ReadString(line);i++){m_out+=line+"\r\n";}file->Close( );deletefile;}else{m_out+="本地址没有发现http主机\r\n";}m_out+="------------------------------------------------------\r\n";UpdateData(FALSE);

用于查询的函数编写完后,还要给按钮的单击事件增加代码,以便按钮按下时程序开始查询。给按钮IDC_BUTTON_HTTP增加BN_CLICKED消息的代码如下:

voidCScanDlg::OnScanHttp( ) { constCStringhttp="http://"; UpdateData(TRUE); m_out=""; UpdateData(FALSE); TryURL(http+m_host); TryURL(http+"www."+m_host); }

这段程序代码中,UpdateData(TRUE)的调用将给m_host赋予用户定义的值。UpdateData(FALSE)语句将清空输出编辑框变量m_out的内容,接下来调用两次TryURL( )。例如,当用户输入时,程序将首先试一试,然后试一试。到此为止,就可以编译程序并执行了。在该例中,我们在执行时输入后,再单击“HTPP”按钮。在连接到Internet后,程序输出主页的头20行,如图10-9所示。图10-9服务器功能扫描程序对话框 3.FTP协议查询代码 下面我们来探讨如何实现FTP链接。首先给对话框类增加一个实现链接的函数TryFTP,代码如下:

voidCScanDlg::TryFTP(CStringhost) { CInternetSessionsession; m_out+="正在链接FTP地址"+host+"\r\n"; UpdateData(FALSE); CFtpConnection*connection=NULL; try { connection=session.GetFtpConnection(host); } catch(CInternetException*pEx) { connection=NULL; pEx->Delete( ); }if(connection) { m_out+="已建立链接。\r\n"; CStringline; connection->GetCurrentDirectory(line); m_out+="缺省目录为"+line+"\r\n"; connection->Close( ); deleteconnection; } else { m_out+="本地址没有发现ftp主机。\r\n"; } m_out+="------------------------------------------------------\r\n"; UpdateData(FALSE); }

本函数和TryURL( )很相似,不过它不使用OpenURL( )来打开一个文件,而用GetFtpConnection( )与FTP服务器建立链接。如果链接成功,将使用GetCurrentDirectory( )来得到服务器的缺省目录。 大多数FTP地址前有前缀ftp.,一些早期的地址没有此前缀。现在,给IDC_BUTTON_FTP链接单击消息的处理函数,并编写如下程序代码: voidCScanDlg::OnButtonFtp( ) { UpdateData(TRUE); m_out=""; UpdateData(FALSE); TryFTP(m_host); TryFTP("ftp."+m_host); }

现在已完成了FTP链接的实现代码。重新编译程序并运行,键入,可以发现在处提供ftp服务。从程序开始查询到输出结果将有一段较长的时间,因为程序要等结果全部探查到后再显示,但实际上现在的商业Internet程序都是实时显示的。如果我们希望结果实时显示,就要使用异步套接口(AsynchronousSockets)或多线程编程。 4.Gopher协议查询代码

Gopher是一种基于文本的协议,它和WWW相似,可以通过点击链接点实现网络的链接和浏览。但是,Gopher是通过逐级文字菜单来组织链接和内容的,它不像WWW那样有丰富的多媒体页面。要实现Gopher查询,我们应给CScanDlg类增加另一个成员函数voidTryGopher(CStringhost)。如果查询成功,该函数将返回查询地址的第一个Gopher位置(locator)。位置是Gopher协议的概念,任何Gopher客户程序必须先得到一个Gopher位置,然后才能进行相应的Gopher操作。TryGopher( )函数如下: voidCScanDlg::TryGopher(CStringhost) { CInternetSessionsession; m_out+="正在链接gopher地址"+host+"\r\n"; UpdateData(FALSE); CGopherConnection*connection=NULL; try { connection=session.GetGopherConnection(host); }catch(CInternetException*pEx){connection=NULL;pEx->Delete( );}if(connection){m_out+="已建立链接。\r\n";CStringline;CGopherLocatorlocator=connection->CreateLocator(NULL,NULL,GOPHER_TYPE_DIRECTORY);line=locator;m_out+="第一个Gopher位置是"+line+"\r\n";connection->Close( );deleteconnection;}else{m_out+="本地址没有发现gopher主机。\r\n";}m_out+="------------------------------------------------------\r\n";UpdateData(FALSE);}

本函数和前两个函数大致相似,下面对主要语句进行一些说明。

(1)建立Gopher链接的语句是:connection=session.GetGopherConnection(host); (2)建立一个Gopher位置(locator)的语句是:CGopherLocatorlocator=connection->CreateLocator(NULL,NULL, GOPHER_TYPE_DIRECTORY);

注意:函数CreateLocator( )有多个版本,这里使用的是含有三个参数的版本。其函数原形如下:

CGopherLocatorCreateLocator(LPCTSTRpstrDisplayString,  LPCTSTRpstrSelectorString,  DWORDdwGopherType);

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

现在,给IDC_BUTTON_GOPHER链接单击消息的处理函数,并编写如下程序:

voidCScanDlg::OnButtonGopher( ) { UpdateData(TRUE); m_out=""; UpdateData(FALSE); TryGopher(m_host); TryGopher("gopher."+m_host); }

重新编译程序,输入地址,程序将会探查出它是一个Gopher地址,并显示出第一个Gopher位置。 5.FINGER协议查询代码

FINGER协议的作用是提供一个网址的具体情况,它是Internet上最古老的协议之一。在一个FINGER服务器上,可以查询它的某一个用户或整个网址的情况。当然,这对网络的安全是不利的。为了安全起见,许多网络服务器不提供FINGER服务,但当它接收到FINGER查询请求时,仍然会返回一些有用信息。

在MFC和Win32API中,没有提供直接实现FINGER查询的函数,但可以使用变通的办法来实现它。所有的Internet链接都需要一个宿主名和端口号,所有的著名的服务都有其特定的端口号。例如,HTTP服务使用远程宿主机上的端口80,FTP服务使用端口21,Gopher服务使用端口70。对于FINGER服务来说,它使用端口79。FINGER是一种简单的协议,如果向远程宿主机的端口79发送字符串,FINGER服务器在端口79侦听到后,将会发送出一个FINGER回答。如果发送的字符串仅仅包含\r\n,服务器通常会把本服务器上所有用户的列表及相关信息(如用户真实姓名等)作为应答返回。因此,如果我们不使用缺省的端口70,而是使用端口79来建立Gopher链接,就能发出FINGER查询。

给CScanDlg类增加一个如下的成员函数voidTryFinger(CStringhost):

voidCScanDlg::TryFinger(CStringhost) { CInternetSessionsession; m_out+="正在链接finger地址"+host+"\r\n"; UpdateData(FALSE); CGopherConnection*connection=NULL; try { connection=session.GetGopherConnection(host,NULL,NULL,79); } catch(CInternetException*pEx) { connection=NULL; pEx->Delete( ); } if(connection) { m_out+="已建立链接.\r\n"; CGopherLocatorlocator=connection->CreateLocator (NULL,NULL,GOPHER_TYPE_TEXT_FILE);CGopherFile*file=NULL;try{file=connection->OpenFile(locator);}catch(CInternetException*pEx){file=NULL;pEx->Delete( );}if(file){CStringline;for(inti=0;i<20&&file->ReadString(line);i++){m_out+=line+"\r\n";}file->Close( );deletefile;}else{m_out+="finger查询失败。\r\n";}connection->Close( );deleteconnection;}else{m_out+="本地址没有发现finger主机。\r\n";}m_out+="------------------------------------------------------\r\n";UpdateData(FALSE);}

本函数中,语句connection=session.GetGopherConnection(host,NULL,NULL,79)用于建立FINGER链接。随后,创建一个文本文件类型的Gopher位置,用来操作服务器返回的信息,该语句为CGopherLocatorlocator=connection->CreateLocator(NULL,NULL,GOPHER_TYPE_TEXT_FILE)。使用该Gopher位置打开文件并使用一个for循环来读出该文件的头20行,随后将它显示出来。 现在,给IDC_BUTTON_FINGER按钮链接单击消息的处理函数,并编写如下代码: voidCScanDlg::OnButtonFinger( ) { UpdateData(TRUE); m_out=""; UpdateData(FALSE); TryFinger(m_host); }

编译程序,输入地址,程序将会返回该服务器的E-mail地址,从返回的信息可知,出于安全考虑,该服务器的其他FINGER服务已被取消了。如果你输入的网址没有提供FINGER服务,程序将有较长一段时间没有反应,最后弹出一个消息框,通知用户链接出现超时错误。 6.WHOIS协议查询代码 还有一个协议也能提供网址的相关信息,它也是一种古老的协议,MFC并不直接支持它,这就是WHOIS协议。在整个Internet上,只有少数服务器提供WHOIS服务。WHOIS服务建立了Internet上的域名数据库。如果对某个域名进行WHOIS查询,服务器将会返回拥有该域的机构或个人的实际姓名、地址、电话号码等信息。国际上的域名注册机构拥有WHOIS服务器,例如,域名结尾为.com的域都在一个称为InterNIC的机构中注册,该机构拥有一个WHOIS服务器,称为。WHOIS协议和FINGER协议一样,是一种简单的协议,它使用端口43。如果向WHOIS服务器的端口43发送包含域名的字符串,则WHOIS服务器将会返回该域拥有者的信息。给CScanDlg类增加一个如下的成员函数voidTryWhois(CStringhost):voidCScanDlg::TryWhois(CStringhost){CInternetSessionsession;m_out+="正在链接Whois地址"+host+"\r\n";UpdateData(FALSE);CGopherConnection*connection=NULL;try{connection=session.GetGopherConnection("",NULL,NULL,43);}catch(CInternetException*pEx){connection=NULL;pEx->Delete( );}if(connection){m_out+="已建立链接。\r\n";CGopherLocatorlocator=connection->CreateLocator(NULL,host,GOPHER_TYPE_TEXT_FILE); CGopherFile*file=NULL; try { file=connection->OpenFile(locator); } catch(CInternetException*pEx) { file=NULL; pEx->Delete( ); } if(file) { CStringline; for(inti=0;i<20&&file->ReadString(line);i++) { m_out+=line+"\r\n"; } file->Close( ); deletefile; } else { m_out+="Whois查询失败。\r\n"; } connection->Close( ); deleteconnection; } else { m_out+="Whois查询失败。\r\n"; } m_out+="------------------------------------------------------\r\n"; UpdateData(FALSE); }

在本函数中,语句connection=session.GetGopherConnection("",NULL,NULL,43)使程序链接到了WHOIS服务器。随后,建立一个Gopher位置来查询用户输入的域:CGopherLocatorlocator=connection->CreateLocator(NULL,host,GOPHER_TYPE_TEXT_FILE),由于链接的域为,所以本函数只能查询结尾为.com的域。读者可以对本段程序进行扩充,链接到其他相关的WHOIS服务器,以查询其他类型的域。

现在,给IDC_BUTTON_WHOIS按钮链接单击消息的处理函数,并编写如下代码:

voidCScanDlg::OnButtonWhois( ) { UpdateData(TRUE); m_out=""; UpdateData(FALSE); TryWhois(m_host); }

重新编译程序,现在,我们可以对结尾为.com的域进行WHOIS查询了。10.4原始套接口(SOCK_RAW)程序设计实例 10.4.1原始套接口简介 要在程序中使用原始套接口,首先也要创建一个原始套接口。原始套接口只能使用SOCK_RAW套接口类型来创建。在创建SOCK_RAW套接口时,socket( )函数的第三个参数,即协议字段通常使用IPPROTO_ICMP或IPPROTO_IGMP;而在创建SOCK_STREAM和SOCK_DGRAM类型的套接口时,socket( )函数的第三个参数通常为0。创建一个SOCK_RAW套接口的典型代码如下: …… 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) { //输出创建套接口错误的提示信息

} ……

以上代码表示创建SOCK_RAW套接口时指定的协议为ICMP,即在IP协议的数据区中承载的是ICMP协议。 在SOCK_RAW套接口创建完成后,就可以进行数据的发送和接收了,只是IP数据报头会包含在收到的数据中。在套接口使用完毕后,同样要关闭套接口。10.4.2原始套接口程序设计实例

具体Ping程序的实现步骤如下:

(1)定义IP和ICMP协议头。

(2)定义回送请求和应答数据包的结构。

(3)在主程序中初始化Winsock协议栈(使用WSAStartup( )函数)。

(4)在主程序中调用Ping函数。

(5)释放Winsock协议栈。 Ping函数的主要功能如下:

(1)创建一个原始套接口。

(2)根据主机名查询主机地址。

(3)输出Ping程序要测试的目标主机地址。

(4)控制Ping程序进行4次发送与接收的测试(发送和接收分别使用SendEcho-Request( )和WaitForEchoReply( )函数来完成)。

(5)计算传输时间,并输出提示信息。

(6)关闭原始套接口。 SendEchoRequest( )函数的功能是:填写回送请求信息,计算校验和,使用sendto发送回送请求数据。

WaitForEchoReply( )和其他几个函数的定义比较简单。可以查看程序中的说明。 下面是在头文件ping.h中定义的IP和ICMP协议头的结构。

/////////////////////////////////////////////////////////////////////////////////////////////////// //ping.h //在该头文件中定义了IP和ICMP协议头的结构

#pragmapack(1) //

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论