NIO:数据报(UDP)信道

news/2024/5/18 15:13:59 标签: nio, NIO, udp, UDP, 信道, 数据报

数据报UDP信道

 JavaNIO包通过DatagramChannel类实现了数据报UDP信道。与我们之前看到的其他形式的SelectableChannel一样,DatagramChannelDatagramSocket上添加了选择和非阻塞行为,以及基于缓冲区的I/O操作能力。 

DatagramChannel: 创建,连接和关闭 

static DatagramChannel open()

boolean isOpen()

DatagramSocket socket() void close()

需要调用DatagramChannelopen()工厂方法来创建一个DatagramChannel实例,该实例是未绑定的。DatagramChannel只是对基本DatagramSocket的一个包装器(wrapper)。使用其socket()方法可以直接访问内部的DatagramSocket实例。这就允许通过调用基本的DatagramSocket方法进行绑定、设置套接字选项等操作。用完DatagramChannel后,要调用它的close()方法将其关闭。 

只要创建了一个DatagramChannel实例,就可以非常直接地发送和接收数据。

DatagramChannel: 发送和接收

int send(ByteBuffer src, SocketAddress target)

SocketAddress receive(ByteBuffer dst)

send()方法用于创建一个包含了给定ByteBuffer中的数据的数据报文,并将其发送到目的地址指定的SocketAddress上。receive()方法用于将接收到的数据报文存入指定缓冲区并返回发送者的地址。重要提示:如果缓冲区的剩余空间小于数据报文中的数据大小,多余的数据将毫无提示地丢弃。

 以下代码段用于创建一个DatagramChannel实例,并将UTF-16编码的字符串"Hello"送到运行在同一主机的5000端口上的UDP服务器上。

DatagramChannel channel = DatagramChannel.open();

ByteBuffer buffer =

ByteBuffer.wrap("Hello".getBytes("UTF-16"));

channel.send(buffer, new InetSocketAddress("localhost",

5000));

以下代码段用于创建一个DatagramChannel实例,将底层的套接字绑定到5000端口,接收最长为20字节的数据报文,并将字节转换成使用UTF-16编码的字符串。

DatagramChannel channel = DatagramChannel.open();

channel.socket().bind(new InetSocketAddress(5000));

ByteBuffer buffer = ByteBuffer.allocateDirect(20);

SocketAddress address = channel.receive(buffer);

buffer.flip();

String received = Charset.forName("UTF-16").

newDecoder().decode(buffer).toString();

在上面的send()实例中,调用send()方法时并没有显式地绑定本地端口,因此将随机选择一个可用端口。相应的receive()方法用于返回一个SocketAddress,其中包含了端口号。

如果总是向同一个远程终端发送或接收数据,我们可以选择调用connect()方法,并使用SocketAddress指定远程终端的地址。

DatagramChannel: 连接DatagramChannel

DatagramChannel connect(SocketAddress remote)

DatagramChannel disconnect()

boolean isConnected()

int read(ByteBuffer dst)

long read(ByteBuffer[] dsts)

long read(ByteBuffer[] dsts, int offset, int length)

int write(ByteBuffer src)

long write(ByteBuffer[] srcs)

long write(ByteBuffer[] srcs, int offset, int length)

这些方法限制我们只能通过指定的地址发送和接收数据。为什么要这样做呢?原因之一是调用connect()方法后,可以使用read()write()方法来代替receive()send()方法,并且不需要处理远程地址。read()write()方法分别用于接收和发送一个数据报文。分散式读操作以一个ByteBuffer数组为参数,只接收一个数据报文,并按顺序将其填入缓冲区中。聚集式写操作将缓冲区数组中的所有字节连接起来创建一个要传输的数据报文。重要提示:现在能够发送的最大数据报文可以包含65507个字节,试图发送更多的数据将被无提示地截断。

 使用connect()方法的另一个好处是,已建立连接的数据报信道可能只接收从指定终端发送来的数据,因此我们不需要测试接收端的有效性。注意,DatagramChannelconnect()方法只起到限制发送和接收终端的作用,连接时并没有数据包在SocketChannel上进行交换,而且也不需要像SocketChannel那样等待或测试连接是否完成。(见第6章)

