文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何在java中的使用UDP协议

2023-06-06 10:34

关注

本篇文章为大家展示了如何在java中的使用UDP协议,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

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

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

UDP报文格式如下:

如何在java中的使用UDP协议

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

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

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

# tcpdump -i lo -X udp port 9999tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes11: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.... ...

说明:

  1. 源端口:0xb262,十进制的45666。

  2. 目的端口:0x270f,十进制的9999。

  3. 长度:0x000e,14个字节的报文长度。

  4. 校验和: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>() {   @Override   protected 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>() {   @Override   protected 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实现的单播的客户端代码做如下修改:

增加option:

.option(ChannelOption.SO_BROADCAST, true)

将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);

下面通过对bio之单播的例子所产生的系统调用进行跟踪:

启动服务器端服务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是面向无连接的。

上述内容就是如何在java中的使用UDP协议,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