海码充电站的技术专栏 java Coder

网络(四)--UDP

2019-01-18
watermelon


网络--UDP

前言

目前工作中使用socket大多数都是用于tcp,但是udp再一些场景中很重要,也更适合。

1:为什么

在网络质量令人不十分满意的环境下,UDP协议数据包丢失会比较严重。 但是由于UDP的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。 比如聊天用的ICQ和OICQ就是使用的UDP协议。

2:是什么

UDP( User Datagram Protocol )协议是用户数据报,在网络中它与TCP协议一样用于处理数据包。在OSI模型中,在第四层——传输层,处于IP协议的上一层。
UDP是一种面向无连接的传输方式,传输速度快,但是每次传输的大小不能超过64K。
在无连接的情况下,两个实体之间的通信不需要建立好一个连接,因此其下层的有关资源不需要事先进行预订的保留。
这些资源在数据传输时动态的进行分配。
无连接服务的另一个特征就是他不需要通信的两个实体同时是活跃的(即处于激活状态)。当发送端的实体正在进行发送时,它才是活跃的。
无连接服务的优点就是灵活方便并且比较迅速。但是无连接服务不能防止报文的丢失、重复或失序。无连接服务特别适合传输少量零星的报文。

3:怎么用

在java中操纵UDP使用JDK中java.net包下的DatagramSocket和DatagramPacket类,可以方便的控制用户数据报文。
DatagramPacket类将数据字节填充到UDP包中,这称为数据报。 DatagramSocket来发送这个包。
要接受数据,可以从DatagramSocket中接受一个 DatagramPack对象,然后从该包中读取数据的内容。

1:DatagramSocket类

创建接收和发送UDP的Socket实例

  • DatagramSocket():创建实例。通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。 
  • DatagramSocket(int port):创建实例,并固定监听Port端口的报文。 
  • DatagramSocket(int port, InetAddress localAddr):这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文

  • receive(DatagramPacket d):接收数据报文到d中。receive方法产生一个“阻塞”。 
  • send(DatagramPacket d):发送报文d到目的地。 
  • setSoTimeout(int timeout):设置超时时间,单位为毫秒。 
  • close():关闭DatagramSocket。在应用程序退出的时候,通常会主动释放资源,关闭Socket,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭Socket,或在捕获到异常抛出后关闭Sock

注意:1.在创建DatagramSocket类实例时,如果端口已经被使用,会产生一个SocketException的异常抛出,并导致程序非法终止,这个异常应该注意捕获。

“阻塞”是一个专业名词,它会产生一个内部循环,使程序暂停在这个地方,直到一个条件触发
2:DatagramPacket

用于处理报文,将byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成byte数组。  

  • DatagramPacket(byte[] buf, int length, InetAddress addr, int port):从buf数组中,取出length长的数据创建数据包对象,目标是addr地址,port端口。  
  • DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):从buf数组中,取出offset开始的、length长的数据创建数据包对象,目标是addr地址,port端口。 
  • DatagramPacket(byte[] buf, int offset, int length):将数据包中从offset开始、length长的数据装进buf数组。
  • DatagramPacket(byte[] buf, int length):将数据包中length长的数据装进buf数组。 
    getData():它从实例中取得报文的byte数组编码。 
3:代码示例
  • 接收端: ```java import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException;

public class Receiver {

public static void recive() {
	System.out.println("---recive---");
	// 接收端
	try {
		//创建接收方的套接字 对象  并与send方法中DatagramPacket的ip地址与端口号一致
		DatagramSocket socket = new DatagramSocket(9001,
				InetAddress.getByName("172.22.67.6"));
		//接收数据的buf数组并指定大小
		byte[] buf = new byte[1024];
		//创建接收数据包,存储在buf中
		DatagramPacket packet = new DatagramPacket(buf, buf.length);
		//接收操作
		socket.receive(packet);
		byte data[] = packet.getData();// 接收的数据
		InetAddress address = packet.getAddress();// 接收的地址
		System.out.println("接收的文本:::" + new String(data));
		System.out.println("接收的ip地址:::" + address.toString());
		System.out.println("接收的端口::" + packet.getPort()); // 9004
 
		// 告诉发送者 我接收完毕了
		String temp = "我接收完毕了";
		byte buffer[] = temp.getBytes();
		//创建数据报,指定发送给 发送者的socketaddress地址
		DatagramPacket packet2 = new DatagramPacket(buffer, buffer.length,
				packet.getSocketAddress());
		//发送
		socket.send(packet2);
		//关闭
		socket.close();
	} catch (SocketException e) {
		e.printStackTrace();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
 
public static void main(String[] args) {
	recive();
} } ```
  • 发送端 ```java

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

public class Sender {

// 发送者--->客户端 客户端--->发送者
// 发送者发给客户端数据,客户端返回数据给发送者
public static void send() {
	System.out.println("---send----");
	// 发送端
	try {
		// 创建发送方的套接字 对象 采用9004默认端口号
		DatagramSocket socket = new DatagramSocket(9004);
		// 发送的内容
		String text = "hi 红军!";
		byte[] buf = text.getBytes();
		// 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
		DatagramPacket packet = new DatagramPacket(buf, buf.length,
				InetAddress.getByName("172.22.67.6"), 9001);
		// 从此套接字发送数据报包
		socket.send(packet);
		// 接收,接收者返回的数据
		displayReciveInfo(socket);
		// 关闭此数据报套接字。
		socket.close();
	} catch (SocketException e) {
		e.printStackTrace();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
 
/**
 * 接收数据并打印出来
 * 
 * @param socket
 * @throws IOException
 */
public static void displayReciveInfo(DatagramSocket socket)
		throws IOException {
	byte[] buffer = new byte[1024];
	DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
	socket.receive(packet);
 
	byte data[] = packet.getData();// 接收的数据
	InetAddress address = packet.getAddress();// 接收的地址
	System.out.println("接收的文本:::" + new String(data));
	System.out.println("接收的ip地址:::" + address.toString());
	System.out.println("接收的端口::" + packet.getPort()); // 9004
}
 
public static void main(String[] args) {
	send();
} } ```

引用: 《java中的UDP总结》
《在Java中实现UDP协议编程(DatagramSocket/DatagramPacket)》


Comments

Content