TCP/IP系列之传输层UDP协议

UDP是一个简单的面向数据报的传输层协议:进程的每个输出操作都正好产生一个UDP
数据报,并组装成一份待发送的IP数据报.IP数据报的数据部分放的是UDP数据报.如下图

UDP数据包的格式如下图:

一个Wireshark抓取的包如下图:

UDP长度字段指的是UDP首部和UDP数据的字节长度.该字段的最小值为8字节
(发送一份0字节的UDP数据报是OK).UDP检验和覆盖UDP首部和UDP数据,检验和是16bit字的
二进制反码和,UDP数据报的长度可以为奇数字节,但是检验和算法是把若干个16bit字相加.
解决方法是必要时在最后增加填充字节0,这只是为了检验和的计算(也就是说,可能增加的
填充字节不被传送).其目的是为了发现UDP首部和数据在发送端到接收端之间发生的任何改动.

物理网络层一般要限制每次发送数据帧的最大长度.任何
时候I P层接收到一份要发送的I P数据报时,它要判断向本地哪个接口发送数据(选路),并查
询该接口获得其MTU.I P把MTU与数据报长度进行比较,如果需要则进行分片.分片可以发
生在原始发送端主机上,也可以发生在中间路由器上.
把一份I P数据报分片以后,只有到达目的地才进行重新组装(这里的重新组装与其他网
络协议不同,它们要求在下一站就进行进行重新组装,而不是在最终的目的地).重新组装由
目的端的I P层来完成,其目的是使分片和重新组装过程对运输层(TCP和UDP)是透明的.
对于发送端发送的每份IP数据报来说,其标识字段都包含一个唯一值.该值在数据报分片时被复制
到每个片中(我们现在已经看到这个字段的用途).标志字段用其中一个比特来表示"更多的片".
除了最后一片外,其他每个组成数据报的片都要把该比特置1.

在Java中,java.util.DatagramSocket负责接收和发送UDP数据报,
java.util.DatagramPacket表示UDP数据报。每个
DatagramSocket与一个本地地址(包括本地主机的IP地址和本地UDP端
口)绑定,每个DatagramSocket可以把UDP数据报发送给任意一个远程
DatagramSocket,也可以接收来自任意一个远程DatagramSocket的UDP
数据报。在UDP数据报中包含了目的地址的信息,DatagramSocket根据
该信息把数据报发送到目的地。

UDP协议是无连接的协议,客户端的DatagramSocket与服务器端的DatagramSocket
不存在一一对应关系,两者无需建立连接,就能交换数据报。
DatagramSocket负责接收和发送数据报。
每个DatagramSocket对象都会与一个本地端口绑定,在此端口监听发送过来的数据报。
在客户程序中,一般由操作系统为DatagramSocket分配本地端口,这种端口也称为匿名端口;
sendSocket = new DatagramSocket();
在服务器程序中,一般由程序显式的为DatagramSocket指定本地端口。
receiveSocket = new DatagramSocket(port);
DatagramSocket提供了接收和发送数据报的方法
public void send(DatagramPacket src)throws IOException //发送数据报:
public void receive(DatagramPacket dst)throws IOException //接收数据报
receive在网络上没有数据报时,执行该方法的线程会进入阻塞状态,直到收到数据报为止。

DatagramPacket表示数据报,它的构造方法可以分为两类:
一类构造方法创建的DatagramPacket对象用来接收数据
还有一类构造方法创建的DatagramPacket对象用来发送数据。
两类构造方法的主要区别是,用于发送数据的构造方法需要设定数据报到达的目的地址,
byte[] bigData = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bigData, 0, bigData.length,
InetAddress.getByName(targetServer), port);
而用于接收数据的构造方法无需设定地址。
byte[] receiveByte = new byte[MAX_LENGTH];
DatagramPacket packet = new DatagramPacket(receiveByte, 0, MAX_LENGTH);
DatagramPacket的构造方法有一个参数length,它决定了要接收或发送的数据报的长度。
对于用于接收数据的DatagramPacket,如果实际接收到的数据报的长度大于DatagramPacket的长度,
那么多余的数据就会被丢弃。因此,必须为DatagramPacket选择合适的长度。
选择数据报大小的通用原则是:
如果网络非常不可靠,如分组无线电网络,则要选择较小的数据报,以减少传输中遭破坏的可能性。
如果网络非常可靠,而且传输速度很快,就应当尽可能使用大的数据报。
对于多数网络,8K是一个很好的折衷方案。
数据报中只能存放字节形式的数据。在发送方,需要把其他格式的数据转换为字节序列。
在接收方,需要把字节序列转换为原来格式的数据。
同一个DatagramPacket对象可以被重用,用来多次发送或接收数据。

值得注意的是,UDP协议提供不可靠的传输,如果数据报没有到达目的地,
send()方法不会抛出任何异常,因此发送方程序无法知道数据报是否被接收方接收到,
除非双方通过应用层的特定协议来确保接收方未收到数据报时,发送方能重发数据报。
一个完整的JAVA代码示例
public class DatagramTester {
	public DatagramTester(String tgtIP,int port) throws IOException {		
		Receiver receiver = new Receiver(port);
		Sender sender = new Sender(tgtIP,port);
		
		receiver.start();
		sender.start();
	}
	public static void main(String args[]) throws IOException {
		args[0] = "147.151.240.234";
		DatagramTester tester = new DatagramTester(args[0],8005);
	}
}

class Sender extends Thread { 
	private DatagramSocket sendSocket;
	
	private String targetServer;//"147.151.240.234";	
	private int port;	
	public Sender(String serverIP,int port) throws SocketException{
		sendSocket = new DatagramSocket(); 
		this.targetServer = serverIP;
		this.port = port;		
	}
	
	public void run() {
		try {
			send(); 
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void send() throws IOException {		
		BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
		String msg = null;
		while ((msg = localReader.readLine()) != null) {
			byte[] bigData = msg.getBytes();			
			DatagramPacket packet = new DatagramPacket(bigData, 0, bigData.length, 
					InetAddress.getByName(targetServer), port);
			sendSocket.send(packet);		
			if (msg.equals("bye"))
				break;
		}
		
	}
}
class Receiver extends Thread { 
	private static final int MAX_LENGTH = 4584;
	private DatagramSocket receiveSocket;	
	public Receiver(int port) throws SocketException{		
		this.receiveSocket = new DatagramSocket(port);
	}
	public void run() {
		try {
			while(true){
				System.out.print(receive());
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private String receive() throws IOException {
		byte[] receiveByte = new byte[MAX_LENGTH];		
		DatagramPacket packet = new DatagramPacket(receiveByte, 0, MAX_LENGTH);		
		receiveSocket.receive(packet);				
		int bytesReceived = packet.getLength();		
		System.out.println("ReceiveSocket> received " + packet.getLength() + " bytes");				
		return new String(receiveByte, 0, bytesReceived);
		
	}
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值