4编程实现基于UDP的PING.docx
《4编程实现基于UDP的PING.docx》由会员分享,可在线阅读,更多相关《4编程实现基于UDP的PING.docx(15页珍藏版)》请在冰豆网上搜索。
4编程实现基于UDP的PING
计算机网络课程设计
课程名称计算机网络课程设计
学院计算机学院
专业班级
学号
学生姓名
指导教师梁路
年月日
计算机网络课程设计任务书
设计题目
编程实现基于UDP的PING(Java)
已知技术参数和设计要求
1.编程实现PING的服务器端和客户端,实现操作系统提供的ping命令的类似功能。
2.服务器端PingServer功能:
2.1可以并发地为多个用户服务;
2.2显示用户通过客户端发送来的消息内容(包含头部和payload);
2.3能够模拟分组的丢失;能够模拟分组传输延迟;
2.4将用户发送来的请求request在延迟一段随机选择的时间(小于1s)后返回给客户端,作为收到请求的响应reply;
2.5通过如下命令行启动服务器:
javaPingServerport。
port为PingServer的工作端口号
3.客户端PingClient功能:
3.1启动后发送10个request。
发送一个request后,最多等待1秒以便接收PingServer返回的reply消息。
如果在该时间内没有收到服务器的reply,则认为该请求或对该请求的reply已经丢失;在收到reply后立即发送下一个request。
3.2请求消息的payload中至少包含关键字PingUDP、序号、时间戳等内容。
如:
PingUDPSequenceNumberTimeStampCRLF
其中:
CRLF表示回车换行符(0X0D0A);TimeStamp为发送该消息的机器时间。
3.3为每个请求计算折返时间(RTT),统计10个请求的平均RTT、最大/小RTT。
3.4通过如下命令行启动:
javaPingClienthostport。
host为PingServer所在的主机地址;port为PingServer的工作端口号
设计内容与步骤
1.学习ICMP,了解ping命令的工作机理;
2.学习JavaUDPSocket通信机制;
3.了解Java多线程程序设计;
4.服务器PingServer程序设计;
5.客户端PingClient程序设计。
6.调试与演示
设计工作计划与进度安排
1.ping命令工作机制学习2小时
2.JavaUDPSocket通信机制2小时
3.Java多线程程序设计4小时
4.PingServer程序设计6小时
5.PingClient程序设计12小时
6.调试与演示4小时
6.课程设计说明书10小时
目录:
1.基础知识:
1.1.ICMP
ICMP是(InternetControlMessageProtocol)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是UserDatagramProtocol的简称,中文全称是用户数据包协议,是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
在网络中它与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(longmillis)方法
线程等待:
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.线程代码:
/**
*
*/
ion;
/**
*线程
*@authorLingHuacai
*
*/
publicclassThreadServerextendsThread{
privateDatagramSocketsocket;
privateDatagramPacketpacket;
publicThreadServer(DatagramSocketsocket,DatagramPacketpacket){
//初始化socket和packet
this.socket=socket;
this.packet=packet;
}
publicvoidrun(){
//定义延迟回送报文的随机时间
longrandomTime=(long)(Math.random()*1500);
Stringdata=newString(packet.getData());
byte[]buffer=data.getBytes();
//获取客户端地址
InetAddresshost=packet.getAddress();
//获取客户端应用进程端口号
intport=packet.getPort();
//回送给客户端的报文
DatagramPacketsend=newDatagramPacket(buffer,buffer.length,host,port);
try{
sleep(randomTime);
}catch(InterruptedExceptione){
e.printStackTrace();
}
try{
//回送报文
socket.send(send);
}catch(IOExceptionee){
ee.printStackTrace();
}
}
}
4.2.服务器端代码:
/**
*
*/
packageudp;
import
import
import
importjava.net.SocketException;
/**
*服务器端
*@authorLingHuacai
*
*/
publicclassPingServerextendsThread{
//监听的端口号
privateintinitPort;
privatebyte[]buf=newbyte[32];
privateDatagramPacketpacket;
privateDatagramSocketsocket;
publicPingServer(intinitPort){
this.initPort=initPort;
}
publicvoidrun(){
try{
//初始化socket,定义socket的端口号
socket=newDatagramSocket(initPort);
System.out.println("Serverstarted");
}catch(SocketExceptione){
System.out.println("监听端口"+initPort+"失败");
e.printStackTrace();
//初始化端口号失败,终止程序
System.exit(0);
}
//记录当前时间
longstartTime=System.currentTimeMillis();
//死循环,不断监听是否有报文请求
while(true){
try{
packet=newDatagramPacket(buf,buf.length);
//获取客户端发来的报文
socket.receive(packet);
}catch(IOExceptione){
e.printStackTrace();
}
//输出分组和服务器启动时间的差值
System.out.println("接收到这个分组的的时间与服务器启动时间差:
"+(System.currentTimeMillis()-startTime)+"毫秒!
");
if(5000>(System.currentTimeMillis()-startTime)||
(System.currentTimeMillis()-startTime)>5500){
ThreadServerserver=newThreadServer(socket,packet);
//启动线程
server.start();
}
}
}
publicvoiddestroy(){
socket.close();
}
publicstaticvoidmain(String[]args){
//初始化服务器
PingServerping=newPingServer(Integer.valueOf(args[0]));
ping.start();
}
}
4.3.客户端代码:
/**
*客户端
*@authorLingHuacai
*
*/
publicclassPingClientextendsThread{
privateDatagramSocketclient;
privateInetAddresshostAddress;
intport;
//定义并初始化接收到的响应报文的个数
intj=0;
//定义并初始化最小往返时间,最大往返时间,平均往返时间
longminRtt=0,maxRtt=0,avrRtt=0,sumRtt=0;
long[]rtt=newlong[10];
publicPingClient(Stringhost,intport){
this.port=port;
try{
client=newDatagramSocket();
//获取客户端地址
hostAddress=InetAddress.getByName(host);
}catch(UnknownHostExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(SocketExceptionee){
ee.printStackTrace();
}
}
publicvoidrun(){
//定义时间戳格式
SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddhh:
mm:
ss.SS");
for(inti=0;i<10;i++){
rtt[i]=0;
}
for(inti=0;i<10;i++){
//发送报文中的时间
DatesendTime=newDate();
StringoutMessage=newString("PingUDP:
"+i+","+sdf.format(sendTime));
Stringrecieve=null;
byte[]buffer=outMessage.getBytes();
byte[]buf=newbyte[buffer.length];
DatagramPacketsendPacket=newDatagramPacket(buffer,buffer.length,hostAddress,port);
DatagramPacketrecievePacket=newDatagramPacket(buf,buf.length);
try{
client.send(sendPacket);
//设置超时时间为1秒
client.setSoTimeout(1000);
//接收响应报文
client.receive(recievePacket);
recieve=newString(recievePacket.getData());
//判断是否为延时报文,若为延时报文则丢弃,继续接收报文
while(!
recieve.equals(outMessage)){
recieve=null;
client.receive(recievePacket);
recieve=newString(recievePacket.getData());
}
//记录接收后时间
DaterecieveTime=newDate();
//计算往返时间
rtt[i]=recieveTime.getTime()-sendTime.getTime();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
if(recieve!
=null){
}
}
if(rtt[0]!
=0){
minRtt=rtt[0];
maxRtt=rtt[0];
sumRtt=rtt[0];
j++;
}
elseminRtt=2000;
for(inti=1;i<10;i++){
if(rtt[i]!
=0){
j++;
//计算最小往返时间
if(minRtt>rtt[i]){
minRtt=rtt[i];
}
//最大往返时间
if(maxRttmaxRtt=rtt[i];
}
//总往返时间
sumRtt=sumRtt+rtt[i];
}
}
if(j!
=0){
//计算平均往返时间
avrRtt=sumRtt/j;
}
else{
}
//关闭线程
client.close();
}
publicstaticvoidmain(String[]args){
PingClientclientThread=newPingClient(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命令如下:
javacPingServer.java
javacPingClient.java
服务器端运行截图:
客户端运行截图:
6.课程设计小结
我选择了编程实现基于UDP的PING作为这次的计算机网络课程设计的题目。
原本是打算写链路状态路由算法,看了书上的内容也找了一些资料,可是弄不太懂路由器之间的初始路由表是怎么获得的,于是就选了这个主题,比较明白易懂一点。
但是由于之前没有接触过网络的编程,并不了解网络Socket的使用,所以在编程的时候碰到一些瓶颈。
在设计客户端最多等待1秒接收服务器端返回的信息用的时间最久,本来想是不是应该在PingClient端使用wait()方法来停止时间,使用另一个线程来进行接收服务器端回送的数据,后来想到还有1秒钟这个限制。
想过很多种方法,也尝试过很多方法,一边学习一边编程,了解到DatagramSocket的receive方法是阻塞模式的,原本并不知道阻塞模式是什么。
后来才在网上看到DatagramSocket有SetSoTimeout这个方法可以在一段时间之后抛出一个异常来终止receive的阻塞模式。
通过这次的课程设计,我了解了一点有关网络编程的基本知识,对ICMP有了更深刻的了解。