资源描述
一:PING程序设计 2
1具体设计任务 2
1.1 试验目标 2
1.2 试验内容和要求 2
1.2.1 RAW模式SOCKET编程 2
1.2.2 具体内容 3
2.3 试验关键仪器设备和材料 3
3 基础思绪及设计方案 4
结构ICMP请求回应包,发送,接收回应,若超时,打印超市 4
3.1 原理框图 4
3.2 关键程序源代码 6
4 试验结果 12
5 碰到问题及处理方法 13
6试验心得 13
7 思索题 13
二.文件传输协议简单设计和实现 16
1 具体设计任务 16
1.1试验目标 16
1.2试验内容和要求 16
1.2.1试验内容 16
1.2.2具体要求 16
1.3试验关键仪器设备和材料 17
2 文件传输协议实现原理 17
2.1基础思绪及设计方案 17
2.2 相关理论 17
2.3程序实现方法和方案 18
2.3.1 FTP服务器步骤图 18
2.3.2 FTP用户端步骤图 20
3 关键代码及其功效 21
3.1 服务端代码: 21
3.2用户端主函数: 28
4 结果分析 32
5思索题 34
6.问题及处理方法: 36
问题:数据包丢失。 36
原因:以后数据把先来数据淹没了。 36
7.心得体会 36
一:PING程序设计
摘要:
该程序关键为ping程序,经过ping 程序来探测主机到主机之间是否可通信,假如不能ping到某台主机,表明不能和这台主机建立连接。
ping 使用是ICMP协议,关键步骤为:它发送ICMP回送请求消息给目标主机,ICMP协议要求,目标主机必需返回ICMP回送应答消息给源主机,假如源主机在一定时间内收到应答,则认为主机可达。若传送IP数据包发生错误--比如主机不可达,路由不可达等等,ICMP协议将会把错误信息封包,然后传送回给主机。给主机一个处理错误机会,这也就是为何说建立在IP层以上协议是可能做到安全原因。ICMP数据包由8bit错误类型和8bit代码和16bit校验和组成。而前 16bit就组成了ICMP所要传输信息。
关键词:ping ICMP协议 IP
1具体设计任务
1.1 试验目标
PING程序是我们使用比较多用于测试网络连通性程序。PING程序基于ICMP,使用ICMP回送请求和回送应答来工作。由计算机网络课程知道,ICMP是基于IP一个协议,ICMP包经过IP封装以后传输。
课程设计中选择PING程序设计,其目标是期望同学们经过PING程序设计,能初步掌握TCP/IP网络协议基础实现方法,对网络实现机制有深入认识。
1.2 试验内容和要求
1.2.1 RAW模式SOCKET编程
PING程序是面向用户应用程序,该程序使用ICMP封装机制,经过IP协议来工作。为了实现直接对IP和ICMP包进行操作,试验中使用RAW模式SOCKET编程。
熟悉SOCKET编程,包含基础系统调用如SOCKET、BIND等;
1.2.2 具体内容
1)定义数据结构
需要定义好IP数据报、ICMP包等相关数据结构;
2)程序实现
在WINDOWS环境下实现PING程序;
3)程序要求
在命令提醒符下输入:
PING ΧΧΧ.ΧΧΧ.ΧΧΧ.ΧΧΧ
其中ΧΧΧ为目标主机IP地址,不要求支持域名,对是否带有开关变量也不做要求。不带开关变量时,要求返回4次响应。
返回信息格式:
REPLY FROM ΧΧΧ.ΧΧΧ.ΧΧΧ.ΧΧΧ
或
REQUEST TimeOut (无法PING通情况)。
2.3 试验关键仪器设备和材料
联网计算机
3 基础思绪及设计方案
结构ICMP请求回应包,发送,接收回应,若超时,打印超市
3.1 原理框图
开始
输入IP地址
套街字创建并连接
ping()函数
SendEchoRequest()
RecvEchoReply()
检测目标主机,
,创建ICMP包,计算校验和,发送回应请求
接收应答回复并进行解析
显示结果
找到主机
设置目标IP地址
是
否
释放socket
WSACleanup()
3.2 关键程序源代码
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#pragma comment(lib, "ws2_32.lib")//导入库文件
#define ICMP_ECHOREPLY 0 //ICMP回应回复
#define ICMP_ECHOREQ 8 //ICMP回应请求
#define REQ_DATASIZE 32 //请求数据报大小
#include <iostream>
using namespace std;
//定义IP首部格式
typedef struct _IPHeader
{
u_char VIHL; //版本和首部长度
u_char ToS; //服务类型
u_short TotalLen; //总长度
u_short ID; //标识号
u_short Frag_Flags; //片偏移量
u_char TTL; //生存时间
u_char Protocol; //协议
u_short Checksum; //首部校验和
struct in_addr SrcIP; //源IP地址
struct in_addr DestIP; //目标地址
}IPHDR, *PIPHDR;
//定义ICMP首部格式
typedef struct _ICMPHeader
{
u_char Type; //类型
u_char Code; //代码
u_short Checksum; //首部校验和
u_short ID; //标识
u_short Seq; //序列号
char Data; //数据
}ICMPHDR, *PICMPHDR;
//定义ICMP回应请求
typedef struct _ECHOREQUEST
{
ICMPHDR icmpHdr;
DWORD dwTime;
char cData[REQ_DATASIZE];
}ECHOREQUEST, *PECHOREQUEST;
//定义ICMP回应回复
typedef struct _ECHOREPLY
{
IPHDR ipHdr;
ECHOREQUEST echoRequest;
char cFiller[256];
}ECHOREPLY, *PECHOREPLY;
/**********************************************************************************/
//计算校验和
u_short checksum(u_short *buffer, int len)
{
register int nleft = len;
register u_short *w = buffer;
register u_short answer;
register int sum = 0;
//使用32位累加器,进行16位反馈计算
while ( nleft > 1 )
{
sum += *w++;
nleft -= 2;
}
//补全奇数位
if ( nleft == 1 )
{
u_short u = 0;
*(u_char *)(&u) = *(u_char*)w;
sum += u;
}
//将反馈16位从高位移到低位
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
/**********************************************************************************************/
//发送回应请求函数
int SendEchoRequest(SOCKET s, struct sockaddr_in *lpstToAddr)
{
static ECHOREQUEST echoReq;
static nId = 1;
static nSeq = 1;
int nRet;
//填充回应请求消息
echoReq.icmpHdr.Type = ICMP_ECHOREQ;
echoReq.icmpHdr.Code = 0;
echoReq.icmpHdr.Checksum = 0;
echoReq.icmpHdr.ID = nId++;
echoReq.icmpHdr.Seq = nSeq++;
//填充要发送数据
for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
{
echoReq.cData[nRet] = '1' + nRet;
}
//存放发送时间
echoReq.dwTime = GetTickCount();
//计算回应请求校验和
echoReq.icmpHdr.Checksum = checksum((u_short*)&echoReq, sizeof(ECHOREQUEST));
//发送回应请求
nRet = sendto(s,(LPSTR)&echoReq,sizeof(ECHOREQUEST),
0,(struct sockaddr*)lpstToAddr,sizeof(SOCKADDR_IN));
if (nRet == SOCKET_ERROR)
{
printf("send to() error:%d\n", WSAGetLastError());
}
return (nRet);
}
/*******************************************************************************/
//接收应答回复并进行解析
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
{
ECHOREPLY echoReply;
int nRet;
int nAddrLen = sizeof(struct sockaddr_in);
//接收应答回复
nRet = recvfrom(s,(LPSTR)&echoReply,sizeof(ECHOREPLY),
0,(LPSOCKADDR)lpsaFrom,&nAddrLen);
//检验接收结果
if (nRet == SOCKET_ERROR)
{
printf("recvfrom() error:%d\n",WSAGetLastError());
}
//统计返回TTL
*pTTL = echoReply.ipHdr.TTL;
//返回应答时间
return(echoReply.echoRequest.dwTime);
}
/********************************************************************************/
//等候回应回复,使用select模型
int WaitForEchoReply(SOCKET s)
{
struct timeval timeout;
fd_set readfds;
readfds.fd_count = 1;
readfds.fd_array[0] = s;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
return(select(1, &readfds, NULL, NULL, &timeout));
}
/******************************************************************************/
//PING功效实现
void Ping(char *pstrHost,bool logic)
{
SOCKET rawSocket;
LPHOSTENT lpHost;
struct sockaddr_in destIP;
struct sockaddr_in srcIP;
DWORD dwTimeSent;
DWORD dwElapsed;
u_char cTTL;
int nLoop,k=4;
int nRet,minimum=100000,maximum=0,average=0;
int sent=4,reveived=0,lost=0;
//创建原始套接字,ICMP类型
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (rawSocket == SOCKET_ERROR)
{
printf("socket() error:%d\n", WSAGetLastError());
return;
}
//检测目标主机
lpHost = gethostbyname(pstrHost);
if (lpHost==NULL)
{
printf("Host not found:%s\n", pstrHost);
return;
}
//设置目标机地址
destIP.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr)); //设置目标IP
destIP.sin_family = AF_INET; //地址规格
destIP.sin_port = 0;
//提醒开始进行PING
printf("\nPinging %s [%s] with %d bytes of data:\n",pstrHost,inet_ntoa(destIP.sin_addr),REQ_DATASIZE);
//提议数次PING测试
for (nLoop=0; nLoop<k; nLoop++)
{if (logic)k=k+1;
//发送ICMP回应请求
SendEchoRequest(rawSocket, &destIP);
//等候回复数据
nRet = WaitForEchoReply(rawSocket);
if (nRet == SOCKET_ERROR)
{
printf("select() error:%d\n", WSAGetLastError());
break;
}
if (!nRet)
{
lost++;
printf("\nRequest time out.");
continue;
}
//接收回复
dwTimeSent = RecvEchoReply(rawSocket, &srcIP, &cTTL);
reveived++;
//计算花费时间
dwElapsed = GetTickCount() - dwTimeSent;
if(dwElapsed>maximum)maximum=dwElapsed;
if(dwElapsed<minimum)minimum=dwElapsed;
average+=dwElapsed;
printf("\nReply from %s: bytes = %d time = %ldms TTL = %d",
inet_ntoa(srcIP.sin_addr),REQ_DATASIZE,dwElapsed,cTTL);
}
printf("\n\n");
printf("Ping statistics for %s:\n",inet_ntoa(srcIP.sin_addr));
printf(" Packets: Sent = %d, Received = %d, Lost = %d (%.f%% loss),\n",
sent,reveived,lost,(float)(lost*1.0/sent)*100);
if(lost==0)
{printf("Approximate round trip times in milli-seconds:\n");
printf(" Minimum = %dms, Maximum = %dms, Average = %dms\n",minimum,maximum,average/sent);}
printf("\n\n");
nRet = closesocket(rawSocket);
if (nRet == SOCKET_ERROR)
{
printf("closesocket() error:%d\n", WSAGetLastError());
}
}
//主程序
void main()
{
printf("Welcome to the Ping Test\n");
while(1)
{
WSADATA wsd;//检测输入参数
//初始化Winsock
if (WSAStartup(MAKEWORD(1, 1), &wsd) != 0)
{printf("加载 Winsock 失败!\n");}
char opt1[100];
char *ptr=opt1;
bool log=false;
printf("Ping ");
cin.getline(opt1,100,'\n');
if((opt1[0]=='-')&&(opt1[1]=='t')(opt1[2]==’’))
{log=true;ptr=opt1+3;}
//开始PING
Ping(ptr,log);
//程序释放 Winsock 资源
WSACleanup();
}
}
4 试验结果
直接PING IP地址,且PING通情况下,返回四次响应。以下图:
PING 域名
PING域名,若有丢包,以下图所表示:
PING(域名前加-t,显示循环):
5 碰到问题及处理方法
调试时发觉发送时间比预料中长很多,经检验原来是函数中符号“}”放置位置过后。
6试验心得
经过这次课程设计,加深了对网络协议了解,一部机器想联网,必需遵守共同通信约定(协议),才能正常同其它机器进行通信。
7 思索题
1. 本题目只要求实现PING部分简单功效,在Windows命令行模式下,输入“Ping”回车,查看PING全部功效,考虑怎样实现这些功效。
答:首先,Ping命令会构建一个固定格式ICMP请求数据包,然后由ICMP协议将这个数据包连同地址“192.168.1.2”一起交给IP层协议(和ICMP一样,实际上是一组后台运行进程),IP层协议将以地址“192.168.1.2”作为目标地址,本机IP地址作为源地址,加上部分其它控制信息,构建一个IP数据包,并在一个映射表中查找出IP地址192.168.1.2所对应物理地址(也叫MAC地址,熟悉网卡配置好友不会陌生,这是数据链路层协议构建数据链路层传输单元——帧所必需),一并交给数据链路层。后者构建一个数据帧,目标地址是IP层传过来物理地址,源地址则是本机物理地址,还要附加上部分控制信息,依据以太网介质访问规则,将它们传送出去。
2. 假如一台主机能ping通自己但网络不通,可能是什么原因?
答:(1)Windows服务器网络服务功效还没开启(2)计算机TCP/IP协议没有和网卡有效绑定;(3)可能计算机网卡安装不正确,也可能是没连通;
3. 考虑Netstat、Traceroute、ipconfig等网络测试应用程序工作原理和使用。
答:Netstat工作原理及使用:(1)显示当地或和之相连远程机器连接状态,包含TCP、IP、UDP、ICMP协议使用情况,了解当地机开放端口情况.(2)检验网络接口是否已正确安装,假如在用netstat这个命令后仍不能显示一些网络接口信息,则说明这个网络接口没有正确连接,需要重新查找原因。(3)经过加入“-r”参数查询和本机相连路由器地址分配情况。(4)检验部分常见木马等黑客程序,因为任何黑客程序全部需要经过打开一个端口来达成和其服务器进行通信目标。
Traceroute程序设计是利用ICMP及IP headerTTL(Time To Live)。首先,traceroute送出一个TTL是1IP 数据报到目标地,当路径上第一个路由器(router)收到这个数据报时,它将TTL减1。此时,TTL变为0了,所以该路由器会将此数据报丢掉,并送回一个「ICMP time exceeded」消息,traceroute 收到这个消息后,便知道这个路由器存在于这个路径上,接着traceroute 再送出另一个TTL是2 datagram,发觉第2 个路由器...... traceroute 每次将送出数据报TTL 加1来发觉另一个路由器,这个反复动作一直连续到某个数据报抵达目标地。当数据报抵达目标地后,该主机并不会送回ICMP time exceeded消息,因为它已是目标地了。
Ipconfig工作原理及使用: (1)查找目标主机IP地址及其它相关TCP/IP协议信息。(2)当用户网络中设置是DHCP(动态IP地址配置协议)时,利用Ipconfig/winipcfg能够让用户很方便地了解到所用IPconfig/winipcfg机IP地址实际配置情况。因为它有一个“/all”这个参数,所以它可侦查到本机上全部网络适配IP地址分配情况,比ping命令更为具体。假如我们一台IP地址为192.168.2.199上运行”Ipconfig”命令后,窗口中显示了主机名、DNS服务器、节点类型和主机相关信息如网卡类型、MAC地址、IP地址、子网掩码和默认网关等。其中网络适配器MAC地址在检测网络错误时很有用。 配置不正确IP地址或子网掩码是接口配置常见故障。其中配置不正确IP地址有两种情情况: (1)网号部分不正确。此时实施每一条Ipconfig命令全部会显示“no answer”,这么,实施该命令后错误IP地址就能被发觉,修改即可。(2)主机部分不正确。如和另一主机配置地址相同而引发冲突。这种故障是当两台主机同时配置相同IP地址时出现间歇性通信问题。更换IP地址中主机号部分,该问题即能排除。
二.文件传输协议简单设计和实现
摘要:
初始化套接字,使用TCP协议实现用户端对服务器硬盘文件访问,包含上传和下载。
关键词:上传 下载 TCP 套接字
1 具体设计任务
1.1试验目标
文件传送是多种计算机网络全部实现基础功效,文件传送协议是一个最基础应用层协议根据用户/服务器模式进行工作,提供交互式访问,是INTERNET使用最广泛协议之一。
本试验目标是,学会利用已经有网络环境设计并实现简单应用层协议,掌握TCP/IP 网络应用程序基础设计方法和实现技巧。
1.2试验内容和要求
1.2.1试验内容
我们计算机网络试验环境建立在TCP/IP 网络体系结构之上。各计算机除了安装TCP/IP 软件外,还安装了TCP/IP 开发系统。试验室各计算机含有Windows环境中套接字socket 编程接口功效,可为用户提供全网范围进程通信功效。本试验要求学生利用这些功效,设计和实现一个简单文件传送协议。
1.2.2具体要求
用socket 编程接口编写两个程序,分别为用户程序(client.c)和服务器程序(server.c),该程序应能实现下述命令功效:
get:取远方一个文件
put:传给远方一个文件
pwd:显示远主目前目录
dir:列出远方目前目录
cd :改变远方目前目录
? :显示你提供命令
quit :退出返回
这此命令具体工作方法(指给出结果形式)能够参考FTP 对应命令,有余力同学能够多实现多个命令。
最终,写出试验汇报。
1.3试验关键仪器设备和材料
联网计算机
2 文件传输协议实现原理
2.1基础思绪及设计方案
FTP基础思绪为,用户端向服务器提交查看/下载文件请求,服务器则一直作检验请求活动,当检测到有请求时,则响应请求。具体功效以下:
服务器功效:
1)浏览当地目录、自动获取IP,将当地目录发送至用户端(用户端能够在对应位置显示)2)上传某文件至用户端
用户端功效
1)查看当地目录(将要下载文件存入到目录中),显示从服务器取得目录
2)下载文件
2.2 相关理论
连接套接字socket,用TCP相关功效侦听,socket发送(send),socket接收(receive)。
2.3程序实现方法和方案
2.3.1 FTP服务器步骤图
开始
初始化(路径、端口)
获取IP、端口
将文件上传
侦听
依据用户端操作命令传送目录
初始化完成否?
能开启?
是否有连接?
请求下载?
输出提醒信息
其它请求?
结束
输犯错误信息
否
否
否
否
否
是
是
是
是
2.3.2 FTP用户端步骤图
3 关键代码及其功效
3.1 服务端代码:
#include <Winsock2.h>
#include <stdio.h>
#include<iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
bool m_connected=0;
int m_sendingFile=0;
char m_currentPath[1500]={'\0'};
FILE *m_file=0;
sockaddr_in addr_Client;
SOCKET sock_Client;
void Analysis(char *buf,int );
DWORD WINAPI RecvThread(
LPVOID lpParameter // thread data
)
{
printf("%s已连接\n",inet_ntoa(addr_Client.sin_addr));
SOCKET sock=(SOCKET)lpParameter;
char buf[1500];
for(;1;)
{
int len=recv(sock,buf,1500,0);
if(len==-1)
{
closesocket(sock);
m_connected=0;
if(m_file){fclose(m_file);m_file=0;}
m_sendingFile=0;
printf("和 %s 连接断开\n",inet_ntoa(addr_Client.sin_addr));
return 0;
}
Analysis(buf,len);
}
return 1;
}
DWORD WINAPI ListenThread(
LPVOID lpParameter // thread data
)
{
SOCKET LISTEN=(SOCKET)lpParameter;
for(;1;)
{
if(!m_connected)
{
int len=sizeof(sockaddr);
::GetCurrentDirectory(1500,m_currentPath);//目前路径
//strcpy(m_currentPath,"Z:");
sock_Client=accept(LISTEN,(sockaddr*)&addr_Client,&len);
CreateThread(0,0,RecvThread,(void*)sock_Client,0,0);
}
else Sleep();
}
return 1;
}
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(2438);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
listen(sockSrv,5);
CreateThread(0,0,ListenThread,(void*)sockSrv,0,0);
for(;1;)
{
Sleep(100);
}
}
#include<io.h>
void OnDir();
void OnPwd();
void OnCd(char *buf);
void OnPut(char *buf);
/*DWORD WINAPI OnGet(
LPVOID lpParameter // thread data
);*/
void OnGet(char *);
void Analysis(char* buf,int len)
{
int *comand=(int *)buf;
switch(*comand)
{
case 1:OnGet(buf);break;
case 2:OnPut(buf);break;
case 3:OnPwd();break;
case 4:
//返回 显示命令 + 目录
OnDir();
break;
case 5:
OnCd(buf);
break;
// CreateThread(0,0,OnGet,(void*)buf,0,0);
//返回改变目录后目录
case 11://请求文件数据
if(!m_sendingFile)break;
char sendBuf[1500];
*((int*)sendBuf)=11;
len=fread((void*)(sendBuf+4),1,1496,m_file);
//Sleep(10);
send(sock_Client,sendBuf,len+4,0);
if(len==0)
{
fclose(m_file);
m_file=0;
*((int *)sendBuf)=8;
strcpy(sendBuf+4,"传送成功");
send(sock_Client,sendBuf,strlen(sendBuf+4)+5,0);
m_sendingFile=0;
printf("send sucess\n");
}
break;
case 22:
if(!m_sendingFile)break;
fwrite((void*)(buf+4),1,len-4,m_file);
//Sleep(10);
if(len==4)
{
fclose(m_file);
m_file=0;
*((int *)sendBuf)=8;
strcpy(sendBuf+4,"传送成功");
send(sock_Client,sendBuf,strlen(sendBuf+4)+5,0);
m_sendingFile=0;
printf("send sucess\n");
}
len=22;
send(sock_Client,(char*)&len,10,0);
break;
}
}
void OnPwd()
{
char sendBuf[1000];
int *type=(int *)sendBuf ; *type=8;//显示
char *s=sendBuf+4;
strcpy(s,m_currentPath);
send(sock_Client,sendBuf,strlen(m_currentPath)+5,0);
}
void OnDir()
{
char sendBuf[1500];
char filename[200];
strcpy(filename,m_currentPath);
strcat(filename,"\\**");
long hFile;
_finddata_t fileinfo;
if ((hFile=_findfirst(filename, &fileinfo)) != -1)
{
int *flat=(int *)sendBuf; *flat=8;//显示 8
char *filename2=sendBuf+4;
filename2[0]='\0';
filename2[1]='\0';
do
{
int foder=0;
if (fileinfo.attrib & _A_SUBDIR)
foder=1;
if(strlen(&sendBuf[4])+strlen(fileinfo.name)+strlen("(文件夹)\n")>1495)
{
send(sock_Client,sendBuf,4+strlen(&sendBuf[4])+1,0);
memset(sendBuf,0,1500);
filename2=sendBuf+4;
*flat=8;
filename2[0
展开阅读全文