计算机网络课设《基于UDP的Ping》

1. 服务器端代码 PingServer.java

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

/**
 * 
 * @author isWudn
 * 
 * *******************************服务器端******************************
 * 1、可以并发地为多个用户服务
 * 2、显示用户通过客户端发送来的消息内容(包含头部和payload)
 * 3、能够模拟分组的丢失、能够模拟分组传输延迟
 * 4、将用户发送来的请求request在延迟一段随机选择的时间(小于1s)后返回给客户端,作为收到请求的响应reply
 * 5、通过如下命令行启动服务器:java PingServer port。port为PingServer的工作端口号
 *
 */
public class PingServer{
	private int initPort;//监听的端口号
	private DatagramSocket socket;//服务器的socket
	private DatagramPacket packet;//从客户端收到的packet	
	private byte[] buf = new byte[1024];
	
	/**
	 * 构造器
	 * @param initPort服务器监听的端口号
	 */
	public PingServer(int initPort) {
		this.initPort = initPort;
	}
	
	/**
	 * 线程体
	 */
	public void run() {
		System.out.println("编程实现基于UDP的PING (Java)服务器端");
		System.out.println("----------PING SERVER STARTED---------");	
		try {
			//初始化socket,定义socket的端口号
			socket = new DatagramSocket(initPort);
		} catch (SocketException e) {
			//捕获到此异常一般是输入端口非法 或者 被占用
			System.out.println("Listening port" + initPort + "fail!");
			e.printStackTrace();
			//初始化端口号失败,终止程序
			System.exit(0);
		}
		
		//死循环,不断监听是否有报文请求
		while(true) {
			//获取客户端发来的报文
			packet = new DatagramPacket(buf,buf.length);
			try {
				//程序会停留在该语句,直到有新的请求连接产生
				socket.receive(packet);
			} catch (IOException e) {
				System.out.println("Request receive exception!");
				e.printStackTrace();
			}		
			//启动线程
			ThreadServer server = new ThreadServer(socket,packet);			
			server.start();
		}
	}
	
	public void destroy() {
		socket.close();//回收资源
	}

	public static void main(String[] args) {
		//初始化服务器
		PingServer ping  = new PingServer(Integer.valueOf(args[0]));
		//PingServer ping  = new PingServer(9999);
		ping.run();
	}
}

2. 服务器端线程代码 ThreadServer.java

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

/**
 * 
 * @author isWudn
 * 
 * *****************PingServer用来处理多用户请求的线程***********************
 * 2、显示用户通过客户端发送来的消息内容(包含头部和payload)
 * 3、能够模拟分组的丢失、能够模拟分组传输延迟
 * 4、将用户发送来的请求request在延迟一段随机选择的时间(小于1s)后返回给客户端,作为收到请求的响应reply
 * 
 */
public class ThreadServer extends Thread{
	private DatagramSocket socket;//接收和发送数据报
	private DatagramPacket packet;//数据报
	
	/**
	 * 构造器
	 * @param socket服务器的socket
	 * @param packet客户端发送过来的packet
	 */
	public ThreadServer(DatagramSocket socket, DatagramPacket packet) {
		//初始化socket和packet
		this.socket = socket;
		this.packet = packet;
	}
	
	public void run() {
		//定义延迟回送报文的随机时间,范围在0~1500之间,要比1000大才能延迟
		long randomTime = (long)(Math.random()*1500);
		
		/***********************接收报文****************************/
		String data = null;
		//如果随机时间大于1400,则认为分组丢失
		if(randomTime>1400) {
			data = "Receive message lost!";
			System.out.println(data);
		}else {
			//获取packet对象里封装的字符数组
			data = new String(packet.getData());
			System.out.println("Receiving a message:" + data);
		}
		/************************发送报文********************************/
		byte[] buffer = data.getBytes();
		//获取客户端地址
		InetAddress host = packet.getAddress();
		//获取客户端应用进程端口号
		int port = packet.getPort();
		//回送给客户端的报文
		DatagramPacket sendPacket = new DatagramPacket(buffer,buffer.length,host,port);
		//休眠0~1.5秒,模拟传输延迟
		try {
			sleep(randomTime);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			socket.send(sendPacket);
		} catch (IOException e) {
			System.out.println("The server failed to send a reply message!");
			e.printStackTrace();
		}
		
	}
}

3. 客户端代码 PingClient.java

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

/**
 * 
 * @author isWudn
 * 
 * ****************************客户端***********************************
 * 1、启动后发送10个request。每发送一个request后,最多等待1秒便接收PingServer返回的reply消息。
 * 	    如果在该时间内没有收到服务器的reply,则认为该请求或对该请求的reply已经丢失;
 * 	    在收到reply后立即发送下一个request。
 * 
 * 2、请求消息的payload中至少包含关键字PingUDP、序号、时间戳等内容。
 * 	    如:PingUDP SequenceNumber TimeStamp CRLF
 * 	    其中:CRLF表示回车换行符(0X0D0A);TimeStamp为发送该消息的机器时间。
 * 
 * 3、为每个请求计算折返时间(RTT),统计10个请求的平均RTT、最大/小RTT。
 * 
 * 4、通过如下命令行启动:java PingClient host port。
 * 	  host为PingServer所在的主机地址;port为PingServer的工作端口号
 *
 */

