做实验去找-->>网络及其计算实验(黑龙江大学)
实验一
一、实验名称
实验一 基于套接字的网络通信程序
二、实验目的
通过本实验使得学生掌握 Windows 通信程序的编写,通信原理,实际编写通信程序,本试验是后续实验的基础。要求学生做到在一台计算机上实现两个程序的通信,以及在两台计算机上实现两个应用程序的通信,对于学生所使用的程序开发环境不限。
三、实验类型
设计型
四、实验环境
编程环境:jdk1.8.0_181 、IntelliJ IDEA 2020.3.4
运行环境:Windows 11 家庭中文版
五、实验要求
利用编程语言,基于TCP或者UDP编写一个简单的Client/Server网络应用程序。要求:
(1)实现客户向服务器传输任意一个字符串,服务器将收到的字符串变换成大写后传回客户;
(2)进一步改进程序,利用网络通信完成具体的功能。例如:采用多线程
的服务器,使服务器可以为多客户端提供服务;或者,客户端向服务器提供一个查询请求,服务器将查询后的结果返回给客户端。
(3)撰写实验报告。
六、实验内容与步骤
1、实验中主要使用的技术和算法。
(A)熟悉并掌握Winsock的原理,常用的套接字函数如下:
(1)创建套接字------socket()
功能:使用前创建一个新的套接字
格式:SOCKET PASCAL FAR socket(int af,int type,int procotol);
参数:af:通信发生的区域
type:要建立的套接字类型
procotol:使用的特定协议
(2)指定本地地址------bind()
功能:将套接字地址与所创建的套接字号联系起来。
格式:int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);
参数:s:是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
其它:没有错误,bind()返回0,否则SOCKET_ERROR
地址结构说明:
struct sockaddr_in
{short sin_family;//AF_INET
u_short sin_port;//16 位端口号,网络字节顺序
struct in_addr sin_addr;//32位IP地址,网络字节顺序
char sin_zero[8];//保留}
(3)建立套接字连接------connect()和 accept()
功能:共同完成连接工作
格式:int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);
SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);
参数:同上
(4)监听连接------listen()
功能:用于面向连接服务器,表明它愿意接收连接。
格式:int PASCAL FAR listen(SOCKET s, int backlog);
(5)数据传输------send()与 recv()
功能:数据的发送与接收
格式:int PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);
int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);
参数:buf:指向存有传输数据的缓冲区的指针。
(6)多路复用------select()
功能:用来检测一个或多个套接字状态。
格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds, fd_set FAR * exceptfds,const struct timeval FAR * timeout);
参数:readfds:指向要做读检测的指针
writefds:指向要做写检测的指针
exceptfds:指向要检测是否出错的指针
timeout:最大等待时间
(7)关闭套接字------closesocket()
功能:关闭套接字s
格式:BOOL PASCAL FAR closesocket(SOCKET s);
(B)面向连接和无连接的套接字的系统调用时序图


(C).应用程序流程图

