网络IO编程基本模型是C/S,即客户端服务端通信。但对于传统通信方式服务会被阻塞。下面介绍几种非同步通信方式。
- BIO 开启单独线程进行端口监听,对每个请求开启一个线程进行相应
- 伪异步IO 服务端开启一个线程池,请求从线程池中随机取出一个可用线程进行处理。
- NIO 使用
seletor
选择器对通道进行监听,通道负责和缓存区进行数据的读写。
1. 传统方式
示例:
final Socket socket = serverSocket.accept();
程序会阻塞
package com.hiro.learn.normal;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Project: learn
*
* @author : hirolin
* @date : 2019/5/23 10:20
*/
public class OioServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("server started");
while (true) {
final Socket socket = serverSocket.accept();
System.out.println("client connected!");
handle(socket);
}
}
public static void handle(Socket socket) {
try {
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true) {
int read = inputStream.read(bytes);
if (read != -1) {
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
System.out.println("socket close");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2.BIO 线程池方式
package com.hiro.learn.bio;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Project: learn
*
* @author : hirolin
* @date : 2019/5/23 10:20
*/
public class BioServer {
public static void main(String[] args) throws IOException {
ExecutorService executorService = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("server started");
while (true) {
final Socket socket = serverSocket.accept();
System.out.println("client connected!");
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(socket.getPort());
handle(socket);
}
}
);
}
}
public static void handle(Socket socket) {
try {
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true) {
int read = inputStream.read(bytes);
if (read != -1) {
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
System.out.println("socket close");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3.NIO
package com.hiro.learn.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* Project: learn
*
* @author : hirolin
* @date : 2019/5/23 0:04
*/
public class NIOService {
//通道管理器
private Selector selector;
/**
* 1.获取serverChannel通道
* 2.设置通道为非阻塞
* 3.将通道绑定端口
* 4.获取selector选择器
* 5.将serverChannel通道注册到selector选择器,指定事件类型
* * @param port
* @throws IOException
*/
public void initServer(int port) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port));
this.selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
/**
* 1.使用轮询的方式进行事件响应 select() 会一直阻塞
* 2.获取响应的事件
* 3.遍历
* 3.1删除当前处理的key
* 3.2获取当前通道 进行数据传输
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
System.out.println("server started .... ");
while (true) {
selector.select();
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
ite.remove();
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key
.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.write(ByteBuffer.wrap(new String("send client message by server").getBytes()));
channel.register(this.selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
read(key);
}
}
}
}
/**
* 处理读取客户端发来的信息 的事件
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException{
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(10);
channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("server receive:"+msg);
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
channel.write(outBuffer);// 将消息回送给客户端
}
/**
* main
* @throws IOException
*/
public static void main(String[] args) throws IOException {
NIOService server = new NIOService();
server.initServer(8000);
server.listen();
}
}
测试方法可以使用telnet 127.0.0.1 8000进行链接,启动多个窗口
(window需要先安装 win7安装之后可能要重启一下)