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

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));
  • 1

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
  1. 将IP地址修改为广播地址255.255.255.255
channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("hello server", CharsetUtil.UTF_8), new InetSocketAddress("255.255.255.255", 8899)));
  • 1

底层实现

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是面向无连接的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/436991.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python网页填表教程_PythonSpot 中文系列教程 · 翻译完成

原文&#xff1a;PythonSpot Python Tutorials 协议&#xff1a;CC BY-NC-SA 4.0 欢迎任何人参与和完善&#xff1a;一个人可以走的很快&#xff0c;但是一群人却可以走的更远。在线阅读ApacheCN 学习资源目录PythonSpot 中文系列教程初学者 介绍Python 字符串字符串&#xff0…

【转】TCP/IP协议到底在讲什么?【乐搏TestPro】

用比喻和漫画给有需要的小伙伴解释下IP、TCP捎带题一下各种协议与HTTP协议的关系&#xff1b; 目录&#xff1a; 负责传输的IP协议 确保可靠性的TCP协议 各种协议与HTTP协议的关系 一、负责传输的IP协议 按照层次分&#xff0c;IP&#xff08;全称&#xff1a;Internet Prot…

Qt中标绘功能的实现方法对比

使用Qt开发桌面程序&#xff0c;经常会有标绘的需求&#xff0c;一般有以下几点&#xff1a; 新建&#xff1a;圆、矩形、椭圆、文字标注&#xff0c;插入图像等&#xff1b;编辑&#xff1a;指对已标绘内容的属性编辑修改功能&#xff1b;删除&#xff1a;指对已标绘内容的删…

【转】PE文件结构详解--(完整版)

&#xff08;一&#xff09;基本概念 PE&#xff08;Portable Execute&#xff09;文件是Windows下可执行文件的总称&#xff0c;常见的有DLL&#xff0c;EXE&#xff0c;OCX&#xff0c;SYS等&#xff0c;事实上&#xff0c;一个文件是否是PE文件与其扩展名无关&#xff0c;P…

【转】逆变与协变详解

逆变&#xff08;contravariant&#xff09;与协变&#xff08;covariant&#xff09;是C#4新增的概念&#xff0c;许多书籍和博客都有讲解&#xff0c;我觉得都没有把它们讲清楚&#xff0c;搞明白了它们&#xff0c;可以更准确地去定义泛型委托和接口&#xff0c;这里我尝试画…

【转】GitHub 从单机到联机:玩转 Pull Request

最近在参与一个叫 Exercism 的项目&#xff0c;这是一个由 GitHub 生态工程师 Katrina Owen 发起的编程练习社区&#xff0c;提供了超过50门语言的练习。作为用户&#xff0c;你仅需使用命令行工具即可下载和提交练习&#xff0c;提交后还可以和社区中其他学习者交流讨论。 Exe…

【转】GitHub客户端操作1--仓库相关操作github团队协作流程

1、创建仓库 点击“”&#xff0c;点击Create,然后填写Name为my&#xff0c;点击Create repository&#xff0c;即可创建一个my仓库 2、从本地仓库存放位置&#xff0c;添加test仓库到GitHub客户端 点击“”&#xff0c;点击Add,然后点击Browse&#xff0c;选择仓库存放位置&a…

【转】GitHub客户端操作2--分支操作

简单分支操作 &#xff08;1&#xff09;创建新分支&#xff1a;my分支 备注&#xff1a;新创建的分支&#xff1a;my分支里面的内容是和master分支里面的内容是完全一样的。 &#xff08;2&#xff09;删除my分支 &#xff08;3&#xff09;修改my分支【在my分支上进行项目内…

【转】GitHub客户端操作3--pull Request(拉请求)

一、参考说明 参考文章一&#xff1a;Github上提交代码(pullrequest) 网址&#xff1a; https://jingyan.baidu.com/article/358570f64dcdc2ce4724fc32.html 参考文章二&#xff1a;GitHub——Pull Request 网址&#xff1a;http://blog.csdn.net/u012325167/article/detai…

【转】GitHub上README.md教程