2、编码
java
package cn.hd.sy1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("等待连接服务端...");
OutputStream os;
InputStream is = null;
Socket s = new Socket("127.0.0.1", 1000);
System.out.println("连接服务端成功!");
while (true) {
System.out.println("请输入:");
os = s.getOutputStream();
String str = scanner.next();
if ("exit".equals(str)) {
break;
}
os.write(str.getBytes());
is = s.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String data = new String(bytes, 0, len);
System.out.println("服务端:" + data);
}
os.close();
if (is != null) {
is.close();
}
s.close();
}
}
package cn.hd.sy1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//服务端
public class ServerDemo {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
ServerSocket serverSocket = null;
Socket socket = null;
try {
//服务器监听端口号1000
serverSocket = new ServerSocket(1000);
System.out.println("等待连接...");
socket = serverSocket.accept();
System.out.println("连接成功!");
while (true) {
is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String data = new String(bytes, 0, len);
System.out.println("客户端:" + data);
os = socket.getOutputStream();
os.write(data.toUpperCase().getBytes());
if ("exit".equals(data)) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3、实验过程及结果(截图并加以说明)


七、实验总结
通过本实验,我深入了解了Windows通信程序的编写和通信原理。我学会了如何在一台计算机上实现两个程序的通信,以及如何在两台计算机上实现两个应用程序的通信。我了解了不同的通信方式,如基于套接字的网络通信和基于命名管道的本地通信。在实际编写通信程序的过程中,我掌握了如何建立连接、发送和接收数据、关闭连接等操作。通过这些实践,我对Windows通信程序有了更深入地理解,为以后的实际应用打下了坚实的基础。我相信这些知识和技能对我的未来学习和工作都将大有裨益。
附录
在接下来的实验中,我希望进一步深入研究使用消息队列进行通信,这可以实现异步通信和解耦合,提高系统的稳定性和可扩展性。另外,我还想探索使用远程过程调用(RPC)实现跨网络的程序通信,这可以让不同计算机上的程序像调用本地函数一样进行通信,非常方便。我还希望尝试使用WebSocket等现代化的通信技术来实现实时通信和数据交换,这对于一些互联网应用和实时系统非常重要。通过不断尝试和学习新的通信技术,我相信我可以不断提升自己的技能和能力。
实验二
一、实验名称
实验二 停止等待协议算法实现
二、实验目的
在实现了两个程序通信的前提下,模拟实现停止等待ARQ协议。
要求模拟实现:
(1)正常数据帧的通信过程
(2)错误帧的通信过程
(3)数据帧(确认帧)丢失的通信过程
三、实验类型
设计型
四、实验环境
编程环境:jdk1.8.0_181、IntelliJ IDEA 2020.3.4
运行环境:Windows 11 家庭中文版
五、实验要求
在实验一的基础上,模拟实现停止等待协议,完成下面的工作:
(1)正确数据帧的通信过程:需要在发送方显示所发送的数据帧,至少包
括序号、信息、校验码;接收方收到正确的数据,给发送方回复确认帧,包括确认号;发送方接收确认帧后,继续发送下一个数据帧。
(2)错误帧的通信过程:帧的数据部分发生错误,接收方给出上一次的确认号,发送方重新发送上一次的数据帧。
(3)数据帧的及确认帧的丢失过程:发送方在发送数据时,启动超时计时器,接收方在超时计时器的时间内没有返回响应信息,发送方重新发送上一次的数据帧。
(4)撰写实验报告。
六、实验内容与步骤
1、实验中主要使用的技术和算法。
发送方构造发送的数据帧,帧的结构中至少包含:序号、信息、校验码;
发送方将数据帧发给接收方;接收方判断帧的正确情况,并返回相应的响应信息。
如果只是使用一比特对帧进行编序号,可参考如下算法进行实现:
发方程序:
(1)从主机取一个数据帧,送交发送缓存。
(2)V(S)←0。
(3)N(S)←V(S)。
(4)将发送缓存中的数据帧发送出去。
(5)设置超时计时器。
(6)等待。
(7)收到确认帧 ACKn,
若n=1--V(s),则:
从主机取一个新的数据帧,放入发送缓存;
V(S)←[1 - V(S)],转到 (3)。
否则,丢弃这个确认帧,转到(6)。
(8)若超时计时器时间到,则转到(4)。
收方程序:
(1)V(R)←0。
(2)等待。
(3)收到一个数据帧;
若CRC校验无误
若N(S)=V(R),则执行(4);
否则丢弃此数据帧,然后转到(6)。
否则丢弃此数据帧,然后转到(2)。
(4) 将收到的数据帧中的数据部分送交上层软件
(5)V(R)←[1-V(R)]。
(6)n←V(R);
发送确认帧ACKn,转到(2)。
2、编码
java
package cn.hd.sy2;
import java.util.Random;
public class CyclicRedundancyCheck {
// 生成码 除数P
private int[] generatingCode;
public CyclicRedundancyCheck(String str) {
this.generatingCode = stringToArray(str);
}
// 将01字符串转为数组
private int[] stringToArray(String str) {
char[] chars = str.toCharArray();
int[] res = new int[chars.length];
for (int i = 0; i < chars.length; i++) {
res[i] = chars[i] - '0';//转换成整型
}
return res;
}
// 获取帧检验序列FCS
public String getFCS(String dividend) {
// 1. 传进来的是被除数:要发送的数据
StringBuilder dividendBuilder = new StringBuilder(dividend);
for (int i = 0; i < generatingCode.length - 1; i++) {
// 往后面补充 除数位数-1个0
dividendBuilder.append("0");
}
dividend = dividendBuilder.toString();
return calRemainder(stringToArray(dividend));
}
// 获取余数
public String getRemainder(String dividend) {
return calRemainder(stringToArray(dividend));
}
// 计算余数:模2除法求余数
private String calRemainder(int[] code) {
// 将余数转换为二进制字符串表示
int len = code.length - (generatingCode.length - 1); //len是数据部分的长度
for (int i = 0; i < len; i++) {
if (code[i] != 0) {
for (int j = 0; j < generatingCode.length; j++) {
//将当前位置的code数组元素与生成码的第j个元素进行异或操作,并将结果赋值给当前位置的code数组元素。
code[i + j] ^= generatingCode[j];
}
}
}
StringBuilder res = new StringBuilder();
for (int i = len; i < code.length; i++) {
res.append(code[i]);
}
return res.toString();
}
// 判断余数是否为0
public boolean isZero(String data) {
// 获取余数
int remainder = Integer.parseInt(data);
return remainder == 0;
}
// 数据包突变
public String setMutation(String data) {
int[] res = stringToArray(data);
Random random = new Random();
int i = random.nextInt(res.length);
// 突变
if (res[i] == 0) {
res[i] = 1;
} else {
res[i] = 0;
}
StringBuilder sb = new StringBuilder();
for (int re : res) {
sb.append(re);
}
return sb.toString();
}
public int[] getGeneratingCode() {
return generatingCode;
}
public void setGeneratingCode(String str) {
this.generatingCode = stringToArray(str);
}
}
package cn.hd.sy2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Sender {
public static void menu() {
System.out.println("1->正常数据帧的通信过程");
System.out.println("2->错误帧的通信过程");
System.out.println("3->确认帧的丢失的通信过程");
System.out.println("4->数据帧的丢失的通信过程");
}
public static void main(String[] args) {
menu();
Scanner scan = new Scanner(System.in);
try {
Socket socket = new Socket("127.0.0.1", 8888);
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
CyclicRedundancyCheck cyclicRedundancyCheck = new CyclicRedundancyCheck("10011");
int count = 0;
while (true) {
System.out.println("请选择菜单:");
int choose = scan.nextInt();
System.out.println("请输入数据,目前准备发送第" + count + "帧");
String message = scan.next();
String fcs = cyclicRedundancyCheck.getFCS(message);
switch (choose) {
case 1:
// 向服务器发送数据包
output.println(message + fcs);
output.println(count);
System.out.println("数据包:" + message + fcs);
// 设置超时器
// socket.setSoTimeout(2000);
// 从服务器接收响应消息并显示在控制台上
String response = input.readLine();
System.out.println("正确!想收到" + response + ", 实际收到" + response);
count++;
break;
case 2:
// 突变的数据
String newMessage = cyclicRedundancyCheck.setMutation(message);
// 向服务器发送数据包
output.println(newMessage + fcs);
output.println(count);
System.out.println("原来数据包:" + message + fcs);
System.out.println("突变数据包:" + newMessage + fcs);
// 从服务器接收响应消息并显示在控制台上
response = input.readLine();
System.out.println(response);
break;
case 3:
// 向服务器发送数据包
output.println(message + fcs);
System.out.println("数据包:" + message + fcs);
output.println(100);
// 设置超时器:睡眠2秒
Thread.sleep(2000);
System.out.println("ack丢失,超时自动重传!");
// 向服务器发送数据包
output.println(message + fcs);
output.println(count);
System.out.println("数据包:" + message + fcs);
// 设置超时器
// socket.setSoTimeout(2000);
// 从服务器接收响应消息并显示在控制台上
response = input.readLine();
System.out.println("正确!想收到" + response + ", 实际收到" + response);
count++;
break;
case 4:
System.out.println("数据包:" + message + fcs);
// 设置超时器:睡眠2秒
Thread.sleep(2000);
System.out.println("数据帧丢失,超时重传!!!");
break;
default:
System.out.println("输入错误,请重新输入~!");
if (message.equals("exit")) {
socket.close();
break;
}
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package cn.hd.sy2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Receive {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("等待客户端连接...");
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接");
BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter output = new PrintWriter(clientSocket.getOutputStream(),true);
CyclicRedundancyCheck cyclicRedundancyCheck = new CyclicRedundancyCheck("10011");
int count = 0;
while (true) {
// 从客户端接收数据并显示到控制台上
String message = input.readLine();
count = Integer.parseInt(input.readLine());
if (count == 100) {
continue;
}
// 获取余数
String remainder = cyclicRedundancyCheck.getRemainder(message);
// 校验余数是否为0
boolean isZero = cyclicRedundancyCheck.isZero(remainder);
if (isZero) {
System.out.println("已经成功接收到第" + count + "帧,数据为:" + message.substring(0,message.length()-4));
output.println("ack" + ++count);
}else {
output.println("发生错误!!!想收到ack"+ ++count + ", 实际收到ack" + --count);
System.out.println("收到了错误的数据包:" + message);
}
if(message.equals("close")) {
break;
}
}
clientSocket.close();
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、实验过程及结果(截图并加以说明)




七、实验总结
通过本实验,我深入了解了正常数据帧的通信过程、错误帧的通信过程以及数据帧的丢失的通信过程。我学会了如何处理和纠正错误帧,以及如何处理丢失的数据帧。在实际编写通信程序的过程中,我掌握了如何识别和处理不同类型的数据帧,以确保通信的可靠性和正确性。通过这些实践,我对数据帧的通信过程有了更深刻的认识,为我在网络通信领域的学习和工作打下了坚实的基础。我相信这些知识和技能对我的未来学习和工作都将大有裨益。
附录
在接下来的实验中,我希望进一步深入研究流控制和拥塞控制的通信过程,了解在网络通信中如何有效地管理数据流和处理拥塞情况。此外,我也希望进一步研究数据链路层的协议,如以太网协议和无线局域网协议,以及它们在实际通信中的应用和性能优化。另外,我也对网络安全和数据加密在通信过程中的应用很感兴趣,希望深入了解如何保护通信数据的安全性和完整性。通过不断学习和实践,我相信我可以在网络通信领域取得更多的进步和成就。
实验三
一、实验名称
实验三 网桥原理模拟
二、实验目的
本实验对应教材的数据链路层协议。
1、在实验一的基础上实现网桥原理的模拟,更新站表的过程;
2、站表逆向学习;
3、帧转发处理过程。
三、实验类型
设计型
四、实验环境
编程环境:jdk1.8.0_181 、IntelliJ IDEA 2020.3.4
运行环境:Windows 11 家庭中文版
五、实验要求
在实验一的基础上实现网桥模拟;
(1)采用 Timer 机制来实现定时处理,每间隔一段时间发送数据帧。
(2)显示不同机制下网桥站表的变化过程;程序中能够显示上图中两个网
桥转发表的变化,以及每次对数据帧的具体操作,如:向***接口转发(不转发)。
(3)撰写实验报告。
六、实验内容与步骤
1、实验中主要使用的技术和算法。
- 掌握网桥的学习转发过程如下:
(1)网桥收到一帧后先进行自学习。查找转发表中与收到帧的源地址有无相匹配的项目。如没有,就在转发表中增加一个项目(源地址、进入的接口和时间)。如有,则把原有的项目进行更新。
(2)转发帧。查找转发表中与收到帧的目的地址有无相匹配的项目。如没有,则通过所有其他接口(但进入网桥的接口除外)进行转发。如有,则按转发表中给出的接口进行转发。
(3)若转发表中给出的接口就是该帧进入网桥的接口,则应丢弃这个帧(因为这时不需要经过网桥进行转发)。
(4)可选择生成树算法,避免网络存在回路,即在任何两个网站之间只有一条路径。
(5)返回。
- 利用上述学习转发算法,完成下列帧在传送过程中,网桥转发表的变化。

2、编码
java
package cn.hd.sy3;
import java.util.HashMap;
import java.util.Map;
public class Host {
String mac;// mac地址
String port; // 端口号
static Map<String, String> hostMap = new HashMap<>();// 存储所有主机信息 mac:port
public Host(String mac, String port) {
this.mac = mac;
this.port = port;
hostMap.put(mac, port);
}
public static void initHosts(Host[] hosts) {
String[] macName = new String[hosts.length];
String[] portName = new String[hosts.length];
for (int i = 0; i < hosts.length; i++) {
if (i < 10) {
macName[i] = "liu-he-0" + i;
portName[i] = "000" + i;
} else {//主机个数大于10
macName[i] = "liu-he-" + i;
portName[i] = "00" + i;
}
hosts[i] = new Host(macName[i], portName[i]);
}
}
public static void showHosts() {
for (Map.Entry<String, String> entry : hostMap.entrySet()) {
System.out.println("MAC:" + entry.getKey() + " Port:" + entry.getValue());
}
}
}
package cn.hd.sy3;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class NetBridge {
// 转发表
static Map<String, String[]> exchangeTable = new HashMap<>();
/*
* liu-he-00 liu-he-01 aaa
* liu-he-02 liu-he-02 bbb
* liu-he-00 liu-he-01 ccc
* liu-he-00 liu-he-00 eee
* liu-he-00 liu-he-02 fff
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Host.initHosts(new Host[4]);// 初始化4台主机
while (true) {
System.out.println("1. 输入发送帧:源地址、目的地址、信息");
System.out.println("2. 显示转发表信息");
System.out.println("3. 显示所有主机信息");
System.out.println("4. 退出");
String choose = scanner.next();
switch (choose) {
case "1":
System.out.println("请输入:");
scanner.nextLine(); // 目的清除缓冲区
String frame = scanner.nextLine();
NetBridge.transferFrame(frame);
break;
case "2":
NetBridge.showExchangeTable();
break;
case "3":
Host.showHosts();
break;
case "4":
System.exit(0);
break;
default:
System.out.println("输入错误,请重新输入!");
break;
}
}
}
// 显示转发表信息
public static void showExchangeTable() {
for (Map.Entry<String, String[]> entry : exchangeTable.entrySet()) {
System.out.println("MAC:" + entry.getKey() + " Port:" + entry.getValue()[0] + " Date:" + entry.getValue()[1]);
}
}
// 传输帧
public static void transferFrame(String frame) {
String[] data = frame.split(" ");
String srcMac = data[0]; // 源地址
String dstMac = data[1]; // 目的地址
String info = data[2];// 信息
// 学习:查找转发表 entry:键值对 -> mac:port
// 源地址是否存在
boolean isSrcExist = exchangeTable.containsKey(srcMac);
// 目的地址是否存在
boolean isDestExist = exchangeTable.containsKey(dstMac);
// 源地址不在转发表中:需要加入交换表
// 源地址在转发表中:需要更新交换表中对应的进入接口和有效时间
insertOrUpdateExchangeTable(srcMac, isSrcExist);
// 目的地址存在
if (isDestExist) {
System.out.println("查找到目的地址:" + dstMac + ",直接转发信息" + info);
} else {
// 目的地址不存在:需要广播
receiveBroadCast(srcMac, dstMac, info);
}
}
public static void insertOrUpdateExchangeTable(String srcMac, boolean isSrcExist) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 1. 获取当前 mac:port
String curMac = srcMac;
String curPort = Host.hostMap.get(srcMac);
String date = dateFormat.format(System.currentTimeMillis());
exchangeTable.put(curMac, new String[]{curPort, date});
// 2. 存在则更新
if (isSrcExist) {
System.out.println("源Mac地址为:" + srcMac);
System.out.println("加入转发表的端口:" + curPort);
System.out.println("更新加入的时间为:" + date);
} else {
// 不存在则新增
System.out.println("源Mac地址为:" + srcMac);
System.out.println("加入了转发表的端口:" + curPort);
System.out.println("加入的时间为:" + date);
}
}
public static void receiveBroadCast(String srcMac, String dstMac, String info) {
// 遍历所有主机
for (Map.Entry<String, String> entry : Host.hostMap.entrySet()) {
String curMac = entry.getKey();
String curPort = entry.getValue();
// 4.1 发送时的接口
String srcPort = Host.hostMap.get(srcMac);
// 4.2 接收时的接口
String dstPort = Host.hostMap.get(dstMac);
if (curMac.equals(dstMac) && srcPort.equals(dstPort)) {
System.out.println(dstPort + "接口丢弃了该帧, 不需要经过网桥转发,该帧就是进入网桥的接口");
System.out.println("已收到消息:" + info);
continue;
}
if (dstMac.equals(curMac)) {
System.out.println("转发:地址" + curMac + "通过" + curPort + "接口收下了该帧");
System.out.println("消息为:" + info);
}
}
}
}
3、实验过程及结果(截图并加以说明)



七、实验总结
通过实现网桥原理的模拟,我深入了解了站表的更新过程。我学会了如何通过监听网络中的数据流量,更新站表以建立目的地址和端口之间的映射关系。我还学会了站表的逆向学习,即根据数据包的源地址更新站表。在实际模拟网桥原理的过程中,我掌握了帧转发处理过程,包括如何根据目的地址在站表中查找对应的端口,然后将帧转发到相应的端口。通过这些实践,我对网桥原理有了更深入的理解,为我在网络通信领域的学习和工作打下了坚实的基础。我相信这些知识和技能对我的未来学习和工作都将大有裨益。
附录
在接下来的实验中,我希望进一步深入研究虚拟局域网(VLAN)的实现原理和应用,了解如何通过VLAN将网络划分为多个逻辑上的子网,以实现更灵活的网络管理和安全隔离。另外,我也希望研究多层交换机的工作原理和路由器与交换机的区别,以及它们在网络通信中的不同应用场景。除此之外,我也对网络拓扑的设计和优化很感兴趣,希望通过实践和研究,掌握如何设计高性能和可靠的网络拓扑结构。通过不断深入研究和实践,我相信我可以在网络通信领域取得更多的进步和成就。
实验四
一、实验名称
实验四 网络协议分析
二、实验目的
本实验对应教材的数据链路层、网络层、传输层协议。
网络协议工具可以获得局域网内各节点计算机的大量基本信息,利用Sniffer、WinPcap、Wireshark等工具,过滤、捕获流经本地的信息,可以加以分析和显示,编辑、发送各种类型的信包,同时捕获、分析对发送信包的响应信包,从而观察发送信包后对方的相应响应。为未来应用开发测试过程的问题定位,做准备工作。
了解HTTP协议通信过程,理解HTTP报文的命令。
三、实验类型
设计型
四、实验环境
编程环境:jdk1.8.0_181、IntelliJ IDEA 2020.3.4
运行环境:Windows 11 家庭中文版
五、实验要求
1、利用编程语言,基于 HTTP 报文协议实现WEB通信,理解HTTP报文格式及实现通信过程。要求实现客户/服务器之间读取WEB的页面,或者可以进一步改进程序,加入自己的想法,使之更加具有实用价值。
2、对截获的数据包进行分析(需要在实验报告中附上截图作为作答一句):分析 HTTP 协议工作原理:
(1)浏览器和服务器所运行的HTTP版本号是多少?
(2)浏览器支持的语言类型在哪里可以查看?当前截获的数据包的浏览器所支持的语言类型是什么?
(3)浏览器支持的压缩方式在哪里可以查看?当前截获的数据包的浏览器所支持的压缩方式是什么?
(4)通过什么信息可以判断服务器是否成功返回客户端所需要的信息?
(5)在响应报文中,服务器返回对象的最后修改时间是多少?返回浏览器的内容共多少字节?
TCP会话过程数据包的分析:
(1)从捕获的数据包中找出三次握手建立连接的数据包。
(2)从找到的三次握手数据包中观察,客户端协商的MSS为多少?客户端的接收窗口为多少?
(3)服务器协商的MSS为多少?服务器端的接收窗口为多少?
(4)说明在三次握手过程中数据包的序号、确认号、SYN标志位、ACK标志位的变化。
(5)当客户端发送了HTTP请求报文以后,客户端收到服务器的ACK为多少?
(6)在捕获的数据包中找到上次握手释放链接的数据包。
六、实验内容与步骤
1、实验中主要使用的技术和算法。
(一)简单http协议实现
-
实现一个简单的HTTP服务器程序能通过 GET 命令将网页发送到客户端;
-
实现多线程的服务器端;
-
能实现其他的HTTP命令。

根据以上HTTP请求报文结构,编写如下 HTTP 服务器程序:

通过自己编写客户端程序,或者使用本地浏览器访问服务器,如下:

模仿HTTP服务器的程序Phttpd源代码如下:
import java.io.*;
import java.net.*;
import java.util.*;
// Phttpd 类
class Phttpd {
public static void main(String args[]) {
ServerSocket servsock = null;
Socket sock;
OutputStream out;
BufferedReader in;
FileInputStream infile = null;
byte[] buff = new byte[1024];
boolean cont = true;
int i;
try {
servsock = new ServerSocket(800, 300);
while (true) {
sock = servsock.accept();
System.out.println("连接请求"
- (sock.getInetAddress()).getHostName());
try {
14
infile = new FileInputStream(args[0]);
} catch (FileNotFoundException e) {
System.err.println("没有找到文件");
System.exit(1);
}
in = new BufferedReader(new
InputStreamReader(sock.getInputStream()));
out = sock.getOutputStream();
//跳过 2 个换行符
for (i = 0; i < 2; ++i)
in.readLine();
cont = true;
while (cont) {
try {
int n = infile.read(buff);
out.write(buff, 0, n);
} catch (Exception e) {
cont = false;
}
}
sock.close();
infile.close();
}
} catch (IOException e) {
System.exit(1);
}
}
}
(二)分析HTTP和TCP协议的分析
利用利用 Sniffer、WinPcap、Wireshark 等工具,过滤、捕获流经本地的信 息,可以加以分析和显示,编辑、发送各种类型的信包,同时捕获、分析对发送 信包的响应信包,从而观察发送信包后对方的相应响应。
2、编码
java
package cn.hd.sy4;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
//http://localhost:8080/a.html
public static void main(String[] args) {
System.out.println("正在等待连接...");
Server server = new Server();
server.startServer();
}
public void startServer() {
try {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept();
Thread thread = new Thread(new Process(socket));//多线程
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.hd.sy4;
import java.io.*;
import java.net.Socket;
public class Process implements Runnable {
Socket socket;
public Process(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//字节流转换成字符流
String ss = br.readLine();//按行读
System.out.println(ss);//打印请求行
String s = ss.split(" ")[1];//显示数组中第二个
System.out.println(s);//打印资源路径
String path = s.substring(1);
System.out.println(path);
OutputStream os = socket.getOutputStream();
int len;
byte[] b = new byte[1024];
//向客户端发送HTTP响应头,版本为1.1,状态码为200,表示请求成功
os.write("HTTP/1.1 200 OK\r\n".getBytes());
//根据资源路径的不同,设置不同的响应头中的content-Type字段
if (path.endsWith(".html")) {
os.write("content-Type:text/html\r\n".getBytes());//HTML类型
} else if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
os.write("content-Type:image/jpeg\r\n".getBytes());//JPEG类型
} else if (path.endsWith(".png")) {
os.write("content-Type:image/png\r\n".getBytes());//PNG类型
} else if (path.equals("favicon.ico")) {
os.write("content-Type:text/plain\r\n".getBytes());//文本类型
os.write("\r\n".getBytes());
os.close();
br.close();
return;
}
os.write("\r\n".getBytes());//添加空行作为响应头和响应体之间的分隔符
FileInputStream fileInputStream = new FileInputStream("D:\\experimentDemo\\ComputerNetwork\\src\\cn\\hd\\sy4\\" + path);//数据单元
while ((len = fileInputStream.read(b)) != -1) {
os.write(b, 0, len);//将文件内容写入输出流
}
os.close();
fileInputStream.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpage</title>
<link rel="icon" type="image/png" href="img/ico.png">
</head>
<body>
<p style="font-size: xx-large;text-align: center">我的http协议测试</p>
<hr>
<div style="text-align: center;">
<img src="img/1.jpg" style="width: 20%" alt="图片1">
<img src="img/2.png" style="width: 20%" alt="图片2">
</div>
<hr/>
</body>
</html>
3、实验过程及结果(截图并加以说明)


七、实验总结
通过本实验,我深入了解了HTTP协议通信过程,理解了HTTP报文的命令和通信原理。我学会了通过编程实现HTTP协议,将服务器程序通过GET命令将网页发送到客户端,并实现了多线程的服务器端以及其他HTTP命令的处理。在实际编写通信程序的过程中,我掌握了如何建立HTTP连接、发送HTTP请求、处理HTTP响应等操作。通过这些实践,我对HTTP协议有了更深入的理解,为我在网络通信领域的学习和工作打下了坚实的基础。我相信这些知识和技能对我的未来学习和工作都将大有裨益。
附录
在接下来的实验中,我希望进一步深入研究虚拟局域网(VLAN)的实现原理和应用,了解如何通过VLAN将网络划分为多个逻辑上的子网,以实现更灵活的网络管理和安全隔离。另外,我也希望研究多层交换机的工作原理和路由器与交换机的区别,以及它们在网络通信中的不同应用场景。除此之外,我也对网络拓扑的设计和优化很感兴趣,希望通过实践和研究,掌握如何设计高性能和可靠的网络拓扑结构。通过不断深入研究和实践,我相信我可以在网络通信领域取得更多的进步和成就。
实验五
一、实验名称
实验五 常用网络命令
二、实验目的
通过本试验掌握网络上的实用命令的实用,主要包括:ARP,IPCONFIG,net,ping命令等。具体要求如下:
(1)掌握实用命令的格式
(2)掌握各个实用命令的参数
(3)在计算机上测试每个命令
三、实验类型
设计型
四、实验环境
编程环境:jdk1.8.0_181、IntelliJ IDEA 2020.3.4
运行环境:Windows 11 家庭中文版
五、实验要求
(1)在MS-DOS方式下运行下列 ipconfig 命令,查看本机TCP/IP协议配置等相关信息,并分析结果。
(2)在获取本机IP地址之后,在MS-DOS方式下运行下列Ping命令,填写实验运行结果(附截图)。
(a)ping本机IP地址(b)ping本机IP地址--t(c)ping默认网关--n6(d)ping本局域网内任意一台主机
(3)用nslookup命令从域名地址当中解析出IP地址,从IP地址当中解析出域名地址。填写下表:

(4)利用路由跟踪命令tracert,跟踪到达某个网站(如www.163.com)的路由信息。
(5)用netstat命令,显示以太网接口的统计信息,并显示所有已建立好的有效连接。
(6)用arp命令查看arp地址映射表,填加一条静态地址映射,并显示结果和操作过程。
六、实验内容与步骤
1、实验中主要使用的技术和算法。
在MS-DOS方式下依次运行下列命令,ping,ipconfig,nslookup,arp,tracert,并通过help查看每个命令的含义及其参数。
(1)Ping命令的使用技巧
Ping是一个使用频率极高的实用程序,用于确定本地主机是否能与另一台主机交换(发送与接收)数据报。根据返回的信息,我们就可以推断TCP/IP参数是否设置得正确以及运行是否正常。
(2)通过Ping检测网络故障的典型次序
下面就给出一个典型的检测次序及对应的可能故障:
ping 127.0.0.1:这个Ping命令被送到本地计算机的IP软件,该命令永不退出该计算机。如果没有做到这一点,就表示 TCP/IP 的安装或运行存在某些最基本的问题。
ping本机IP:这个命令被送到我们计算机所配置的IP地址,我们的计算机始终都应该对该Ping 命令作出应答,如果没有,则表示本地配置或安装存在问题。
出现此问题时,局域网用户请断开网络电缆,然后重新发送该命令。如果网线断开后本命令正确,则表示另一台计算机可能配置了相同的IP地址。
ping局域网内其他IP:这个命令应该离开我们的计算机,经过网卡及网络电缆到达其他计算机,再返回。收到回送应答表明本地网络中的网卡和载体运行正确。
但如果收到0个回送应答,那么表示子网掩码(进行子网分割时,将IP地址的网络部分与主机部分分开的代码)不正确或网卡配置错误或电缆系统有问题。
ping网关IP:这个命令如果应答正确,表示局域网中的网关路由器正在运行并能够作出应答。
ping远程IP:如果收到4个应答,表示成功地使用了缺省网关。对于拨号上网用户则表示能够成功的访问Internet(但不排除ISP的DNS会有问题)。
ping localhost:localhost是个作系统的网络保留名,它是127.0.0.1的别名,每太计算机都应该能够将该名字转换成该地址。如果没有做到这一带内,则表示主机文件(/Windows/host)中存在问题。
ping www.xxx.com(如www.yesky.com天极网):对这个域名执行Ping
www.xxx.com地址,通常是通过DNS服务器 如果这里出现故障,则表示DNS
服务器的IP地址配置不正确或DNS服务器有故障(对于拨号上网用户,某些ISP已经不需要设置DNS服务器了)。
如果上面所列出的所有Ping命令都能正常运行,那么我们对自己的计算机
进行本地和远程通信的功能基本上就可以放心了。但是,这些命令的成功并不表
示我们所有的网络配置都没有问题,例如,某些子网掩码错误就可能无法用这些
方法检测到。
(2)Netstat
Netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。
如果我们的计算机有时候接受到的数据报会导致出错数据删除或故障,我们
不必感到奇怪,TCP/IP可以容许这些类型的错误,并能够自动重发数据报。但如果累计的出错情况数目占到所接收的IP数据报相当大的百分比,或者它的数目正迅速增加,那么我们就应该使用Netstat查一查为什么会出现这些情况了。
netstat --a:本选项显示一个所有的有效连接信息列表,包括已建立的连接(ESTABLISHED),也包括监听连接请求(LISTENING)的那些连接。
netstat --n:显示所有已建立的有效连接。
(3)IPConfig命令
Ipconfig:当使用IPConfig时不带任何参数选项,那么它为每个已经配置了的接口显示 IP 地址、子网掩码和缺省网关值。
ipconfig/all:当使用all选项时,IPConfig能为DNS和WINS服务器显示它已配置且所要使用的附加信息(如IP地址等),并且显示内置于本地网卡中的物理地址(MAC)。如果IP地址是从DHCP服务器租用的,IPConfig将显示DHCP服务器的IP地址和租用地址预计失效的日期。
(4)ARP(地址转换协议)
ARP是一个重要的TCP/IP协议,并且用于确定对应IP地址的网卡物理地址。
使用arp命令,我们能够查看本地计算机或另一台计算机的ARP高速缓存中的当前内容。此外,使用arp命令,也可以用人工方式输入静态的网卡物理/IP地址对,我们可能会使用这种方式为缺省网关和本地服务器等常用主机进行这项工作,有助于减少网络上的信息量。
ARP常用命令选项:
arp -a或arp -g:用于查看高速缓存中的所有项目。-a和-g参数的结果是一样的,多年来-g一直是UNIX平台上用来显示 ARP 高速缓存中所有项目的选项,而Windows用的是arp -a(-a可被视为all,即全部的意思),但它也可以接受比较传统的-g选项。
arp -a IP:如果我们有多个网卡,那么使用arp -a 加上接口的IP地址,就可以只显示与该接口相关的 ARP 缓存项目。
arp -s IP物理地址:我们可以向ARP高速缓存中人工输入一个静态项目。该项目在计算机引导过程中将保持有效状态,或者在出现错误时,人工配置的物理地址将自动更新该项目。
(5)Tracert
如果有网络连通性问题,可以使用tracert命令来检查到达的目标IP地址的路径并记录结果。tracert 命令显示用于将数据包从计算机传递到目标位置的一组 IP 路由器,以及每个跃点所需的时间。
tracert IP address [-d]该命令返回到达IP地址所经过的路由器列表。通过使用-d选项,将更快地显示路由器路径,因为tracert不会尝试解析路径中路由器的名称。
2、实验过程及结果(截图并加以说明)




表1-1 域名解析表
|-----------------------------------------|----------------------------|
| 域名地址 | IP地址 |
| public-dns-b.dnspai.com | 202.203.208.100 |
| public-dns-b.dnspai.com | 202.203.208.32 |
| www.163.com | 2409:8c54:3810:2:0:2:1:10 |
| www.google.com | 2001::a88f:a275 31.13.92.5 |



七、实验总结
通过本实验,我更加深刻地理解了常用的网络命令,并进行了实际练习。我掌握了实用命令的格式、各个实用命令的参数,以及在计算机上具体测试每个命令的操作步骤。在实际练习中,我学会了如何使用网络命令来诊断网络问题、管理网络设备、测试网络连接等。通过这些实践,我对网络命令有了更深入地理解,为我在网络管理和维护工作中积累了宝贵的经验。我相信这些知识和技能对我的未来学习和工作都将大有裨益。
附录
在接下来的实验中,我希望进一步深入研究使用不同的网络命令来进行更复杂的网络故障排除,比如使用traceroute命令来跟踪数据包在网络中的路径,使用tcpdump命令来捕获网络数据包进行分析,以及使用nmap命令来扫描网络上的开放端口和服务等。这些命令可以帮助我更全面地了解网络的运行情况,提高我的网络故障排除能力。另外,我还可以尝试在不同的操作系统上进行网络命令的实践,比如在Linux、Windows、Mac等系统上进行相同命令的操作,以便更全面地掌握这些命令的使用方法。通过这些额外的实践,我相信我可以更加深入地理解网络命令的使用和原理。
若觉得有帮助,欢迎点赞关注,一起成长进步~
声明:本文仅供学习交流,禁作商用;禁篡改、歪曲及有偿传播,引用需标明来源。侵权必究。