UDPping程序

UDPping程序

要求:

在这个编程作业中,你将用 Java 编写一个客户ping程序。该客户将发送一个简单的ping报文,接收一个从服务器返回的对应pong报文,并确定从该客户发送ping报文到接收到pong报文为止的时延。 该时延称为往返时延(RTT)。由该客户和服务器提供的功能类似于在现代操作系统中可用的标准ping程序。然而,标准的ping使用互联网控制报文协议(ICMP)(我们将在第5章中学习ICMP). 此时我们将创建一个非标准(但简单)的基于UDP的ping程序

你的ping程序经UDP向目标服务器发送10个ping报文。对于每个报文,当对应的pong报文返回时,你的客户要确定和打印RTT。因为UDP是一个不可靠的协议,由客户发送的分组可能会丢失。为此,客户不能无限期地等待对ping报文的回答。客户等待服务器回答的时间至多为1秒;如果没有收到回答,客户假定该分组丢失并相应地打印一条报文。

在此作业中,你将给出服务器的完整代码(在配套网站中可找到)。你的任务是编写客户代码,该代码与服务器代码非常类似。建议你先仔细学习服务器的代码,然后编写你的客户代码,可以随意地从服务器代码中剪贴代码行。

电脑内置的ping程序示例:

梳理题目之后,结合电脑中的ping程序示例,我们给出UDPServer的功能和UDPClient的功能

Server

  • 显示用户通过客户端发送来的消息内容(包含头部和payload)
  • 能够模拟分组的丢失;能够模拟分组传输延迟
  • 将Client发送来的请求在延迟一段随机选择的时间(小于1s)后返回给客户端,作为收到请求的响应reply;

Client

  • 启动后发送10个request。发送一个request后,最多等待1秒以便接收server返回的消息。如果在1s内没有收到服务器的reply,则认为该请求或对该请求的reply已经丢失
  • 在收到reply后立即发送下一个请求。
  • 请求消息的payload中包含关键字PingUDP、序号、时间戳这些内容
  • 计算传包总数,收到包的总数以及丢包率
  • 为每个请求计算折返时间(RTT),统计10个请求的min/avg/maxRTT。

代码

我把这个项目写成了两个project:pingserver和pingclient,分别启动,更加直观一些

pingserver

PingServer类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.company;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Scanner;


public class PingServer extends Thread{

private int intiPort;
private DatagramSocket serverSocket;//服务器码头
private DatagramPacket receivePacket;//接受数据分组
private byte[] buffer=new byte[1024];//数据缓冲区
Scanner scanner;
//初始化端口号
public PingServer(int intiPort) {
this.intiPort=intiPort;
}
public PingServer() {
}

public void run(){
System.out.println("基于udp实现ping");
System.out.println("------PING SERVER------");
System.out.println("请输入监听端口号");
scanner=new Scanner(System.in);
this.intiPort=scanner.nextInt();


try {
//根据输入的端口生成server端socket实例
serverSocket=new DatagramSocket(intiPort);// 默认绑定本主机
} catch (SocketException e) {
CloseUtil.closeAll(serverSocket,scanner);
System.out.println("监听端口:"+intiPort+"启动失败。请从新输入端口号");
e.printStackTrace();
System.exit(0);//终止程序
}


//监听是否有请求数据
while(true){
receivePacket=new DatagramPacket(buffer, buffer.length);//空的数据包接受分组
//监听是否有用户请求
try {
serverSocket.receive(receivePacket);
} catch (IOException e) {
System.out.println("分组异常");
e.printStackTrace();
}
//如果捕捉到了client发送的请求,那就生成一个新的服务线程来处理数据包
new ServerThread(serverSocket,receivePacket).start();
}
}


}