public class PingClient extends Thread{
	//客户端socket
	private DatagramSocket client;
	//服务器的IP地址
	private InetAddress hostAddress;
	//服务器的应用进程端口号
	private int port;
	//定义并初始化接收到的响应报文的个数
	private int replyNum = 0;
	//定义并初始化最小往返时间、最大往返时间、平均往返时间
	private long minRtt = 0, maxRtt = 0, averRtt = 0,sumRtt = 0;
	//每一个请求对应的rtt,默认初始化为0
	private long[] rtt = new long[10];
	
	/**
	 * 构造器
	 * @param host服务器地址
	 * @param port服务器端口
	 */
	public PingClient(String host, int port) {
		//有效端口的范围是0~65535
		if(port < 0 || port > 65535) {
			System.out.println("Invalid port number!");
			//如果端口号违法,终止程序
			System.exit(0);
		}
		this.port = port;
		try {
			//初始化客户端实例,将该实例绑定到本机默认的IP地址,本机所有可用端口中随机选择的某个端口
			client = new DatagramSocket();
			//根据服务器主机名称确定服务器的IP地址
			hostAddress = InetAddress.getByName(host);
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			System.out.println("Invalid host name!");
			e.printStackTrace();
		}	
	}
	
	/**
	 * 线程体
	 */
	public void run() {
		//定义时间戳格式
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-ddhh:mm:ss.SS");	
		System.out.println("Pinging " + hostAddress + ":");
		//模拟发送10条请求
		for(int i=0; i<10; i++) {
			//数据包装顺序:字符串-->字节数组-->packet报文
			/************************生成发送报文***************************/
			//发送报文前的时间
			Date sendTime = new Date();
			//请求数据
			String outMessage = "head:request " + i +"\n" 
								+ "payload:PingUDP SequenceNUmber:" + i 
								+ " TimeStamp:" + sdf.format(sendTime);		
			//将请求数据放进缓冲区内
			byte[] buffer = outMessage.getBytes();
			//生成发送报文实例
			DatagramPacket sendPacket = new DatagramPacket(buffer,buffer.length,hostAddress,port);
			
			/*************************生成接收报文******************************/
			byte[] buf = new byte[buffer.length];		
			DatagramPacket recievePacket = new DatagramPacket(buf,buf.length);
			
			/*****************************************************************/
			//接收到的响应信息
			String recieve = null;
			try {
				//发送到服务器端
				client.send(sendPacket);
				//接收响应报文
				//client.setSoTimeout(1000);//设置超时时间为1秒
				client.receive(recievePacket);
				recieve = new String(recievePacket.getData());
				//记录接收后的时间
				Date recieveTime = new Date();
				//计算往返时间
				rtt[i] = recieveTime.getTime()-sendTime.getTime();
			} catch (IOException e) {
				e.printStackTrace();
			}
			//如果接收时间大于1000ms,则认为请求丢失或者对请求的回复丢失
			if(rtt[i]>1000) {
				recieve = "head:request "+ i +"\n" 
						+ "Response message lost or Request timed out!";
			}else {
				recieve = recieve + "\n" + "rtt:" + rtt[i] +"ms";
			}
			System.out.println(recieve);			
		}
		
		minRtt = rtt[0];
		for(int i=0;i<10;i++) {
			if(rtt[i] > 1000)  continue;//请求失败不计算往返时间
			replyNum++;	
			//计算最小往返时间
			if(minRtt > rtt[i]) {
				minRtt = rtt[i];
			}
			//计算最大往返时间
			if(maxRtt < rtt[i]) {
				maxRtt = rtt[i];
			}
			//计算总往返时间
			sumRtt += rtt[i];
		}
		if(replyNum!=0) {
			//计算平均往返时间
			averRtt = sumRtt/replyNum;
			System.out.println("Ping statistics for " + hostAddress + ":");
			System.out.println("	Packets: Sent=10, Received=" + replyNum +", Lost=" + (10-replyNum));
			System.out.println("Approximate round trip times in milli-seconds:");
			System.out.println("	minRTT:" + minRtt + "ms, maxRTT:" + maxRtt + "ms, averRTT:" + averRtt + "ms");
		}else {
			System.out.println("Failed to send request! Unable to return message!");
		}
		//关闭线程
		client.close();
	}
	
	public static void main(String[] args) {
		PingClient clientThread = new PingClient(args[0], Integer.valueOf(args[1]));
		//PingClient clientThread = new PingClient("127.0.0.1", 9999);
		clientThread.start();
	}

}

4. 在运行时可能遇上的问题及其解决方法

cmd错误:找不到或无法加载主类 和 错误:找不到符号

  • 8
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值