一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - 詳解UDP協(xié)議格式及在java中的使用

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

2021-08-10 11:18morris131 Java教程

這篇文章主要介紹了UDP協(xié)議格式及在java中的使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

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

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

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

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

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

  • 源端口:數(shù)據(jù)發(fā)送方的端口號.
  • 目的端口:數(shù)據(jù)接收方的端口號。
  • 長度: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命令):

?
1
2
3
4
5
6
7
8
# 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 6
    0x0000: 4500 0022 5914 4000 4011 e3b4 7f00 0001 E.."Y.@.@.......
    0x0010: 7f00 0001 b262 270f 000e fe21 6865 6c6c .....b'....!hell
    0x0020: 6f0a     o.
... ...

說明:

  1. 源端口:0xb262,十進(jìn)制的45666。
  2. 目的端口:0x270f,十進(jìn)制的9999。
  3. 長度:0x000e,14個(gè)字節(jié)的報(bào)文長度。
  4. 校驗(yàn)和:0xfe21。

bio之單播

單播就是一對一通信。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
 
 }
}

客戶端代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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即可,具體修改如下:

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

bio之多播(組播)

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

多播組通過D類IP地址和標(biāo)準(zhǔn)UDP端口號指定。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ù)器端代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
 }
}

客戶端代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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ù)器端代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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);
 }
}

客戶端代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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ù)器端代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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 {
    // 接收數(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();
 }
 
 }
}

客戶端代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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 {
    // 接收數(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)的單播的客戶端代碼做如下修改:

1.增加option:

?
1
.option(ChannelOption.SO_BROADCAST, true)

2.將IP地址修改為廣播地址255.255.255.255

?
1
channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("hello server", CharsetUtil.UTF_8), new InetSocketAddress("255.255.255.255", 8899)));

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

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

?
1
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ù)聲明如下:

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

下面通過對bio之單播的例子所產(chǎn)生的系統(tǒng)調(diào)用進(jìn)行跟蹤:

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

?
1
# 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)用中并沒有listen函數(shù),不需要監(jiān)聽端口,再次驗(yàn)證UDP是面向無連接的。

到此這篇關(guān)于詳解UDP協(xié)議格式及在java中的使用的文章就介紹到這了,更多相關(guān)java中使用UDP協(xié)議內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/u022812849/article/details/109843694

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本xxxxn1819| 成人国产第一区在线观看 | 2019年国产不卡在线刷新 | 毛片a级放荡的护士hd | 亚洲视频999 | 国产在线精品一区二区高清不卡 | 欧美视| 国产成人精品免费2021 | 国产精品每日在线观看男人的天堂 | 青青草国产精品久久碰 | 日韩一级片免费观看 | 久久无码人妻AV精品一区 | 日本高清中文字幕视频在线 | 四川一级毛片 | 国产精品一二三 | 亚洲欧美日韩中文高清一 | 久久香蕉国产免费天天 | 免费看欧美一级特黄a大片一 | 男人天堂网站在线 | 娇妻与老头绿文小说系列 | 99久久精品国产片久人 | 亚洲日本在线观看网址 | 12一14性xxxxx国外 | 高h射尿| 亚洲国产成人精品无码区5566 | 楚乔传第二部免费观看全集完整版 | 亚洲同性男男gay1069 | mm131亚洲精品久久 | 久久成人免费大片 | 蜜桃视频一区二区 | 日本不卡不码高清免费观看 | 天堂樱桃bt在线www | 国产成人福利免费观看 | 国产一区二区免费视频 | 99午夜| 男人使劲躁女人小视频 | 国产精品99久久免费观看 | 免费视频精品一区二区三区 | 91精品国产麻豆国产自产在线 | 亚洲国产精品久久丫 | 九九九国产在线 |