本文是转载文章&#xff0c;文章的来源&#xff1a;csdn博客 博主&#xff1a;果冻虾仁 文章&#xff1a; GitHub上README写法暨GFM语法解读 博文地址&#xff1a;https://blog.csdn.net/guodongxiaren/article/details/23690801 转载请保留原作者guodongxiaren的原文地址&…

【转】WPF单位真的与分辨率无关吗?

转载自http://www.cnblogs.com/helloj2ee/archive/2009/04/21/1440709.htm WPF从发布之日起&#xff0c;一直将“分辨率无关(resolution independence)”作为其亮点&#xff0c;声称使用WPF制作的用户界面在轻巧的Ultra-Mobile PC的屏幕上和在50英寸的电视机上都能很好地显示。…

【转】世上最简单的vue教程

一、需要了解的基本知识 node.js Node.js是一个Javascript运行环境(runtime)&#xff0c;发布于2009年5月&#xff0c;由Ryan Dahl开发&#xff0c;实质是对Chrome V8引擎进行了封装。Node.js对一些特殊用例进行优化&#xff0c;提供替代的API&#xff0c;使得V8在非浏览器环境…

工程师学乐理(一)尝试理解音乐

前言 很早就接触了乐理&#xff0c;但是一直没有学懂&#xff0c;越学问题越多。个人感觉&#xff0c;其中很大的原因是有关教材写得看不懂&#xff0c;用未知的东西描述未知的东西&#xff0c;不知所云。前几年还买了一把吉他&#xff0c;买了课程&#xff0c;断断续续学了几…

【转】Vue.js入门教程(一)从静态页面到前后端分离开发

第一章&#xff1a;基础知识 我能看懂吗&#xff1f; 只要你现在能用htmlcssjs制作一个静态页面&#xff0c;相信我&#xff0c;你100%可以读懂这篇文章。 本文尤其适合那些想要了解前后端分离开发技术&#xff0c;或者刚刚脱离传统MVC开发模式的前端人员。 回想一下&#xf…

工程师学乐理(二)音阶及倾向性

前言 阅读本文前&#xff0c;请先阅读《写给理工科人看的乐理》。 本文主要讲音阶。在其他地方能查到的细节&#xff0c;我们这里就不会多说了。本文重点在于梳理音阶背后的逻辑&#xff0c;尝试把技术点串起来讲。 没有逻辑的东西是咱们工程师比较讨厌的&#xff0c;任何大自…

【转】VS工具:实时可视化树

VisuaStudio号称全宇宙最强大的IDE。在VS2015版本中&#xff0c;微软又给广大开发者带来了一个强大的工具&#xff1a;实时可视化树(Live Visual Tree&#xff0c;以下简称可视树)。其实树可视化工具并不是新鲜的东西&#xff0c;在WPF的时候就有了&#xff0c;只是这次微软集成…

【转】WPF 入门《常用控件》

1.GroupBox 注意: GroupBox仍然需要布局容器来放置元素。如: StackPanel面板 1 2 3 4 5 6 7 <GroupBox Header"select number?"> <StackPanel> <RadioButton>one</RadioButton> <RadioButton>two</…

【转】wpf从我炫系列1----布局控件的使用(上)

今天我来给大家讲解在学习WPF过程中使用布局控件的一些心得&#xff0c;主要给大家介绍一下一个控件的用法。希望对大家学习Wpf有所帮助. 1. StackPanel栈面板 2. WrapPanel环绕面板 3. DockPanel停靠面板 4. Grid网格 5. UniformGrid均布网…

【开源项目】EasyCmd命令图形化软件

EasyCmd 项目地址&#xff1a;https://gitee.com/showmework/EasyCmd 最新版本&#xff1a;v0.2预览版 介绍 命令行图形化。 让命令行更易于使用&#xff0c;从命令行复杂的参数及语法中解放出来。 设计思想 软件设计思想并不是构建用户界面&#xff0c;现代操作系统已经…

php在线读取pdf文件大小_PDF转WORD在线转换器哪家强?

无论是工作还是日常生活中&#xff0c;经常需要将pdf转换成word文档进行编辑&#xff0c;我想大部分人一定是和我一样&#xff0c;首先会去找度娘帮忙&#xff0c;百度一下“pdf转word”出现了很多在线转换器&#xff0c;比如大家常用的讯捷、smallpdf等&#xff0c;然后立马进…