BIO
同步阻塞模型,指的是用户空间(或者线程)主动发起,需要等待内核 IO 操作彻底完成后才返回到用户空间的 IO 操作。在 IO 操作过程中,发起 IO 请求的用户进程(或者线程)处于阻塞状态。
代码示例:
public class SocketServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8088);
while (true) {
// 等待连接 阻塞
Socket socket = serverSocket.accept();
//客户端连接
System.out.println("客户端连接" + socket.getPort());
// 接受客户端消息
byte[] bytes = new byte[1024];
int read = socket.getInputStream().read(bytes);
System.out.println("客户端发送消息:" + new String(bytes, 0, read));
socket.getOutputStream().write("ok".getBytes());
socket.getOutputStream().flush();
}
}
}
public class SocketClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 8088);
socket.getOutputStream().write("hello".getBytes());
socket.getOutputStream().flush();
byte[] bytes = new byte[1024];
int read = socket.getInputStream().read(bytes);
System.out.println("服务端发送消息:" + new String(bytes, 0, read));
socket.close();
}
}
NIO
非阻塞IO + IO 多路复用模型,服务器实现模式为一个线程可以处理多个请求(连接),客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就进行处理。
服务端
public class NioServer {
public static void main(String[] args) throws IOException {
// 创建server
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 绑定端口
serverSocketChannel.socket().bind(new InetSocketAddress(8088));
// 设置非阻塞
serverSocketChannel.configureBlocking(false);
// 复用器
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 监听事件 会阻塞
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()) { // 客户端连接
ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
SocketChannel accept = server.accept();
accept.configureBlocking(false);
// 注册读事件
accept.register(selector, SelectionKey.OP_READ);
System.out.println("客户端连接成功");
} else if (selectionKey.isReadable()) { // 读事件
SocketChannel socket = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int read = socket.read(byteBuffer);
if (read > 0) {
System.out.println("客户端发送消息:" + new String(byteBuffer.array(), 0 , read));
} else if (read == -1) {
System.out.println("客户端断开连接");
socket.close();
}
socket.write(ByteBuffer.wrap("ok".getBytes()));
}
iterator.remove();
}
}
}
}
客户端
public class NioClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8088));
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
channel.configureBlocking(false);
// 正在连接
if (channel.isConnectionPending()) {
channel.finishConnect();
}
ByteBuffer buffer = ByteBuffer.wrap("HelloServer".getBytes());
channel.write(buffer);
channel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) { // 读事件
SocketChannel socket = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int read = socket.read(byteBuffer);
if (read > 0) {
System.out.println("服务端发送消息:" + new String(byteBuffer.array(), 0 , read));
} else if (read == -1) {
System.out.println("服务端断开连接");
socket.close();
}
}
iterator.remove();
}
}
}
}
使用netty
Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
服务端
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//连接处理线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 业务处理线程
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class) // 使用nio管道
.option(ChannelOption.SO_BACKLOG, 1024) // 设置客户端连接请求等待队列
.childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 消息都是ByteBuf , 使用字符串
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new NettyServerHandler());
}
});
//绑定端口
ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
channelFuture.channel().closeFuture().sync();
}
}
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
System.out.println("服务器接受到的消息:" + s);
}
}
客户端
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//加入处理器
channel.pipeline().addLast(new StringDecoder());
channel.pipeline().addLast(new StringEncoder());
channel.pipeline().addLast(new NettyClientHandler());
}
});
//启动客户端去连接服务器端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8088).sync();
channelFuture.sync().channel().writeAndFlush("HelloServer");
//对关闭通道进行监听
channelFuture.channel().closeFuture().sync();
}
}
public class NettyClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
System.out.println("客户端接受到的消息:" + s);
}
}