NIO UDP 编程

和TCP的SocketChannel类似,UDP的[color=red]DatagramChannel[/color]能充分利用NIO的优势。

服务端:Selector, [color=red]DatagramChannel[/color], ByteBuffer

客户端:[color=red]DatagramChannel[/color], ByteBuffer

区别:
a. 服务端没有与TCP的ServerSocketChannel相对应的Channel,服务端和客户端之间更趋于平等,不过服务端的端口号还是固定的。

b. Selector在处理完读取操作后,会触发写操作:发送数据到客户端。

[size=medium]1. 服务端[/size]

public class UDPEchoServerSelector {

private static final int TIMEOUT = 4000; // 超时 (毫秒)
private static final int CAPACITY = 255;

public static void main(String[] args) throws IOException {
args = new String[1];
args[0] = "4451";

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

Selector sel = Selector.open(); // 创建选择器,可以处理多路通道。

DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new InetSocketAddress(servPort)); // 通道关联的socket绑定地址
channel.register(sel, SelectionKey.OP_READ, new ClientData());

while (true) { // 持续运行,接收和返回数据
if (sel.select(TIMEOUT) == 0) {
System.out.println("No I/O needs to be processed");
continue;
}

Iterator<SelectionKey> iter = sel.selectedKeys().iterator(); // 获取可操作的选择键集合
while (iter.hasNext()) {
SelectionKey key = iter.next(); // 键为位掩码

if (key.isReadable()) { // 客户端有数据发送过来
handleRead(key);
}

if (key.isValid() && key.isWritable()) { // 通道正常,且客户端需要响应
handleWrite(key);
}

iter.remove(); // 从集合中移除选择键
}

}
}

private static void handleRead(SelectionKey key) throws IOException {
DatagramChannel channel = (DatagramChannel) key.channel();
ClientData clntDat = (ClientData) key.attachment();
clntDat.buffer.clear();
clntDat.clientAddress = channel.receive(clntDat.buffer); // 获取客户端的地址,用以发送响应
if (clntDat.clientAddress != null) { // 接收到数据
key.interestOps(SelectionKey.OP_WRITE); // 关注客户端读取响应
}
}

private static void handleWrite(SelectionKey key) throws IOException {
DatagramChannel channel = (DatagramChannel) key.channel();
ClientData clntDat = (ClientData) key.attachment();
clntDat.buffer.flip(); // 从起始位置开始发送
int bytesSent = channel.send(clntDat.buffer, clntDat.clientAddress);
if (bytesSent != 0) {
key.interestOps(SelectionKey.OP_READ); // 关注客户端发送数据
}
}

public static class ClientData {
public SocketAddress clientAddress;
public ByteBuffer buffer = ByteBuffer.allocate(CAPACITY);
}
}



2. 客户端

public class UDPEchoClient {

private static final int CAPACITY = 255;
private static final String UTF8 = "UTF-8";

public static void main(String[] args) throws IOException {
args = new String[2];
args[0] = "localhost";
args[1] = "4451";

String servName = args[0];
int servPort = Integer.parseInt(args[1]);

DatagramChannel clntChan = DatagramChannel.open();
clntChan.configureBlocking(false);
clntChan.connect(new InetSocketAddress(servName, servPort));

ByteBuffer sentBuffer = ByteBuffer.wrap("Hello UDP".getBytes(UTF8));
int bytesSent = clntChan.write(sentBuffer); // 向服务器发送数据
System.out.println("UDP client sent " + bytesSent + " bytes");

ByteBuffer recvBuffer = ByteBuffer.allocate(CAPACITY);
clntChan.receive(recvBuffer); // 读取响应
recvBuffer.flip();
System.out.println("UDP client received: " + new String(recvBuffer.array(), UTF8));
}
}



客户端连接服务器后,发送数据可以用write或send方法:

...
clntChan.connect(new InetSocketAddress(servName, servPort));
...
int bytesSent = clntChan.write(sentBuffer);



...
clntChan.connect(new InetSocketAddress(servName, servPort));
...
int bytesSent = clntChan.send(sentBuffer, new InetSocketAddress(servName, servPort)); // 发送地址必须和前面的连接地址相同,否则报java.lang.IllegalArgumentException: Connected address not equal to target address


客户端没有连接服务器,需要用send方法指定目标地址:

...
int bytesSent = clntChan.send(sentBuffer, new InetSocketAddress(servName, servPort)); // 使用write方法会报java.nio.channels.NotYetConnectedException
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值