到目前为止DatagramChannel看起来与DatagramSocket非常相似。数据报信道和套接字的主要区别是,信道可以进行非阻塞I/O操作和使用选择器。DatagramChannel中选择器的创建,信道的注册、选择等,与SocketChannel几乎一模一样。有一个区别是DatagramChannel不能注册连接I/O操作,不过也不需要这样做,因为DatagramChannelconnect()方法永远不会阻塞。

 DatagramChannel: 设置阻塞行为和使用选择器

SelectableChannel configureBlocking(boolean block)

boolean isBlocking()

SelectionKey register(Selector sel, int ops)

SelectionKey register(Selector sel, int ops, Object

attachment) 

boolean isRegistered()

int validOps()

SelectionKey keyFor(Selector sel)

这些方法的功能与SocketChannelServerSocketChannel中的相应方法一样。

下面使用DatagramChannel对第4章中的DatagramSocket UDP回显服务器进行重写。

服务器侦听指定的端口,并将接收到的数据报文简单地回发给客户端。重写后的服务器与原

来版本的主要区别是它不会在send()receive()方法上阻塞等待。

UDPEchoServerSelector.java

0 import java.io.IOException;

1 import java.net.InetSocketAddress;

2 import java.net.SocketAddress;

3 import java.NIO.html" title=nio>nio.ByteBuffer;

4 import java.NIO.html" title=nio>nio.channels.DatagramChannel;

5 import java.NIO.html" title=nio>nio.channels.SelectionKey;

6 import java.NIO.html" title=nio>nio.channels.Selector;

7 import java.util.Iterator;

8

