基于UDP协议的网络编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/carson0408/article/details/83867966

        UDP协议即用户数据报协议是一种面向无连接的协议,由于不需要建立连接,它的通信效率高,实时性好,同时可靠性相对于TCP协议较低。UDP协议的主要作用是完成网络数据流和数据报之间的转换--在信息的发送端,UDP协议将网络数据流封装成数据报,然后将数据报发送出去;在信息的接收端,UDP协议将数据报转换成实际数据内容。UDP在客户端和服务端之间没有虚拟链路,两端各建立一个Socket只是用于发送、接收数据报的对象。Java提供了DatagramSocket对象作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送、接收数据的数据报。

1.DatagramSocket类

         DatagramSocket不维护状态,不能产生IO流,它的唯一作用就是接收和发送数据报。通过构造器创建UDP的Socket。以下是DatagramSocket的构造器。

DatagramSocket():创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、本机所有可用端口中随机选择某个端口。

DatagramSocket(int port):创建一个DatagramSocket实例,并将该对象绑定在本机默认IP地址、指定端口。

DatagramSocket(int port,InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。

        DatagramSocket实例有两个方法:

receive(DatagramPacket p):从该DatagramSocket中接收数据报

send(DatagramPacket p):以该DatagramSocket向外发送数据报 

注意:由于DatagramSocket本身没有状态,所以不知道数据报发往何处,而是由DatagramPacket自身决定数据报发往何处。

2.DatagramPacket类 

         以下是DatagramPacket的构造器:

DatagramPacket(byte[] buf,int length):以一个空数组来创建DatagramPacket对象,该对象的作用是接收DatagramPacket中的数据。

DatagramPacket(byte[] buf,int length,InetAddress addr,int port):以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口,决定了该数据报的目的地。

DatagramPacket(byte[] buf,int offset,int length):以一个空数组来创建DatagramPacket对象,并指定接收到的数据放入buf数组中时从offset开始,最多放length个字节。

DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port):创建一个用于发送的DatagramPacket对象,指定发送buf数组中从offset开始,总共length个字节。

注意:在接收数据之前,应该采用上面的第一个或第三个构造器生成一个DatagramPacket对象,给出接收数据的字节数组及其长度。然后调用DatagramSocket的receive()方法等待数据报的到来,receive()将一直等待,直到收到一个数据报为止。在发送数据之前,调用第二个或第四个构造器创建DatagramPacket对象,此时的字节数组里存放了想发送的数据。除此之外,还要给出完整的目的地址。发送数据通过DatagramS的send()方法实现的。

3.示例

        这个例子主要是通过客户端控制台输入信息发送请求查询结果,而服务端接收到信息之后,响应结果并发送给客户端。这个过程中,DatagramPacket是传递信息的介质,并且数据报本身确定了目标地址。

服务端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;



public class UdpServer {
	public static final int PORT = 30000;
	//定义每个数据报大小为4KB
	private static final int DATA_LEN = 4096;
	//定义接收网络数组的字节数组
	byte[] inBuff = new byte[DATA_LEN];
	//以指定字节数组创建准备接收数据的DatagramPacket对象
	private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
	//定义一个用于发送的DatagramPacket对象
	private DatagramPacket outPacket;
	
	//定义一个map,接收到客户端查询字符之后,可在map中查找是否有相应结果
	HashMap<String, String> map = new HashMap<>();
	{
		map.put("最喜欢的水果", "苹果");
		map.put("最喜欢的蔬菜", "西蓝花");
		map.put("最不喜欢的蔬菜", "土豆");
		map.put("最不喜欢的水果", "榴莲");
		map.put("最喜欢的动物", "狗");
		map.put("最不喜欢的动物", "蛇");
		map.put("最喜欢的画家", "齐白石");
		map.put("最喜欢的城市", "杭州");
	}
	
	public void init() throws IOException {
		try(DatagramSocket socket = new DatagramSocket(PORT)){
			//采用循环接收数据
			for(int i=0;i<1000;i++) {
				//读取socket中的数据,读到的数据放入inPacket封装的数组里
				socket.receive(inPacket);
				//判断inPacket.getData()和inBuff是否是同一个数组
				System.out.println(inBuff==inPacket.getData());
				
				//将接收到的数据转换为字符串用于后续查找
				String string = new String(inBuff,0,inPacket.getLength());
				
				
				//从map中查找结果
				String anString = null;
				if(map.containsKey(string))
					anString ="服务端:"+map.get(string);
				else
					anString="服务端:您要找的信息不存在!";
				byte[] sendData = anString.getBytes();
				//创建发送DatagramPacket对象
				outPacket = new DatagramPacket(sendData, sendData.length, inPacket.getSocketAddress());
				socket.send(outPacket);
			}
		}
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		new UdpServer().init();

	}

}

客户端:

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




public class UdpClient {
	//定义发送数据报的目的地
	public static final int DEST_PORT = 30000;
	public static final String DEST_IP = "127.0.0.1";
	//定义每个数据报大小最大为4KB
	private static final int DATA_LEN = 4096;
	//定义接收网络数据的字节数组
	byte[] inBUff = new byte[DATA_LEN];
	//以指定的字节数组创建准备接收数据的DatagramP对象
	private DatagramPacket inPacket = new DatagramPacket(inBUff, inBUff.length);
	//定义一个用于发送的DatagramPacket对象
	private DatagramPacket outPacket = null;
	public void init() throws IOException {
		try(
			//创建一个客户端DatagramSocket,随机端口
			DatagramSocket socket = new DatagramSocket()){
			//初始化发送用的DatagramPacket,它包含一个长度为0的字节数组
			outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName(DEST_IP), DEST_PORT);
			//创建键盘输入流
			Scanner scan = new Scanner(System.in);
			//不断地读取键盘输入
			while(scan.hasNextLine()) {
				//将键盘输入的一行字符串转换成字节数组
				byte[] buff = scan.nextLine().getBytes();
				//设置发送用的DatagramPacket中的字节数据
				outPacket.setData(buff);
				socket.send(outPacket);
				
				//读取socket中的数据,读到的数据放在inPacket锁封装的字节数组中
				socket.receive(inPacket);
				System.out.println(new String(inBUff,0,inPacket.getLength()));
			}
		}
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		new UdpClient().init();

	}

}

运行结果:

注意:在基于UDP编程中,需要注意的是:

1.Socket没有状态,不知道数据报发往什么位置,仅提供receive、send两个方法

2.发送的数据报本身绑定了目标地址

3.服务端的DatagramSocket对象初始化时需要指定IP和端口,而客户端可以是随机端口

猜你喜欢

转载自blog.csdn.net/carson0408/article/details/83867966