本文原题“从实践角度重新理解BIO和NIO”,原文由Object分享,为了更好的内容表现力,即时通讯在收录时有改动。本文的收录已征得原作者同意,转载请注明原作者信息,请尊重原创知识。
public class Server { public static void main(String[] args) { byte[] buffer = new byte[1024]; try { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务器已启动并监听8080端口"); while (true) { System.out.println(); System.out.println("服务器正在等待连接..."); Socket socket = serverSocket.accept(); System.out.println("服务器已接收到连接请求..."); System.out.println(); System.out.println("服务器正在等待数据..."); socket.getInputStream().read(buffer); System.out.println("服务器已经接收到数据"); System.out.println(); String content = new String(buffer); System.out.println("接收到的数据:" + content); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Consumer { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1",8080); socket.getOutputStream().write("向服务器发数据".getBytes()); socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Consumer { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1",8080); String message = null; Scanner sc = new Scanner(System.in); message = sc.next(); socket.getOutputStream().write(message.getBytes()); socket.close(); sc.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Server { public static void main(String[] args) { byte[] buffer = new byte[1024]; try { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务器已启动并监听8080端口"); while (true) { System.out.println(); System.out.println("服务器正在等待连接..."); Socket socket = serverSocket.accept(); new Thread(new Runnable() { @Override public void run() { System.out.println("服务器已接收到连接请求..."); System.out.println(); System.out.println("服务器正在等待数据..."); try { socket.getInputStream().read(buffer); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("服务器已经接收到数据"); System.out.println(); String content = new String(buffer); System.out.println("接收到的数据:" + content); } }).start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Server { public static void main(String[] args) { byte[] buffer = new byte[1024]; try { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务器已启动并监听8080端口"); while (true) { System.out.println(); System.out.println("服务器正在等待连接..."); //阻塞1:等待连接时阻塞 Socket socket = serverSocket.accept(); System.out.println("服务器已接收到连接请求..."); System.out.println(); System.out.println("服务器正在等待数据..."); //阻塞2:等待数据时阻塞 socket.getInputStream().read(buffer); System.out.println("服务器已经接收到数据"); System.out.println(); String content = new String(buffer); System.out.println("接收到的数据:" + content); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Server { public static void main(String[] args) throws InterruptedException { ByteBuffer byteBuffer = ByteBuffer.allocate(1024); try { //Java为非阻塞设置的类 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8080)); //设置为非阻塞 serverSocketChannel.configureBlocking(false); while(true) { SocketChannel socketChannel = serverSocketChannel.accept(); if(socketChannel==null) { //表示没人连接 System.out.println("正在等待客户端请求连接..."); Thread.sleep(5000); }else { System.out.println("当前接收到客户端请求连接..."); } if(socketChannel!=null) { //设置为非阻塞 socketChannel.configureBlocking(false); byteBuffer.flip();//切换模式 写-->读 int effective = socketChannel.read(byteBuffer); if(effective!=0) { String content = Charset.forName("utf-8").decode(byteBuffer).toString(); System.out.println(content); }else { System.out.println("当前未收到客户端消息"); } } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
不难看出,在这种解决方案下,虽然在接收客户端消息时不会阻塞,但是又开始重新接收服务器请求,用户根本来不及输入消息,服务器就转向接收别的客户端请求了,换言之,服务器弄丢了当前客户端的请求。
public class Server { public static void main(String[] args) throws InterruptedException { ByteBuffer byteBuffer = ByteBuffer.allocate(1024); List<SocketChannel> socketList = new ArrayList<SocketChannel>(); try { //Java为非阻塞设置的类 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8080)); //设置为非阻塞 serverSocketChannel.configureBlocking(false); while(true) { SocketChannel socketChannel = serverSocketChannel.accept(); if(socketChannel==null) { //表示没人连接 System.out.println("正在等待客户端请求连接..."); Thread.sleep(5000); }else { System.out.println("当前接收到客户端请求连接..."); socketList.add(socketChannel); } for(SocketChannel socket:socketList) { socket.configureBlocking(false); int effective = socket.read(byteBuffer); if(effective!=0) { byteBuffer.flip();//切换模式 写-->读 String content = Charset.forName("UTF-8").decode(byteBuffer).toString(); System.out.println("接收到消息:"+content); byteBuffer.clear(); }else { System.out.println("当前未收到客户端消息"); } } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
在解决方案一中,我们采用了非阻塞方式,但是发现一旦非阻塞,等待客户端发送消息时就不会再阻塞了,而是直接重新去获取新客户端的连接请求,这就会造成客户端连接丢失。 而在解决方案二中,我们将连接存储在一个list集合中,每次等待客户端消息时都去轮询,看看消息是否准备好,如果准备好则直接打印消息。 可以看到,从头到尾我们一直没有开启第二个线程,而是一直采用单线程来处理多个客户端的连接,这样的一个模式可以很完美地解决BIO在单线程模式下无法处理多客户端请求的问题,并且解决了非阻塞状态下连接丢失的问题。
struct pollfd{ int fd; short events; short revents; }
来源:即时通讯网 - 即时通讯开发者社区!
轻量级开源移动端即时通讯框架。
快速入门 / 性能 / 指南 / 提问
轻量级Web端即时通讯框架。
详细介绍 / 精编源码 / 手册教程
移动端实时音视频框架。
详细介绍 / 性能测试 / 安装体验
基于MobileIMSDK的移动IM系统。
详细介绍 / 产品截图 / 安装体验
一套产品级Web端IM系统。
详细介绍 / 产品截图 / 演示视频
引用:Chowing 发表于 2021-01-19 22:12 感觉这篇入门是最友好的
引用:纯情小火鸡 发表于 2021-12-22 15:30 看了不少本站的一些文章了,真的是都很有深度,而且能让看的人真正的理解
精华主题数超过100个。
连续任职达2年以上的合格正式版主
为论区做出突出贡献的开发者、版主等。
Copyright © 2014-2024 即时通讯网 - 即时通讯开发者社区 / 版本 V4.4
苏州网际时代信息科技有限公司 (苏ICP备16005070号-1)
Processed in 0.140625 second(s), 46 queries , Gzip On.