本课程要求学生在"计算机网络原理"理论学习基础上,验证和加深对计算机网络概念的理解。通过本课程的实践,培养学生独立思考、综合分析和动手实践的能力。
通过本课程的学习,达到如下目标:
理解计算机网络体系结构和工作原理,掌握常用的网络命令,能够对命令的功能进行解释,分析命令执行结果,得到合理有效结论。
掌握Packet Tracer软件的操作方法,能够使用该软件模拟组网、配置交换机、路由器,能够按照实验方案实施仿真实验,采集和整理数据。
具备网络编程能力,能够设计抓包程序获取数据包,结合相关协议对数据包各个字段的含义进行分析、处理和解释,获取有效结论。
常用网络命令的工作原理及协议
ARP的原理
ICMP协议及原理
telnet远程登录的原理
IP报文格式及路由转发
以太网CSMA/CD协议原理
透明网桥、以太网交换机
虚拟局域网VLAN
路由器的作用、静态路由、动态路由
IP报文格式及IP协议
Socket网络编程
ipconfig命令显示当前所有的TCP/IP配置值、刷新动态主机配置协议(DHCP)和域名系统(DNS)设置。具体用法如下:
ipconfig [/allcompartments] [/? | /all |
/renew [adapter] | /release [adapter] |
/renew6 [adapter] | /release6 [adapter] |
/flushdns | /displaydns | /registerdns |
/showclassid adapter |
/setclassid adapter [classid] |
/showclassid6 adapter |
/setclassid6 adapter [classid] ]
下面选取ipconfig常用参数进行说明:
功能:显示所有适配器的完整TCP/IP配置信息。
功能:更新所有适配器(不带adapter参数),或特定适配器(带有adapter参数)的DHCP配置。
运行截图:
运行结果解释:因实验过程中计算机只有无线局域网连接,因此只有在无线局域网适配器上执行操作。
功能:发送DHCPRELEASE消息到DHCP服务器,以释放所有适配器(不带adapter参数)或特定适配器(带有adapter参数)的当前DHCP配置并丢弃IP地址配置。
运行结果解释:跟/renew参数对比,可知ipv4地址、子网掩码、默认网关都被释放。(release命令只释放ipv4适配器,因此ipv6地址还存在)
因为释放IP地址,因此在运行/release参数后计算机出现无法上网的情况,重新运行/renew参数后恢复正常。
ping命令可以被用来检查网络是否连通。具体用法如下:
ping [-t] [-a] [-n count] [-l size] [-f] [-i TTL] [-v TOS]
[-r count] [-s count] [[-j host-list] | [-k host-list]]
[-w timeout] [-R] [-S srcaddr] [-c compartment] [-p]
[-4] [-6] target\_name
下面选取ping命令常用参数进行说明:
运行结果解释:因中国大陆无法使用Google和Wikipedia,因此ping请求找不到主机。
功能:对指定的计算机一直进行ping操作,直到从键盘按Ctrl+C组合键中断为止。
功能:要发送的回显请求数。(默认四条)
运行结果:
运行结果解释:使用两次ping -n,第一次为10条,第二次为2条。结果如上。
netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。具体用大如下:
NETSTAT [-a] [-b] [-e] [-f] [-n] [-o] [-p proto] [-r] [-s] [-x] [-t] [interval]
下面选取netstat命令常用参数进行说明:
功能:显示一个所有的有效连接信息列表,包括已建立的连接(ESTABLISHED),也包括监听连接请求(LISTENING)的那些连接。
功能:用于显示关于以太网的统计数据,包括传送的数据报的总字节数、错误数、删除数、数据报的数量和广播的数量。
功能:以数字形式显示地址和端口号。
运行结果:
功能:照各个协议分别显示其统计数据
运行结果:
arp命令用于显示和修改地址解析协议(ARP)使用的"IP 到物理"地址转换表。具体用法如下:
ARP -s inet\_addr eth\_addr [if\_addr]
ARP -d inet\_addr [if\_addr]
ARP -a [inet\_addr] [-N if\_addr] [-v]
下面选取arp命令常用参数进行说明:
功能:用于查看高速缓存中的所有项目。
功能:删除ARP缓存列表。
运行结果解释:直接运行arp-d命令会跳出提示:"ARP 项添加失败: 请求的操作需要提升"。后了解到需要使用管理员身份运行cmd才能使用arp-d命令。结果如上。
tracert命令是跟踪路由路径的一个实用程序,用于确定数据报访问目标所经过的路径。具体用法如下:
tracert [-d] [-h maximum\_hops] [-j host-list] [-w timeout]
[-R] [-S srcaddr] [-4] [-6] target\_name
下面选取tracert命令常用参数进行说明:
功能:路由追踪,返回从源到目标的路由情况。
运行结果解释:因为服务商对某些结点设置进制追踪,因此结果中有相当多的结点请求超时。
功能:路由追踪,返回从源到目标的路由情况(与无参数相似),但不解析各路由器的名称,只返回路由器的IP地址。
运行结果:
运行结果解释:因为只返回IP地址,因此要比无参数运行速度快。
3)-h maximum_hops
功能:指定在搜索目标的路径中跃点的最大数,默认值为30。
⑥telnet命令
tnelnet命令常用于网页服务器的远程控制,可供使用者在本地主机运行远程主机上的工作,具体用法如下:
telnet [-a][-e escape char][-f log file][-l user][-t term][host [port]]
因为缺少真实可以被远程操作的主机,因此使用Cisco Packet Tracer软件模拟。使用PC机远程控制路由器:
如上图,初步熟悉交换机命令,并对交换机进行了初始化配置,建立了最为简单的拓扑结构。
拓扑结构如下:
拓扑结构解释如下:
使用PC0分别ping同组内的PC5与非同组PC4、PC2。以此来测试VLAN划分是否成功,结果如下:
路由器配置如下:
PC通过ping和telnet命令验证路由器配置是否正确:
配置路由器3:
配置路由器4(其中转发表配置在第二张图):
使用ping与tracert命令测试:
剩余三个路由器配置类似,在此不一一展示。
在PC0上使用tracert命令分别追踪到PC3与PC2的路径。结果如下:
通过分析追踪结果,可知RIP配置结果正确。
①运行结果(IP包头的各字段信息)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200122155428998.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDMxODE5Mg==,size_16,color_FFFFFF,t_70
②使用软件及操作系统
编程软件:Visual Studio 2017
编程语言:c++
操作系统:Windows 10 家庭中文版
程序中需要使用到Ws2_32.dll库,以及socket编程中的旧函数,在vs中对项目属性设置如下:
③程序设计思想
通过socket编程捕获IP包,然后通过二进制计算解析捕获的IP包。程序中使用函数需要管理员身份运行才可使用,因此需要在编写完成后找到.exe文件运行。同时程序为了能方便截图运行结果,设置为捕获20个IP包后持续线程休眠。
④程序流程图
⑤程序源码及注释
#include
#include
#include
#include
#include
#pragma comment(lib,"Ws2_32.lib")
using namespace std;
struct IP_HEAD
{
unsigned char ver_hlen;//版本和首部长度,总共占8字节
unsigned char DS;//区分服务
unsigned short PacketLen;//总长度
unsigned short Identification;//标识
union {
unsigned char Flag;//标识
unsigned char FlagOf;//片偏移
};
unsigned char TTL;//生存时间
unsigned char Protocol;//协议
unsigned short HeadCheckSum;//首部校验和
unsigned long Src;//原地址,32位
unsigned long Des;//目的地址
unsigned char Options;//选项
};
int cnt;
void Decode(char *buf, int len) {
int n = len;
if (n >= sizeof(IP_HEAD))
{
IP_HEAD iphead;
iphead = *(IP_HEAD*)buf;
cout << "第" << cnt++ << "个IP包:" << endl;
cout << "协议版本:" << (iphead.ver_hlen >> 4) << endl;//右移四位
cout << "首部长度" << ((iphead.ver_hlen & 0x0f) << 2) << endl;
cout << "区分服务:Priority:" << (iphead.DS >> 5) << " Service:" << ((iphead.DS >> 1) & 0x0f) << endl;
cout << "总长度:" << ntohs(iphead.PacketLen) << endl;//ntohs用于将网络字节序转为主机字节序
cout << "标识:" << ntohs(iphead.Identification) << endl;
cout << "标志:DF=" << ((iphead.Flag >> 14) & 0x0F) << " MF=" << ((iphead.Flag >> 13) & 0x0F) << endl;
cout << "片偏移:" << (iphead.FlagOf & 0x1fff) << endl;
cout << "生存周期:" << (int)iphead.TTL << endl;
cout << "协议:" << (int)iphead.Protocol << endl;
cout << "首部校验和:" << ntohs(iphead.HeadCheckSum) << endl;
//inet_ntoa用于将一个十进制网络字节序转换为点分十进制IP格式的字符串,in_addr是一个结构体,可以用来表示一个32位的IPv4地址
cout << "源地址:" << inet_ntoa(*(in_addr*)&iphead.Src) << endl;
cout << "目的地址:" << inet_ntoa(*(in_addr*)&iphead.Des) << endl;
cout << "=================================================================" << endl << endl;
}
}
//::WSACleanup()的功能是终止Winsock 2 DLL (Ws2_32.dll) 的使用
void AutoWSACleanup() {
::WSACleanup();
}
int main() {
int n;
WSADATA wd;//用来存储被WSAStartup函数调用后返回的Windows Sockets数据
//调用2.2版Winsock
n = WSAStartup(MAKEWORD(2, 2), &wd);
if (n)
{
cout << "WSAStartup函数错误!" << endl;
return -1;
system("pasue");
}
atexit(AutoWSACleanup);//注册终止函数
//创建socket
SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if (sock == INVALID_SOCKET) {
cout << WSAGetLastError();
return 0;
}
//获取本机地址
char name[128];
if (gethostname(name, sizeof(name)) == -1) {
closesocket(sock);
cout << WSAGetLastError();
return 0;
system("pasue");
}
struct hostent* pHostent;//记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表
pHostent = gethostbyname(name);
//绑定本地地址到SOCKET句柄
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr = *(in_addr*)pHostent->h_addr_list[0]; //IP
addr.sin_port = 8888; //端口
if (SOCKET_ERROR == bind(sock, (sockaddr *)&addr, sizeof(addr)))
{
closesocket(sock);
cout << WSAGetLastError();
return 0;
system("pasue");
}
//设置该SOCKET为接收所有流经绑定的IP的网卡的所有数据,包括接收和发送的数据包
u_long sioarg = 1;
DWORD wt = 0;
if (SOCKET_ERROR == WSAIoctl(sock, SIO_RCVALL, &sioarg, sizeof(sioarg), NULL, 0, &wt, NULL, NULL))
{
closesocket(sock);
cout << WSAGetLastError();
return 0;
system("pasue");
}
//我们只需要接收数据,因此设置为阻塞IO,使用最简单的IO模型
u_long bioarg = 0;
if (SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &bioarg))
{
closesocket(sock);
cout << WSAGetLastError();
return 0;
system("pasue");
}
//开始接收数据
//因为前面已经设置为阻塞IO,recv在接收到数据前不会返回。
cnt = 1;
char buf[65535];
int len = 0;
do
{
len = recv(sock, buf, sizeof(buf), 0);
if (len > 0)
{
Decode(buf, len);
}
} while (len > 0 && cnt <= 20);//只接受20个包
closesocket(sock);
while (1)
{
Sleep(3000);
}
system("pasue");
return 0;
}