UDP协议格式以及在java中的使用

UDP是面向无连接的通讯协议,由于通讯不需要连接,所以可以实现广播发送。UDP通讯时不需要接收方确认,属于不可靠的传输,可能会出现丢包现象,实际应用中要求程序员编程验证。

UDP适用于DNS、视频音频等多媒体通信、广播通信(广播、多播)。例如我们常用的QQ,就是一个以UDP为主,TCP为辅的通讯协议。

UDP报文格式如下:


UDP首部有8个字节,由4个字段构成,每个字段都是两个字节,

  1. 源端口:数据发送方的端口号.
  2. 目的端口:数据接收方的端口号。
  3. 长度:UDP数据报的整个长度(包括首部和数据),其最小值为8(只有首部)。
  4. 校验和:检测UDP数据报在传输中是否有错,有错则丢弃。

可以使用nc发送UDP数据包:echo hello | nc -uv 127.0.0.1 9999

用tcpdump抓取到的数据包如下(注意先运行tcpdump,然后再执行nc命令):

# tcpdump -i lo -X udp port 9999
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
11:19:39.267912 IP localhost.45666 > localhost.distinct: UDP, length 60x0000:  4500 0022 5914 4000 4011 e3b4 7f00 0001  E.."Y.@.@.......0x0010:  7f00 0001 b262 270f 000e fe21 6865 6c6c  .....b'....!hell0x0020:  6f0a                                     o.
... ...

说明:

  • 源端口:0xb262,十进制的45666。
  • 目的端口:0x270f,十进制的9999。
  • 长度:0x000e,14个字节的报文长度。
  • 校验和:0xfe21。

bio之单播

单播就是一对一通信。

服务器端代码如下:

package com.morris.udp.bio.single;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;public class Server {public static void main(String[] args) throws IOException {DatagramSocket datagramSocket = new DatagramSocket(9999);byte[] bytes = new byte[1024];DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);datagramSocket.receive(datagramPacket);System.out.println("receive from client: " + new String(bytes));byte[] req = "hello client".getBytes();DatagramPacket resp = new DatagramPacket(req, req.length, datagramPacket.getSocketAddress());datagramSocket.send(resp);}
}

客户端代码如下:

package com.morris.udp.bio.single;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;public class Client {public static void main(String[] args) throws IOException {DatagramSocket datagramSocket = new DatagramSocket();byte[] req = "hello server".getBytes();DatagramPacket datagramPacket = new DatagramPacket(req, req.length, new InetSocketAddress("127.0.0.1", 9999));datagramSocket.send(datagramPacket);datagramSocket.receive(datagramPacket);System.out.println("receive from server: " + new String(datagramPacket.getData()));}
}

客户端和服务端的代码几乎一致,只不过接收和发送数据的顺序不一致,receive和send都欧式阻塞方法。

bio之广播

广播:同一网段所有主机都能接收,前提是端口要开启监听。

只需要将单播的例子中客户端发送数据的IP修改为255.255.255.255即可,具体修改如下:

DatagramPacket datagramPacket = new DatagramPacket(req, req.length, new InetSocketAddress("255.255.255.255", 9999));

bio之多播(组播)

多播数据报套接字类用于发送和接收IP多播包。MulticastSocket是一种DatagramSocket,它具有加入Internet上其他多播主机的“组”的附加功能。

多播组通过D类IP地址和标准UDP端口号指定。D类IP地址在224.0.0.0和239.255.255.255的范围内。地址224.0.0.0被保留,不应使用。

可以通过首先使用所需端口创建MulticastSocket,然后调用joinGroup(InetAddress groupAddr)方法来加入多播组。

服务器端代码如下:

package com.morris.udp.bio.multicast;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;public class Server {public static void main(String[] args) throws IOException {InetAddress group = InetAddress.getByName("228.5.6.7");MulticastSocket s = new MulticastSocket(6789);s.joinGroup(group);byte[] buf = new byte[1000];DatagramPacket recv = new DatagramPacket(buf, buf.length);s.receive(recv);System.out.println("receive : " + new String(buf));s.leaveGroup(group);}
}

客户端代码如下:

package com.morris.udp.bio.multicast;import java.io.IOException;
import java.net.*;public class Client {public static void main(String[] args) throws IOException {String msg = "Hello";InetAddress group = InetAddress.getByName("228.5.6.7");MulticastSocket s = new MulticastSocket();s.joinGroup(group);DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), group, 6789);s.send(hi);s.leaveGroup(group);}
}