ServerThread类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.company;


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class ServerThread extends Thread{
private DatagramSocket serverSocket;

private DatagramPacket receivePacket;

public ServerThread(DatagramSocket serverSocket,
DatagramPacket receivePacket) {
this.serverSocket = serverSocket;
this.receivePacket = receivePacket;
}

@Override
public void run() {
byte[] buffer=new byte[1024];//数据缓冲区

long randomTime=(long)(Math.random()*1500);//模拟延迟时间

String sentence;//接受到的数据

try {
sleep(randomTime);//休眠,模拟延迟
} catch (InterruptedException e) {
System.out.println("--模拟延时错误--");
e.printStackTrace();
}

if(randomTime>1000){//如果延迟大于1秒,数据丢失
sentence="Date lose";
}else{
sentence=new String(receivePacket.getData());
}

InetAddress host=receivePacket.getAddress();//获取ip地址

int port=receivePacket.getPort();//端口号

buffer=sentence.getBytes();//数据—>字节数组,用于发回客户端

DatagramPacket sendPacket=new DatagramPacket(buffer, buffer.length
,host,port); //生成数据包,有了目的地址和端口号

try {
serverSocket.send(sendPacket);
} catch (IOException e) {
CloseUtil.closeAll(serverSocket);
e.printStackTrace();
}
System.out.println(sentence);//显示请求结果
}

}

pingclient

PingClient类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.company;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class PingClient {
public static void start() throws IOException {
System.out.println("基于udp实现ping");
System.out.println("----PING CLIENT------");
Scanner scanner=new Scanner(System.in);
System.out.println("请输入主机号");
String host=scanner.nextLine();
System.out.println("请输入端口号");
int port=scanner.nextInt();
byte[] buffer;//数据包缓冲区
long[] rtt=new long[10];//用于存储10个rtt

for(int i=1;i<=10;i++){//模拟发送10个请求
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-ddhh:mm:ss.SS");//时间格式

Date sendBefore=new Date();//当前时间
String sentence="head:request "+i+" playload:UDP-Number: "
+i+" TimeStamp:"+sdf.format(sendBefore);//请求数据
DatagramSocket clientSocket=new DatagramSocket();

InetAddress IP=InetAddress.getByName(host);


buffer=sentence.getBytes();//数据打包成字节数组

DatagramPacket sendPacket=new DatagramPacket(buffer, buffer.length,IP,port);//字节数组->分组

clientSocket.send(sendPacket);//发送分组

DatagramPacket receivePacket=new DatagramPacket(buffer, buffer.length);//接受分组

try {
clientSocket.receive(receivePacket);
} catch (IOException e) {
System.out.println("分组接受异常");
e.printStackTrace();
}
//数据->字符串
String receiveSentence=new String(receivePacket.getData());

Date receiveAfter=new Date();//接收时间

rtt[i-1]=receiveAfter.getTime()-sendBefore.getTime();//计算rtt

if(rtt[i-1]>=1000){
rtt[i-1]=1000;
System.out.println(sentence+" Data Lose");
}else{

System.out.print(receiveSentence);//显示返回的数据
System.out.println(" time="+rtt[i-1]+" ms");//显示往返时间
}

clientSocket.close();
}
//进行统计
dealRtt(rtt);
CloseUtil.closeAll(scanner);
}


private static void dealRtt(long[] rtt) {
long sumRtt=0;
long maxRtt=0;
long minRtt=rtt[0];
byte loss=0;
for(int i=0;i<10;i++){
if(rtt[i]>=1000)
loss++;
if(rtt[i]>maxRtt)
maxRtt=rtt[i];
if(rtt[i]<minRtt)
minRtt=rtt[i];
sumRtt+=rtt[i];
}
byte received = (byte) (10-loss);
double rate = (double)loss/10;
System.out.println("--- UDPServer ping statistics ---");
System.out.println("10 packets transimitted, "+received+" packets received, "+ rate+"% packet loss.");
System.out.println("round-trip min/avg/max = "+minRtt+"/"+sumRtt/10+"/"+maxRtt+ " ms");
}
}

CloseUtil

这个小组件是两个project都的,作用是用来关闭IO流的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.company;

import java.io.Closeable;
import java.io.IOException;

public class CloseUtil {

public static void closeAll(Closeable...io) {
for(Closeable temp:io){
try {
temp.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

效果如下图所示:

-------------本文结束,感谢您的阅读-------------