首先感谢鲁班学院免费贡献的JAVA精品学习课程,让我白嫖学到了东西!!!
我的学习代码:NIO-Demo: NIO,BIO学习
一.普通socket
1.先来了解一下socket编程,来个Client和Server
public class Client {
public static void main(String[] args) {
Socket socket;
try {
socket= new Socket("127.0.0.1",9001);
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
socket.getOutputStream().write(str.getBytes());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Server {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9001);
while (true){
Socket socket = serverSocket.accept();//阻塞等待客户端连接
System.out.println("有人连上了服务器");
byte [] msg = new byte[1024];
socket.getInputStream().read(msg);//阻塞等待读取客户端发来的消息
System.out.println("收到消息:"+new String(msg));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述代码中,Server端单线程(main线程)一直在等待用户加入连接,假设客户端1加入了连接没有发送消息,此时read()仍在等待,所以客户端1没发完消息客户端2就不能加入连接,问题来了-->一个服务器同时只能服务一个客户端
2.BIO模型
BIO针对上述问题做了优化,主线程循环accpet(),等待客户端加入连接,当连接后使用线程(线程池)去执行read(),每一个连接维护一个读取消息的线程,这样一个Server可以同时支持多个客户端连接,问题来了--->线程资源耗尽
BIO的Server代码
public class BIOServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9001);
while (true){
Socket socket = serverSocket.accept();
System.out.println("有人连上了服务器");
//如失灵请创建thread类实现此功能
new Thread(()->{
while (true){
try {
byte [] msg = new byte[1024];
socket.getInputStream().read(msg);
System.out.println("收到消息:"+new String(msg));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码效果:多个客户端可以同时连接服务器
3.NIO
针对BIO的缺点,NIO采用单线程实现,原理主线程循环读取selector中的key,有的话做key关联的行为类
public class NIOServer {
private ServerSocketChannel channel;
private Selector selector;
public NIOServer(int port) {
try {
selector = Selector.open();
channel = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(port);
channel.socket().bind(address);
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT);
//自定义的accept处理器
key.attach(new AcceptHandler(channel,selector));
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
try {
while (!Thread.interrupted()) {
System.out.println("触发。。。。。。。");
if (selector.select() == 0) {
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey i = iterator.next();
//自定义行为类
NIOHandler handler = (NIOHandler)i.attachment();
handler.handle();
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NIOServer server = new NIOServer(9001);
server.start();
}
}
单线程一直监听selector,有连接或者消息进来,线程中断,处理完然后重新进去,如此实现非阻塞等待,非阻塞读取消息
定义行为类