班级:软件工程二班 姓名:马静 学号:1505060226
一、实验内容
在windows环境下通过编写ping程序,了解ping的功能。
二、实验过程
要实现ping程序需要实现以下步骤:
(1)创建协议类型为IPPRPTO_ICMP的原始套接字,设置套接字属性。
(2)创建并初始化ICMP封包。
(3)调用sendto函数向远程主机发送ICMP请求。
(4)调用recfrom函数接受ICMP响应。
实现以上步骤的源代码如下:
include "stdafx.h"
include "Ping.h"
include "PingDlg.h"
ifdef _DEBUG
define new DEBUG_NEW
undef THIS_FILE
static char THIS_FILE[] = FILE;
endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CPingDlg dialog
CPingDlg::CPingDlg(CWnd* pParent /=NULL/)
: CDialog(CPingDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CPingDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CPingDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CPingDlg)
DDX_Control(pDX, IDC_ReList, m_ReList);
DDX_Control(pDX, IDC_IP, m_Ip);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CPingDlg, CDialog)
//{{AFX_MSG_MAP(CPingDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_Ping, OnPing)
ON_BN_CLICKED(IDC_Clean, OnClean)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CPingDlg message handlers
BOOL CPingDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//GetDlgItem(IDC_Clean)->EnableWindow(false);//清除按钮不能用
m_Ip.SetAddress (172,31,8,232); //初始显示的Ip地址
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
InitSocket();//调用
return TRUE; // return TRUE unless you set the focus to a control
}
void CPingDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CPingDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CPingDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
BOOL CPingDlg::InitSocket()
{ S=socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if(S==INVALID_SOCKET)
{ MessageBox("创建套接字失败!");
return FALSE;
}
SOCKADDR_IN addrSock; //自己的地址
addrSock.sin_family=AF_INET;
addrSock.sin_port=htons(5555);
addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
if(bind(S,(SOCKADDR*)&addrSock,sizeof(SOCKADDR))==SOCKET_ERROR)
{ closesocket(S);
MessageBox("绑定失败!");
return FALSE;
}
return TRUE;
}
void CPingDlg::OnPing()
{
// TODO: Add your control notification handler code here
SetTimeout(S, 1000);
unsigned char B[4];
m_Ip.GetAddress(B[0],B[1],B[2],B[3]); //获得编辑框中输入的IP地址
struct in_addr IP;
memcpy(&IP.S_un.S_addr,B,4);
//存放 到 目的端地址 结构体 中
SOCKADDR_IN dest;
dest.sin_family=AF_INET;
dest.sin_port=htons(0);
dest.sin_addr.S_un.S_addr=IP.S_un.S_addr;
//-----------------------------------------------------------------------------------------------
ICMPHeader pIcmp;
char buff[sizeof(ICMPHeader) +5]; //5:要发送的信息占5B
pIcmp=(ICMPHeader)buff;
// 开始发送和接收ICMP封包
USHORT nSeq = 0;
char recvBuf[1024];
SOCKADDR_IN from;
int nLen = sizeof(from);
for(int nCount = 0;nCount< 4;nCount++)
{ // 填写要发送的ICMP封包数据
pIcmp->icmp_type = 8;
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (unsigned short)GetCurrentProcessId();
pIcmp->icmp_checksum = 0;
pIcmp->icmp_sequence = nSeq++;
pIcmp->icmp_timestamp = GetTickCount();
memset(&buff[sizeof(ICMPHeader)], '0', 5);
pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMPHeader)+5 );
//发送
int nRet = sendto(S, buff,sizeof(ICMPHeader)+5, 0, (SOCKADDR )&dest, sizeof(dest));
if(nRet == SOCKET_ERROR)
{ MessageBox("调用sendto()失败");
break;
}
else
{ //接收
nRet = recvfrom(S, recvBuf, 1024, 0, (sockaddr)&from, &nLen);
if(nRet == SOCKET_ERROR)
{ if(WSAGetLastError() == 10060)
{ m_ReList.AddString("Request timed out");
continue;
}
MessageBox("调用recvfrom()失败");
break;
}
else
{ ICMPHeader pRecvIcmp;
pRecvIcmp= (ICMPHeader)(recvBuf + 20);
// 解析接收到的ICMP封包
if(nRet < sizeof(IPHeader) + sizeof(ICMPHeader)+5)
{ m_ReList.AddString("数据在路上部分丢失");
break;
}
else if(pRecvIcmp->icmp_type != 0)
{ m_ReList.AddString("不是我需要的回应");
break;
}
else if(pRecvIcmp->icmp_type== 3)
{ m_ReList.AddString("目的主机不可达");
break;
}
else if(pRecvIcmp->icmp_id != GetCurrentProcessId())
{ m_ReList.AddString("不是发给我的");
break;
}
else
m_ReList.AddString("收到正确回应");
}
}
}
m_ReList.AddString("结束Ping");
GetDlgItem(IDC_Clean)->EnableWindow(true);
}
BOOL CPingDlg::SetTimeout(SOCKET S, int nTime)
{ int ret = setsockopt(S, SOL_SOCKET,SO_RCVTIMEO, (char*)&nTime, sizeof(nTime));
if(ret!=0)
{ char cChar[50];
sprintf(cChar,"错误代码=%d\n",GetLastError());
MessageBox(cChar,"调用setsockopt()失败,ret=");
//MessageBox("调用setsockopt()失败");
exit(0);
}
else return ret;
}
USHORT CPingDlg::checksum(USHORT *buff, int size)
{
u_char buf1=(u_char)buff;
register int cksum = 0;
while(size>1)
{
cksum += *buff++;
size -=2;
}
if(size==1)
cksum += (UCHAR)buff;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return ((USHORT)~cksum);
}
void CPingDlg::OnClean()
{
// TODO: Add your control notification handler code here
m_Ip.ClearAddress();
m_ReList.ResetContent();
}
整个过程演示:
三、心得体会
通过仔细阅读程序代码,查找相关资料,我大致了解了程序的基本过程。在此次作业中有很多知识是之前没有学过的,所以找了很多东西,也学习了很多。在做作业的时候也发现了很多问题,代码不会写,自己写的代码太简单,自己的思路太简单,程序出错不想改等等。所以在今后的学习中还是要认真。