日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】UDP协议格式以及在java中的使用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

UDP協(xié)議格式以及在java中的使用

UDP是面向無連接的通訊協(xié)議,由于通訊不需要連接,所以可以實(shí)現(xiàn)廣播發(fā)送。UDP通訊時(shí)不需要接收方確認(rèn),屬于不可靠的傳輸,可能會(huì)出現(xiàn)丟包現(xiàn)象,實(shí)際應(yīng)用中要求程序員編程驗(yàn)證。

UDP適用于DNS、視頻音頻等多媒體通信、廣播通信(廣播、多播)。例如我們常用的QQ,就是一個(gè)以UDP為主,TCP為輔的通訊協(xié)議。

UDP報(bào)文格式如下:


UDP首部有8個(gè)字節(jié),由4個(gè)字段構(gòu)成,每個(gè)字段都是兩個(gè)字節(jié),

  • 源端口:數(shù)據(jù)發(fā)送方的端口號(hào).
  • 目的端口:數(shù)據(jù)接收方的端口號(hào)。
  • 長度:UDP數(shù)據(jù)報(bào)的整個(gè)長度(包括首部和數(shù)據(jù)),其最小值為8(只有首部)。
  • 校驗(yàn)和:檢測UDP數(shù)據(jù)報(bào)在傳輸中是否有錯(cuò),有錯(cuò)則丟棄。
  • 可以使用nc發(fā)送UDP數(shù)據(jù)包:echo hello | nc -uv 127.0.0.1 9999。

    用tcpdump抓取到的數(shù)據(jù)包如下(注意先運(yùn)行tcpdump,然后再執(zhí)行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,十進(jìn)制的45666。
    • 目的端口:0x270f,十進(jìn)制的9999。
    • 長度:0x000e,14個(gè)字節(jié)的報(bào)文長度。
    • 校驗(yàn)和:0xfe21。

    bio之單播

    單播就是一對(duì)一通信。

    服務(wù)器端代碼如下:

    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()));} }

    客戶端和服務(wù)端的代碼幾乎一致,只不過接收和發(fā)送數(shù)據(jù)的順序不一致,receive和send都?xì)W式阻塞方法。

    bio之廣播

    廣播:同一網(wǎng)段所有主機(jī)都能接收,前提是端口要開啟監(jiān)聽。

    只需要將單播的例子中客戶端發(fā)送數(shù)據(jù)的IP修改為255.255.255.255即可,具體修改如下:

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

    bio之多播(組播)

    多播數(shù)據(jù)報(bào)套接字類用于發(fā)送和接收IP多播包。MulticastSocket是一種DatagramSocket,它具有加入Internet上其他多播主機(jī)的“組”的附加功能。

    多播組通過D類IP地址和標(biāo)準(zhǔn)UDP端口號(hào)指定。D類IP地址在224.0.0.0和239.255.255.255的范圍內(nèi)。地址224.0.0.0被保留,不應(yīng)使用。

    可以通過首先使用所需端口創(chuàng)建MulticastSocket,然后調(diào)用joinGroup(InetAddress groupAddr)方法來加入多播組。

    服務(wù)器端代碼如下:

    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實(shí)現(xiàn)單播

    服務(wù)器端代碼如下:

    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實(shí)現(xiàn)單播

    服務(wù)器端代碼如下:

    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 {// 接收數(shù)據(jù)System.out.println(msg.content().toString(CharsetUtil.UTF_8));// 發(fā)送數(shù)據(jù)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 {// 接收數(shù)據(jù)System.out.println(msg.content().toString(CharsetUtil.UTF_8));ctx.close();}});Channel channel = bootstrap.bind(0).sync().channel();// 發(fā)送數(shù)據(jù)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("查詢超時(shí)");}} finally {group.shutdownGracefully();}} }

    Netty實(shí)現(xiàn)廣播

    只需要將netty實(shí)現(xiàn)的單播的客戶端代碼做如下修改:

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

    底層實(shí)現(xiàn)

    recvfrom負(fù)責(zé)接收UDP數(shù)據(jù),其函數(shù)聲明如下:

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

    sendto負(fù)責(zé)發(fā)送UDP數(shù)據(jù),其函數(shù)聲明如下:

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
    • 下面通過對(duì)bio之單播的例子所產(chǎn)生的系統(tǒng)調(diào)用進(jìn)行跟蹤:

    啟動(dòng)服務(wù)器端服務(wù)Server:

    # strace -ff -o out java Server

    然后使用nc命令充當(dāng)客戶端進(jìn)行連接:echo hello | nc -uv 127.0.0.1 9999。

    產(chǎn)生的系統(tǒng)調(diào)用中關(guān)鍵信息如下:

    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

    可見發(fā)送和接收數(shù)據(jù)確實(shí)使用了上面的系統(tǒng)調(diào)用,另外上面的系統(tǒng)調(diào)用中并沒有l(wèi)isten函數(shù),不需要監(jiān)聽端口,再次驗(yàn)證UDP是面向無連接的。

    總結(jié)

    以上是生活随笔為你收集整理的【转】UDP协议格式以及在java中的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。