目录
BIO、NIO区别
概念上
具体体现
BIO
NIO
BIO、NIO区别
概念上
BIO:阻塞IO。
NIO:非阻塞IO。
具体体现
BIO
BIO服务端代码。具体阻塞地方写在了代码注释里。
public static void main(String[] args) {
try {
ServerSocket socket = new ServerSocket(9000);
while (true){
System.out.println("等待连接");
Socket accept = socket.accept();//未获取连接则阻塞
System.out.println("有客户端连接");
handler(accept);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handler(Socket accept) throws IOException {
byte[] bytes = new byte[1024];
System.out.println("准备读取");
int read = accept.getInputStream().read(bytes); //未读到数据则阻塞
System.out.println("读取完毕");
if (read!=-1){
System.out.println("数据为:"+new String(bytes,0,read));
}
//数据未处理完,则阻塞下一条数据
}
BIO局限性太大,以上代码一次只允许处理一个连接。我们稍加改造,即使我们把handler方法放在多线程里,可以处理多个消息。但是仍存在很大的问题:如请求过多造成内存溢出,或是有多个连接但是是空连接一直不发送信息(造成资源浪费)。
BIO早已不能满足日益增长的用户量。如果你并发量很小,还是可以用BIO的。
NIO
NIO服务端代码。精华写在了注释里。
static Listlist = new ArrayList (); public static void main(String[] args) throws IOException { //设置端口 ServerSocketChannel serverSocket = ServerSocketChannel.open(); serverSocket.socket().bind(new InetSocketAddress(9000)); //设置连接为非阻塞 serverSocket.configureBlocking(false); System.out.println("服务启动成功"); //非阻塞的一直循环 while (true){ SocketChannel accept = serverSocket.accept(); //如果有连接 if(accept!=null){ System.out.println("有客户端连接"); //设置管道为非阻塞 accept.configureBlocking(false); list.add(accept); } Iterator iterator = list.iterator(); //遍历管道去拿数据,read是不阻塞的。 while (iterator.hasNext()){ SocketChannel sc = iterator.next(); ByteBuffer buffer = ByteBuffer.allocate(128); int read = sc.read(buffer); if(read>0){ System.out.println("收到消息:"+new String(buffer.array())); }else if(read==-1){ iterator.remove(); System.out.println("客户端断开"); } } } }
天生可以支持多个连接,可以设置连接和read为非阻塞。一个线程处理多个客户端。后边使用遍历连接去接受数据。
以上NIO程序存在的问题:while(iterator.hasNext())遍历效率低。
为了解决这个问题,引入了selector多路复用器。把连接事件和读写事件分开,如果有写入事件,只需要遍历写入的socket。代码如下,解释在注释。
public static void main(String[] args) throws IOException {
//开放端口
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9000));
serverSocket.configureBlocking(false);
//打开selector处理channel。linux帮我们创建了一个epoll实例(epoll_create)
Selector selector = Selector.open();
//把serverSocket注册到selector,并指定监听连接
SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务启动成功");
while (true){
//阻塞等待需要处理的事件发生
selector.select(); //底层调用epoll_ctl函数,启用监听。调用epoll_wait去看另一个
// 集合是否有活动事件,没有则阻塞。
//获取在selector里边的全部事件
Set selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
//遍历处理
while (iterator.hasNext()){
SelectionKey key = iterator.next();
//如果事件代表连接
if(key.isAcceptable()){
ServerSocketChannel server=(ServerSocketChannel)key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
System.out.println("客户端连接成功");
}else if(key.isReadable()){ //如果事件代表可读
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
int len = socketChannel.read(byteBuffer);
if (len>0){
System.out.println("接收消息:"+new String(byteBuffer.array()));
}else if(len==-1){
System.out.println("断开连接");
socketChannel.close();
}
}
iterator.remove();
}
}
}
这样就解决了无效遍历的问题。想要更了解epoll,可以读这个文章
Java中套接字(socket)以及有关概念入门_运气不好努力来凑-CSDN博客套接字入门理解,还不懂的可以入门学习https://blog.csdn.net/wai_58934/article/details/122848372?spm=1001.2014.3001.5501今天先到这里,白嫖直播公开课去,明天继续



