高性能网络编程必备技能之IO与NIO阻塞分析
1.阻塞与非阻塞是什么?
阻塞:做某件事情,直到完成,除非超时,如果没有完成,继续等待;
非阻塞:做一件事情,尝试着做,如果说不能做完,就不做了,意思就是直接返回,如果能够做完,就做。
2.传统IO模型,它存在哪些阻塞点。
Socket编程
运行该程序,打开自己电脑的终端,输入
telnet localhost 7777
即可连接socket服务
按ctrl + ] 字符输入
单个客户端连接
import ch.qos.logback.core.net.SyslogOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 传统socket
*/
public class TraditionalSocketDemo {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(7777);
System.out.println("服务端启动。。。");
while(true) {
//获取socket客户端套接字
Socket socket =serverSocket.accept();
System.out.println("有新客户端连接上来了。。。");
InputStream is = socket.getInputStream();
byte[] b = new byte[1024];
while(true){
int data = is.read(b);
if(data != -1) {
String info = new String(b,0,data);
System.out.println(info);
}else {
break;
}
}
}
}
}
阻塞点:
1.Socket socket = serverSocket.accept();
2. int data = is.read(b);
多个客户端连接
import ch.qos.logback.core.net.SyslogOutputStream;
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;
/**
* 传统socket
*/
public class TraditionalSocketDemo {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(7777);
ExecutorService threadPool = Executors.newCachedThreadPool();
System.out.println("服务端启动。。。");
while(true) {
//获取socket客户端套接字
Socket socket =serverSocket.accept();
threadPool.execute(new Runnable(){
public void run() {
System.out.println("有新客户端连接上来了。。。");
InputStream is = null;
try {
is = socket.getInputStream();
byte[] b = new byte[1024];
while(true){
int data = is.read(b);
if(data != -1) {
String info = new String(b,0,data);
System.out.println(info);
}else {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
}
缺点:占用系统的开销
优点:线程进行一对一服务
3.NIO模型
1.增加了一个重要角色(Selector),主要负责调度和监控客户端和服务端(调度器)
2.由阻塞方式改成了非阻塞(non-blocking)。
阻塞点:
this.selector.select();
真正关心的阻塞点是:读取数据
import ch.qos.logback.core.net.SyslogOutputStream;
import org.springframework.expression.spel.ast.Selection;
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;
public class NioSocketDemo {
private Selector selector; //通道选择器(管理器)
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); //用户发起连接的请求
System.out.println("服务已启动。。。");
}
public void listenSelector() throws IOException {
//轮询监听selector
while(true) {
//等待客户连接
//select模型,多路复用
this.selector.select();
Iterator<SelectionKey> iteKey = this.selector.selectedKeys().iterator();
while(iteKey.hasNext()){
SelectionKey key = iteKey.next();
iteKey.remove();
//处理请求
handler(key);
}
}
}
/**
* 处理客户端请求
* @param key
*/
private void handler(SelectionKey key)throws IOException{
if(key.isAcceptable()){
//处理请求
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ); //接受客户端发送的信息时,需要给通道设置读的权限
}else if(key.isReadable()){
//处理读的事件
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int readData = socketChannel.read(buffer);
if(readData > 0) {
String info = new String(buffer.array(),"GBK").trim();
System.out.println("服务端接受数据:" + info);
}else {
System.out.println("客户端关闭了。。。");
key.cancel();
}
}
}
public static void main(String[] args) throws IOException{
NioSocketDemo nio = new NioSocketDemo();
nio.initServer(8888);
nio.listenSelector();
}
}