4-编程实现基于某UDP地PING
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
计算机网络课程设计
课程名称计算机网络课程设计学院计算机学院
专业班级
学号
学生姓名
指导教师梁路
年月日
计算机网络课程设计任务书
目录:
1. 基础知识: (1)
1.1. ICMP (1)
1.2. Ping (1)
1.3. UDP (1)
1.4. 多线程: (2)
2. 设计思路: (2)
2.1. 线程设计: (3)
2.2. 服务器设计: (3)
2.3. 客户端设计: (3)
3. 程序流程图: (3)
3.1. Java线程程序流程图: (4)
3.2. PingServer程序流程图: (4)
3.3. PingClient程序流程图: (5)
4. 代码: (6)
4.1. 线程代码: (6)
4.2. 服务器端代码: (8)
4.3. 客户端代码: (10)
5. 编译过程与截图 (15)
6. 课程设计小结 (17)
1.基础知识:
1.1.ICMP
ICMP是(Internet Control Message Protocol)Internet控制报文协议。
它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。
控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。
这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP协议是一种面向无连接的协议,用于传输出错报告控制信息。
它是一个非常重要的协议,它对于网络安全具有极其重要的意义。
1.2.Ping
PING 是DOS命令,一般用于检测网络通与不通,也叫时延,其值越大,速度越慢PING (Packet Internet Grope),因特网包探索器,用于测试网络连接量的程序。
Ping发送一个ICMP回声请求消息给目的地并报告是否收到所希望的ICMP回声应答。
它是用来检查网络是否通畅或者网络连接速度的命令。
作为一个生活在网络上的管理员或者黑客来说,ping命令是第一个必须掌握的DOS命令,它所利用的原理是这样的:网络上的机器都有唯一确定的IP地址,我们给目标IP地址发送一个数据包,对方就要返回一个同样大小的数据包,根据返回的数据包我们可以确定目标主机的存在,可以初步判断目标主机的操作系统等。
Ping 是Windows系列自带的一个可执行命令。
利用它可以检查网络是否能够连通,用好它可以很好地帮助我们分析判定网络故障。
应用格式:Ping IP 地址。
该命令还可以加许多参数使用,具体是键入Ping按回车即可看到详细说明。
ping指的是端对端连通,通常用来作为可用性的检查。
1.3.UDP
UDP是User Datagram Protocol的简称,中文全称是用户数据包协议,是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
在网络中
它与TCP协议一样用于处理数据包。
在OSI模型中,UDP协议在第四层——传输层,处于IP协议的上一层。
与TCP相比,UDP有不提供数据报分组、组装和不能对数据包的排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。
UDP用来支持那些需要在计算机之间传输数据的网络应用。
(1)UDP是一个无连接协议
(2)由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
(3)UDP信息包的标题很短,只有8个字节
(4)吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
(5)UDP使用尽最大努力交付,即不保证可靠交付
(6)UDP是面向报文的。
发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。
既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小
1.4.多线程:
Java中创建线程有两种方法:使用Thread类和使用Runnable接口。
在使用Runnable接口时需要建立一个Thread实例。
因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例。
Java中关于线程调度的API最主要的有下面几个:
线程睡眠:Thread.sleep(long millis)方法
线程等待:Object类中的wait()方法
线程让步:Thread.yield()方法
线程加入:join()方法
线程唤醒:Object类中的notify()方法
2.设计思路:
2.1.线程设计:
(1)获取从服务其传送到的请求报文的数据,将原报文内的数据封装进一个新的DatagramPacket中。
(2)选取处于0-1500之间的随机数,作为睡眠时间。
(3)睡眠时间结束后,开始向获取到的客户端的地址和端口发送封装好的DatagramPacket
2.2.服务器设计:
(1)定义一个UDP的DatagramSocket传送数据,记录服务器启动的时间(2)进入死循环,不断监听是否有客户端传送来的请求报文,若有,转(3)(3)计算当前时间与服务器启动时间的差值,若差值处于5秒到5.5秒之间,返回(2)(模拟分组丢失);否则启动新线程
2.3.客户端设计:
(1)定义一个UDP的DatagramSocket传送数据。
(2)将关键字PingUDP,序号和时间戳封装到DatagramPacket中作为报文的内容。
(3)向服务器的制定端口发送封装好的报文信息
(4)设置超时计时器
(5)超过超时计时器的时间未收到服务器发来的响应报文,则返回(2);否则,转(5)
(6)判断接收到的报文是否与当前发送的报文相对应,若是,返回(2);否则,返回(5)。
3.程序流程图:
3.1.Java线程程序流程图:
3.2.PingServer程序流程图:
3.3.PingClient程序流程图:
4.代码:
4.1.线程代码:
/**
*
*/
import .DatagramPacket;
import .DatagramSocket;
import java.io.IOException;
import .InetAddress;
/**
* 线程
* @author LingHuacai
*
*/
public class ThreadServer extends Thread{
private DatagramSocket socket;
private DatagramPacket packet;
public ThreadServer(DatagramSocket socket, DatagramPacket packet){
//初始化socket和packet
this.socket = socket;
this.packet = packet;
}
public void run(){
//定义延迟回送报文的随机时间
long randomTime = (long)(Math.random()*1500);
String data = new String(packet.getData());
byte[] buffer = data.getBytes();
System.out.println("收到报文"+data);
//获取客户端地址
InetAddress host = packet.getAddress();
//获取客户端应用进程端口号
int port = packet.getPort();
//回送给客户端的报文
DatagramPacket send = new DatagramPacket(buffer, buffer.length, host, port);
try{
sleep(randomTime);
}catch(InterruptedException e){
e.printStackTrace();
}
try{
//回送报文
socket.send(send);
}catch(IOException ee){
System.out.println("服务器发送回复报文失败!");
ee.printStackTrace();
}
}
}
4.2.服务器端代码:
/**
*
*/
package udp;
import .DatagramPacket;
import .DatagramSocket;
import java.io.IOException;
import java .net.SocketException;
/**
* 服务器端
* @author LingHuacai
*
*/
public class PingServer extends Thread{ //监听的端口号
private int initPort;
private byte[] buf = new byte[32];
private DatagramPacket packet;
private DatagramSocket socket;
public PingServer(int initPort){
this.initPort = initPort;
}
public void run(){
try{
//初始化socket,定义socket的端口号
socket = new DatagramSocket(initPort);
System.out.println("Server started");
}catch(SocketException e){
System.out.println("监听端口"+initPort+"失败");
e.printStackTrace();
//初始化端口号失败,终止程序
System.exit(0);
}
//记录当前时间
long startTime = System.currentTimeMillis();
//死循环,不断监听是否有报文请求
while(true){
try {
packet = new DatagramPacket(buf,buf.length);
//获取客户端发来的报文
socket.receive(packet);
}catch(IOException e){
e.printStackTrace();
}
//输出分组和服务器启动时间的差值
System.out.println("接收到这个分组的的时间与服务器启动时间差:"+(System.currentTimeMillis()-startTime)+"毫秒!");
if(5000>(System.currentTimeMillis()-startTime) ||
(System.currentTimeMillis()-startTime)>5500){
ThreadServer server = new ThreadServer(socket, packet);
//启动线程
server.start();
}
}
else System.out.println("报文丢失!");
}
public void destroy(){
socket.close();
}
public static void main(String[] args){
//初始化服务器
PingServer ping = new PingServer(Integer.valueOf(args[0]));
ping.start();
}
}
4.3.客户端代码:
import .DatagramPacket;
import .DatagramSocket;
import .UnknownHostException;
import java.io.IOException;
import .InetAddress;
import .SocketException;
import java.util.Date;
import java.text.SimpleDateFormat;
/**
* 客户端
* @author LingHuacai
*
*/
public class PingClient extends Thread {
private DatagramSocket client;
private InetAddress hostAddress;
int port;
//定义并初始化接收到的响应报文的个数
int j = 0;
//定义并初始化最小往返时间,最大往返时间,平均往返时间long minRtt=0, maxRtt=0, avrRtt=0, sumRtt=0;
long[] rtt = new long[10];
public PingClient(String host,int port){
this.port = port;
try {
client = new DatagramSocket();
//获取客户端地址
hostAddress = InetAddress.getByName(host);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch(SocketException ee){
ee.printStackTrace();
}
}
public void run(){
//定义时间戳格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-ddhh:mm:ss.SS");
for(int i=0; i<10; i++){
rtt[i] = 0;
}
for(int i = 0;i<10;i++){
//发送报文中的时间
Date sendTime = new Date();
String outMessage = new String("PingUDP:"+i+","+sdf.format(sendTime));
String recieve = null;
byte[] buffer = outMessage.getBytes();
byte[] buf = new byte[buffer.length];
DatagramPacket sendPacket = new DatagramPacket(buffer, buffer.length, hostAddress, port);
DatagramPacket recievePacket = new DatagramPacket(buf, buf.length);
try {
client.send(sendPacket);
//设置超时时间为1秒
client.setSoTimeout(1000);
//接收响应报文
client.receive(recievePacket);
recieve = new String(recievePacket.getData());
//判断是否为延时报文,若为延时报文则丢弃,继续接收报文
while(!recieve.equals(outMessage)){
recieve = null;
client.receive(recievePacket);
recieve = new String(recievePacket.getData());
}
//记录接收后时间
Date recieveTime = new Date();
//计算往返时间
rtt[i] = recieveTime.getTime()-sendTime.getTime();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("响应报文丢失或超时!"+"\n");
e.printStackTrace();
}
if(recieve!=null){
System.out.println(recieve);
System.out.println("rtt:"+rtt[i]);
}
}
if(rtt[0]!=0){
minRtt = rtt[0];
maxRtt = rtt[0];
sumRtt = rtt[0];
j++;
}
else minRtt = 2000;
for(int i=1; i<10; i++){
if(rtt[i]!=0){
j++;
//计算最小往返时间
if(minRtt>rtt[i]){
minRtt = rtt[i];
}
//最大往返时间
if(maxRtt<rtt[i]){
maxRtt = rtt[i];
}
//总往返时间
sumRtt = sumRtt + rtt[i];
}
}
if(j!=0){
//计算平均往返时间
avrRtt = sumRtt/j;
System.out.println("共发送10个请求,成功接收"+j+"回复报文");
System.out.println("最小往返时间为:"+minRtt);
System.out.println("最大往返时间为:"+maxRtt);
System.out.println("平均往返时间为:"+avrRtt);
}
else{
System.out.println("发送请求失败!无回送报文");
}
//关闭线程
client.close();
}
public static void main(String[] args){
PingClient clientThread = new PingClient(args[0], Integer.valueOf(args[1]));
clientThread.start();
}
}
5.编译过程与截图
将PingServer.java文件和PingClient.java文件添加到指定目录中
使用cmd命令(注:使用的计算机需安装了java的JDK并且能够使用cmd 来编译Java程序)编译运行PingServer.java文件和PingClient.java文件过程如下:
(1)输入文件所在的硬盘,如:C:
(2)使用"cd 文件夹名"来打开文件所在的文件夹,如果文件直接存在硬盘之下,则转(3);如果文件的文件夹在另一个文件夹中,则多次输入"cd 文件夹名"直到到达文件所在的目录
(3)分别使用cmd来编译文件,cmd命令如下:
javac PingServer.java
javac PingClient.java
(4)编译成功后,先使用"java PingServer 端口号"(端口号是任意的,类型为整型)命令来运行服务器端;再使用"java PingClient 地址端口号"(地址使用本机地址,即:127.0.0.1;端口号要与服务器端口号相同)运行客户端。
服务器端运行截图:
客户端运行截图:
6.课程设计小结
我选择了编程实现基于UDP的PING作为这次的计算机网络课程设计的题目。
原本是打算写链路状态路由算法,看了书上的内容也找了一些资料,可是弄不太懂路由器之间的初始路由表是怎么获得的,于是就选了这个主题,比较明白易懂一点。
但是由于之前没有接触过网络的编程,并不了解网络Socket的使用,所以在编程的时候碰到一些瓶颈。
在设计客户端最多等待1秒接收服务器端返回的信息用的时间最久,本来想是不是应该在PingClient端使用wait()方法来停止时间,使用另一个线程来进行接收服务器端回送的数据,后来想到还有1秒钟这个限制。
想过很多种方法,也尝试过很多方法,一边学习一边编程,了解到DatagramSocket的receive方法是阻塞模式的,原本并不知道阻塞模式是什么。
后来才在网上看到DatagramSocket有SetSoTimeout这个方法可以在一段时间之后抛出一个异常来
终止receive的阻塞模式。
通过这次的课程设计,我了解了一点有关网络编程的基本知识,对ICMP有了更深刻的了解。