UDP案例
UDPClient
package chapter03_Java_NIO;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/**
* 从示例程序可以看出,在客户端使用DatagramChannel发送数据比在客户端使用SocketChannel发送数据要简单得多。
*/
public class java04_5_UDPClient {
public void send() throws IOException {
//获取DatagramChannel
DatagramChannel dChannel = DatagramChannel.open();
//设置为非阻塞
dChannel.configureBlocking(false);
ByteBuffer buffer =
ByteBuffer.allocate(1024);
Scanner scanner = new Scanner(System.in);
System.out.println("UDP客户端启动成功!");
System.out.println("请输入发送内容:");
while (scanner.hasNext()) {
String next = scanner.next();
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
buffer.put(( date+ " >>" + next).getBytes());
buffer.flip();
//通过DatagramChannel发送数据
dChannel.send(buffer,new InetSocketAddress("127.0.0.1",18899));
buffer.clear();
}
//关闭DatagramChannel
dChannel.close();
}
public static void main(String[] args) throws IOException {
new java04_5_UDPClient().send();
}
}
UDPServer
package chapter03_Java_NIO;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
/**
* 服务端是通过DatagramChannel绑定一个服务器地址(IP+端口),接收客户端发送过来的UDP数据报。服务端的完整代码如下:
*
* @在服务端,首先调用了bind()方法绑定DatagramChannel的监听端口。
* @当数据到来时调用了receive()方法,从DatagramChannel接收数据后写入ByteBuffer缓冲区中。
* @在服务端代码中,为了监控数据的到来,使用了Selector。
*/
public class java04_6_UDPServer {
public void receive() throws IOException {
//获取DatagramChannel
DatagramChannel datagramChannel = DatagramChannel.open();
//设置为非阻塞模式
datagramChannel.configureBlocking(false);
//绑定监听地址
datagramChannel.bind(new InetSocketAddress("127.0.0.1",18899));
System.out.println("UDP服务器启动成功!");
//开启一个通道选择器
Selector selector = Selector.open();
//将通道注册到选择器
datagramChannel.register(selector, SelectionKey.OP_READ);
//通过选择器查询IO事件
while (selector.select() > 0) {
Iterator<SelectionKey> iterator =
selector.selectedKeys().iterator();
ByteBuffer buffer =
ByteBuffer.allocate(1024);
//迭代IO事件
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//可读事件,有数据到来
if (selectionKey.isReadable()) {
//读取DatagramChannel数据
SocketAddress client = datagramChannel.receive(buffer);
buffer.flip();
System.out.println(new String(buffer.array(), 0, buffer.limit()));
buffer.clear();
}
}
iterator.remove();
}
//关闭选择器和通道
selector.close();
datagramChannel.close();
}
public static void main(String[] args) throws IOException {
new java04_6_UDPServer().receive();
}
}
简单NIO案例
Server用main方法启动 Client 用@Test 注解启动
class NioDiscardServer {
public static void startServer() throws IOException {
//1.获取选择器
Selector selector = Selector.open();
//2.获取通道
ServerSocketChannel serverSocketChannel =
ServerSocketChannel.open();
//3.设置为非阻塞
serverSocketChannel.configureBlocking(false);
//4.绑定连接
serverSocketChannel.bind(new InetSocketAddress(18899));
Logger.info("服务器启动成功");
//5.将通道注册的“接收新连接”IO事件注册到选择器上
serverSocketChannel.register(selector,
SelectionKey.OP_ACCEPT);
//6.轮询感兴趣的IO就绪事件(选择键集合)
while (selector.select() > 0) {
//7.获取选择键集合
Iterator<SelectionKey> selectedKeys =
selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
//8.获取单个的选择键,并处理
SelectionKey selectedKey = selectedKeys.next();
//9.判断key是具体的什么事件
if (selectedKey.isAcceptable()) {
//10.若选择键的IO事件是“连接就绪”,就获取客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
//11.将新连接切换为非阻塞模式
socketChannel.configureBlocking(false);
//12.将新连接的通道的可读事件注册到选择器上
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectedKey.isReadable()) {
//13.若选择键的IO事件是“可读”,则读取数据
SocketChannel socketChannel = (SocketChannel) selectedKey.channel();
//14.读取数据,然后丢弃
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int length = 0;
while ((length = socketChannel.read(byteBuffer)) >0)
{
byteBuffer.flip();
Logger.info(new String(byteBuffer.array(), 0, length));
byteBuffer.clear();
}
socketChannel.close();
}
//15.移除选择键
selectedKeys.remove();
}
}
//16.关闭连接
serverSocketChannel.close();
}
public static void main(String[] args) throws IOException {
startServer();
}
/**
* 客户端首先建立到服务器的连接,发送一些简单的数据,然后直接关闭连接。客户端的DiscardClient代码更加简单,代码如下:
*
* 通过Discard服务器的开发实战,大家应该对NIO Selector的使用流程了解得非常清楚了。下面来看一个稍微复杂一点的案例:在服务端接收文件和内容。
*/
@Test
public void startClient() throws IOException {
InetSocketAddress address =new InetSocketAddress("127.0.0.1",18899);
//1.获取通道
SocketChannel socketChannel = SocketChannel.open(address);
//2.切换成非阻塞模式
socketChannel.configureBlocking(false);
//不断地自旋、等待连接完成,或者做一些其他的事情
while (!socketChannel.finishConnect()) {
}
Logger.info("客户端连接成功");
//3.分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("hello world".getBytes());
byteBuffer.flip();
//发送到服务器
socketChannel.write(byteBuffer);
socketChannel.shutdownOutput();
socketChannel.close();
}
}