9 public class UDPEchoServerSelector {

10

11 private static final int TIMEOUT = 3000; // Wait timeout

(milliseconds)

12

13 private static final int ECHOMAX = 255; // Maximum size

of echo datagram

14

15 public static void main(String[] args) throws

IOException {

16

17 if (args.length != 1) // Test for correct argument list

18 throw new IllegalArgumentException("Parameter(s):

<Port>");

19

20 int servPort = Integer.parseInt(args[0]);

21

22 // Create a selector to multiplex client connections.

23 Selector selector = Selector.open();

24

25 DatagramChannel channel = DatagramChannel.open();

26 channel.configureBlocking(false);

27 channel.socket().bind(new

InetSocketAddress(servPort));

28 channel.register(selector, SelectionKey.OP_READ, new

ClientRecord());

29

30 while (true) { // Run forever, receiving and echoing

datagrams

31 // Wait for task or until timeout expires

32 if (selector.select(TIMEOUT) == 0) {

33 System.out.print(".");

34 continue;

35 }

36

37 // Get iterator on set of keys with I/O to process

38 Iterator<SelectionKey> keyIter =

selector.selectedKeys().iterator();

39 while (keyIter.hasNext()) {

40 SelectionKey key = keyIter.next(); // Key is bit mask

41

42 // Client socket channel has pending data?

43 if (key.isReadable())

44 handleRead(key);

45

46 // Client socket channel is available for writing and

47 // key is valid (i.e., channel not closed).

48 if (key.isValid() && key.isWritable())

49 handleWrite(key);

50

51 keyIter.remove();

52 }

53 }

54 }

55

56 public static void handleRead(SelectionKey key) throws

IOException {

57 DatagramChannel channel = (DatagramChannel)

key.channel();

58 ClientRecord clntRec = (ClientRecord)

key.attachment();

59 clntRec.buffer.clear(); // Prepare buffer for receiving

60 clntRec.clientAddress =

channel.receive(clntRec.buffer);

61 if (clntRec.clientAddress != null) { // Did we receive

something?

62 // Register write with the selector

63 key.interestOps(SelectionKey.OP_WRITE);

64 }

65 }

66

67 public static void handleWrite(SelectionKey key) throws

IOException {

68 DatagramChannel channel = (DatagramChannel)

key.channel();

69 ClientRecord clntRec = (ClientRecord)

key.attachment();

70 clntRec.buffer.flip(); // Prepare buffer for sending

71 int bytesSent = channel.send(clntRec.buffer,

clntRec.clientAddress);

72 if (bytesSent != 0) { // Buffer completely written?

73 // No longer interested in writes

74 key.interestOps(SelectionKey.OP_READ);

75 }

76 }

77

78 static class ClientRecord {

79 public SocketAddress clientAddress;

80 public ByteBuffer buffer =

ByteBuffer.allocate(ECHOMAX);

81 }

82 }

UDPEchoServerSelector.java

 

相关下载:

Java_TCPIP_Socket编程(doc)

http://download.csdn.net/detail/undoner/4940239

 

文献来源:

LSOFT.CN(琅软中国)


http://www.niftyadmin.cn/n/1853201.html

相关文章

Python学习--面向对象编程

一、编程范式 编程范式&#xff1a;按照什么方式来去编程&#xff0c;去实现一个功能。举个例子&#xff1a;做饭可以用电磁炉&#xff0c;也可以用燃气灶。不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路&#xff0c;两种最重要的编程范式分别是面向过程…

js-xlsx操作excel表格

1导入与导出功能实现 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title></head> <body> <input type"button" value"导入" onclick"file_i…

Java_TCPIP_Socket编程(doc)下载

Java_TCPIP_Socket编程(doc) 第 1 章 简介 3 1.1 计算机网络&#xff0c;分组报文和协议3 1.2 关于地址6 1.3 关于名字8 1.4 客户端和服务器8 1.5 什么是套接字.9 1.6 练习10 第 2 章 基本套接字 10 2.1 套接字地址10 2.2 TCP套接字.17 2.2.1 TCP客户端.17 2.2.2 T…

Wikipedia’s 10 most-visited pages in 2012

Wikipedia dominates Google search results. That’s something any amateur googler knows after a few searches for anything mildly popular—from, say, the word “Google” itself to Facebook to Fifty Shades of Gray. As further proof of the Wiki-dominance, we …

在线订货管理系统的特点

盛京三四线网店是基于添美批零采销系统搭建&#xff0c;其中的网店功能是其中批发商订货系统的具象化名称&#xff0c;与传统订货比较具有多渠道服务入口、电子商务方式订货、营销工具、进销存、让业务员做业务、串货等特点&#xff1a; 多渠道服务入口比如通信运营商移动、电信…

教父马云的经典语录汇总

近日&#xff0c;马云通过阿里巴巴内部邮箱向员工通知了自己将在今年5月10日卸任阿里巴巴CEO的消息。作为一个创业13年&#xff0c;把阿里巴巴变成中国最大的电子商务平台的教父级人物&#xff0c;马云有他艰辛的创业经历和独特的管理思维。且通过马云的一些话&#xff0c;向这…

Problem W: 零起点学算法21——求平均值

#include<stdio.h> int main() {int a,b,c;scanf("%d %d %d",&a,&b,&c);printf("%.3f\n",(abc)/3.0);return 0; } 转载于:https://www.cnblogs.com/chenlong991223/p/9720488.html

设计中默认样式的强大威力

默认样式&#xff0c;就是最原生态的样式。就像大家经常用的按钮或者蓝色有下划线的超链接。本文&#xff0c;潜行者m将从两个元素来讨论默认样式在设计中的应用。 超链接的默认样式 超链接的默认样式 超链接是网页中最常用最基础的元素&#xff0c;可以说是必不可少的。我们…