NIO实现单播

服务器端代码如下:

package com.morris.udp.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;public class Server {public static void main(String[] args) throws IOException {DatagramChannel datagramChannel = DatagramChannel.open();datagramChannel.bind(new InetSocketAddress(9999));// datagramChannel.configureBlocking(false);ByteBuffer byteBuffer = ByteBuffer.allocate(128);SocketAddress receive = datagramChannel.receive(byteBuffer);byteBuffer.flip();byte[] bytes = new byte[byteBuffer.remaining()];byteBuffer.get(bytes);System.out.println("receive from client: " + new String(bytes));byteBuffer.clear();byteBuffer.put("hello client".getBytes());datagramChannel.send(byteBuffer, receive);}
}

客户端代码如下:

package com.morris.udp.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;public class Client {public static void main(String[] args) throws IOException {DatagramChannel datagramChannel = DatagramChannel.open();// datagramChannel.configureBlocking(false);String req = "hello server";ByteBuffer byteBuffer = ByteBuffer.allocate(req.length());byteBuffer.put(req.getBytes());byteBuffer.flip();datagramChannel.send(byteBuffer, new InetSocketAddress("127.0.0.1", 9999));datagramChannel.receive(byteBuffer);byteBuffer.flip();byte[] bytes = new byte[byteBuffer.remaining()];byteBuffer.get(bytes);System.out.println("receive from server: " + new String(bytes));}
}

Netty实现单播

服务器端代码如下:

package com.morris.udp.netty.single;import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;public class Server {private static final int port = 8899;public static void main(String[] args) throws InterruptedException {NioEventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioDatagramChannel.class).handler(new SimpleChannelInboundHandler<DatagramPacket>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {// 接收数据System.out.println(msg.content().toString(CharsetUtil.UTF_8));// 发送数据ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("hello client", CharsetUtil.UTF_8), msg.sender()));ctx.close();}});bootstrap.bind(port).sync().channel().closeFuture().await();} finally {group.shutdownGracefully();}}
}

客户端代码如下:

package com.morris.udp.netty.single;import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;import java.net.InetSocketAddress;public class Client {public static void main(String[] args) throws InterruptedException {NioEventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioDatagramChannel.class).handler(new SimpleChannelInboundHandler<DatagramPacket>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {// 接收数据System.out.println(msg.content().toString(CharsetUtil.UTF_8));ctx.close();}});Channel channel = bootstrap.bind(0).sync().channel();// 发送数据channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("hello server", CharsetUtil.UTF_8), new InetSocketAddress("127.0.0.1", 8899)));if (!channel.closeFuture().await(30 * 1000)) {System.err.println("查询超时");}} finally {group.shutdownGracefully();}}
}

Netty实现广播

只需要将netty实现的单播的客户端代码做如下修改:

  1. 增加option:
.option(ChannelOption.SO_BROADCAST, true)
  1. 将IP地址修改为广播地址255.255.255.255
channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("hello server", CharsetUtil.UTF_8), new InetSocketAddress("255.255.255.255", 8899)));

底层实现

recvfrom负责接收UDP数据,其函数声明如下:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

sendto负责发送UDP数据,其函数声明如下:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

启动服务器端服务Server:

# strace -ff -o out java Server

然后使用nc命令充当客户端进行连接:echo hello | nc -uv 127.0.0.1 9999

产生的系统调用中关键信息如下:

socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP) = 4
bind(4, {sa_family=AF_INET6, sin6_port=htons(9999), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0
recvfrom(4, "hello\n", 1024, 0, {sa_family=AF_INET6, sin6_port=htons(7361), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 6
write(1, "receive from client: hello\n\0\0\0\0\0"..., 1045) = 1045
write(1, "\n", 1)
sendto(4, "hello client", 12, 0, {sa_family=AF_INET6, sin6_port=htons(7361), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 12

可见发送和接收数据确实使用了上面的系统调用,另外上面的系统调用中并没有listen函数,不需要监听端口,再次验证UDP是面向无连接的。

【转】UDP协议格式以及在java中的使用相关推荐

  1. UDP协议详解(UDP协议特点,UDP协议格式、UDP的应用)

    目录 前言 1.UDP协议的特点 2. UDP协议格式的特点 3. UDP的应用 前言 TCP和UDP协议都是传输层的协议,其中传输层是负责端对端之间的连接,端是指端点. 端口的划分和知名端口 0~1 ...

  2. TCP与UDP协议初步学习——网络环境中分布式进程通信的基本概念

    TCP与UDP协议初步学习--网络环境中分布式进程通信的基本概念 一.单机系统中进程通信方法 进程和进程通信是操作系统中最基本的概念,首先通过回忆操作系统课程中,关于单击系统中进程和进程通信的问题描述 ...

  3. 下面是以十六进制格式存储的一个udp首部_【干货】论UDP协议,在现网中的不可缺少!...

    每一个网工应该都知道TCP.UDP协议.UDP是用户数据报文协议,属于OSI模型中的传输层.它是一种无连接的协议,也就说上一报文和下一报文在协议层没有任何联系,同时提供了简单的不可靠的传输服务. 也就 ...

  4. UDP协议格式及特点

    UDP:用户数据报协议 协议格式: 16位源端端口-16位对端端口:描述通信两端 16位报文长度:16为最大数字位65535,描述报文长度.限制了报文长度.Udp报文(包含报头)长度必须小于64k-8 ...

  5. Python中的端口协议之基于UDP协议的通信传输

    阅读目录 UDP协议: 1.python中基于udp协议的客户端与服务端通信简单过程实现 2.udp协议的一些特点(与tcp协议的比较) 3.利用socketserver模块实现udp传输协议的并发通 ...

  6. 网络攻城狮怎么看待TCP/IP协议与UDP协议?

    在互联网中,存在着各种不同层次的协议,它们分别的功能也是不同的.但做为网络攻城狮,对这些协议的使用以及区分是工作的必备技能.那今天以网络攻城狮的角度来告诉大家攻城狮是怎么看待TCP/IP协议与UDP协 ...

  7. DHCP协议格式、DHCP服务搭建、DHCP协商交互过程入门学习

    相关学习资料 http://www.rfc-editor.org/rfc/rfc2131.txt http://baike.baidu.com/view/7992.htm?fromtitle=DHCP ...

  8. 23、java中的网编基础

    什么是网络编程? 在说网络编程之前要先知道什么是网络,网络是一种实现资源共享和数据传输的系统.而网络编程就是使用代码编写程序来进行网络之间数据的传输.使用java进行网络之间数据的传输是比较简单的,j ...

  9. 4-3:TCP协议之UDP协议

    文章目录 一:UDP协议格式 二:UDP特点 三:UDP的缓冲区 四:基于UDP的应用层协议 五:UDP在内核中的实现 一:UDP协议格式 16位UDP长度:指的是整个数据包(首部+数据)的最大长度 ...

最新文章

  1. apollo在liunx环境实战(三)
  2. Xcode中导入.a静态库后报错添加-force_load或-all_load
  3. linux下mqm用户下S开头日志,Linux新建用户,切换后只显示$问题
  4. 数据结构之串:KMP算法
  5. 加大Linux服务器的文件描述符
  6. opencv ppt效果_opencv几何图形画法
  7. c/c++教程 - 2.4.3 this指针作用,链式编程思想,空指针访问成员函数,const修饰成员函数,常函数,常对象
  8. 结构化编程 —— 顺序、分支(选择)、循环
  9. 停车位配建标准:北上广深
  10. java 工作簿_将多个Excel工作簿合并到一个工作簿中
  11. 接收灵敏度dbm与W
  12. 采用现场总线协议的压力变送器特性介绍
  13. 傅里叶变换的性质(一)
  14. Time Limit Exceeded的原因及避免方法
  15. 卷积神经网络的现代雏形——LeNet
  16. html5 workers,HTML5 Web Workers
  17. bat 批处理 B站缓存视频转成 .mp4格式
  18. 苹果电脑传android文件怎么打开,怎么用苹果电脑给android手机传文件
  19. NBU:1651 Red packet(二分)【好】
  20. 物体跟随鼠标移动——图片为gif格式

热门文章

  1. [翻译]SQL Server 未公开的两个存储过程sp_MSforeachtable 和 sp_MSforeachdb
  2. 配置文件 web.config 的讨论
  3. 20172329 2017-2018-2 《程序设计与数据结构》实验一报告
  4. python里类的概念
  5. Objective-C浮点数转化整数(向上取整、向下取整)
  6. java class load 类加载
  7. LPC1768的SPI通讯
  8. Android Service 的一些笔记
  9. 插座上的Linux充电器.不..Marvell Plug Computer
  10. array专题2---理解暴力枚举与动态规划