ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java 实现NIO网络通信

2022-03-30 23:35:27  阅读:210  来源: 互联网

标签:网络通信 Java NIO java read selector import socketChannel 客户端


IO模型 IO模型就是说用什么样的通道进行数据的发送和接收,Java共支持3种网络编程IO模式:BIO,NIO,AIO   BIO   BIO(Blocking IO) 同步阻塞模型,一个客户端连接对应一个处理线程

 

 不难看出 BIO的劣势在于如果客户端过多  会产生很多的线程

BIO代码实例

server端

 1 package com.io.bio;
 2 
 3 import java.io.IOException;
 4 import java.net.ServerSocket;
 5 import java.net.Socket;
 6 
 7 public class SocketServer {
 8     public static void main(String[] args) throws IOException {
 9         ServerSocket serverSocket = new ServerSocket(9000);
10         while (true) {
11             System.out.println("等待连接。。");
12             //阻塞方法 accept会阻塞线程  直到有连接事件发生
13             Socket clientSocket = serverSocket.accept();
14             System.out.println("有客户端连接了。。");
15             handler(clientSocket);
16 
17             /*new Thread(new Runnable() {
18                 @Override
19                 public void run() {
20                     try {
21                         handler(clientSocket);
22                     } catch (IOException e) {
23                         e.printStackTrace();
24                     }
25                 }
26             }).start();*/
27         }
28     }
29 
30     private static void handler(Socket clientSocket) throws IOException {
31         byte[] bytes = new byte[1024];
32         System.out.println("准备read。。");
33         //接收客户端的数据,阻塞方法,没有数据可读时就阻塞 一直到客户端发送消息后  跳出阻塞 执行后续的逻辑
34         int read = clientSocket.getInputStream().read(bytes);
35         System.out.println("read完毕。。");
36         if (read != -1) {
37             System.out.println("接收到客户端的数据:" + new String(bytes, 0, read));
38         }
39         clientSocket.getOutputStream().write("HelloClient".getBytes());
40         clientSocket.getOutputStream().flush();
41     }
42 }

 

client端

 1 //客户端代码
 2 public class SocketClient {
 3     public static void main(String[] args) throws IOException {
 4         Socket socket = new Socket("localhost", 9000);
 5         //向服务端发送数据
 6         socket.getOutputStream().write("HelloServer".getBytes());
 7         socket.getOutputStream().flush();
 8         System.out.println("向服务端发送数据结束");
 9         byte[] bytes = new byte[1024];
10         //接收服务端回传的数据
11         socket.getInputStream().read(bytes);
12         System.out.println("接收到服务端的数据:" + new String(bytes));
13         socket.close();
14     }
15 }

劣势:

  1.accept  read 是阻塞方法  如果没有相关动作  会浪费资源

  2.连接后,如果需要异步处理消息,过造成线程过多  服务器压力增大

应用场景:

  BIO 方式适用于连接数目比较小且固定的架构, 这种方式对服务器资源要求比较高, 但程序简单易理解。  

NIO(Non Blocking IO) 同步非阻塞IO

  服务端一个线程可以处理多个连接请求,并且把这些请求都注册到多路复用器(selector)中,selector通过轮询的方式,获取到客户端相应的事件,比如accept read 

示例代码

 1 package com.socket.nio.server;
 2 
 3 import java.io.IOException;
 4 import java.net.InetSocketAddress;
 5 import java.nio.ByteBuffer;
 6 import java.nio.channels.*;
 7 import java.util.Iterator;
 8 import java.util.Set;
 9 
10 public class NIOSocketServer {
11 
12     public static void main(String[] args) throws IOException {
13         //创建一个serversocket通道,并且设置为非阻塞
14         ServerSocketChannel socketChannel = ServerSocketChannel.open();
15         //设置为非阻塞  默认是阻塞
16         socketChannel.configureBlocking(false);
17         //服务端绑定一个端口号  实时监听所有的客服端
18         socketChannel.socket().bind(new InetSocketAddress(8888));
19 
20         //创建一个selector多路复用器  并且把serversocektchannel注册到selector中
21         Selector selector = Selector.open();
22         //注册serversocketchannel到selector  并且告知selector当前这个serversocketchannel 对连接感兴趣
23         socketChannel.register(selector, SelectionKey.OP_ACCEPT);
24 
25         //循环监听处理客户端事件
26         while (true) {
27             System.out.println("等待连接");
28             //轮询监听channel  如果有事件发生  将跳出监听  处理客户端事件
29             //select()是阻塞方法   accpet()也是一个阻塞方法
30             selector.select();
31             //客户端的请求  会被监听到  并且把key放入到一个集合中  可以异步处理对应消息
32             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
33             while (iterator.hasNext()) {
34                 SelectionKey selectionKey = iterator.next();
35                 iterator.remove();
36                 handleSelectionKey(selectionKey);
37             }
38         }
39 
40     }
41 
42     private static void handleSelectionKey(SelectionKey selectionKey) throws IOException {
43         if (selectionKey.isAcceptable()) {
44             //处理连接事件
45             ServerSocketChannel socketChannel = (ServerSocketChannel) selectionKey.channel();
46             //accept()是一个阻塞方法  但是因为此时处理的是连接事件  必定又客户端连接过来  所以此方法
47             //马上就执行,继续处理其他事件
48             SocketChannel channel = socketChannel.accept();
49             channel.configureBlocking(false);
50             //服务端接下来对read操作感兴趣
51             channel.register(selectionKey.selector(), SelectionKey.OP_READ);
52         } else if (selectionKey.isReadable()) {
53             //处理read事件
54             System.out.println("服务端收到数据");
55             SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
56             //创建读取缓冲区
57             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
58             //read方法是一个阻塞方法  但read是响应事件,此时必定有客户端发送了消息 处理的是read事件  必定马上执行  不会阻塞线程
59             int len = socketChannel.read(byteBuffer);
60             if (len != -1) {
61                 System.out.println("服务端收到数据;" + new String(byteBuffer.array(), 0, len));
62             }
63             byteBuffer = ByteBuffer.wrap("hello".getBytes());
64             socketChannel.write(byteBuffer);
65             selectionKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
66             socketChannel.close();
67         }
68     }
69 }

客户端代码实例

 1 package com.socket.nio.client;
 2 
 3 import java.io.IOException;
 4 import java.net.InetSocketAddress;
 5 import java.nio.ByteBuffer;
 6 import java.nio.channels.SelectionKey;
 7 import java.nio.channels.Selector;
 8 import java.nio.channels.SocketChannel;
 9 import java.util.Iterator;
10 
11 public class NIOClient {
12 
13     private Selector selector;
14 
15     public static void main(String[] args) throws Exception {
16         NIOClient nioClient=new NIOClient();
17         nioClient.initSocket("127.0.0.1",8888);
18         nioClient.connect();
19     }
20 
21     private void initSocket(String ip, int port) throws IOException {
22         //创建一个socket通道  并且设置为非阻塞
23         SocketChannel socketChannel = SocketChannel.open();
24         socketChannel.configureBlocking(false);
25 
26         //创建一个selector
27         this.selector = Selector.open();
28         //连接服务端
29         socketChannel.connect(new InetSocketAddress(ip,port));
30         //注册当前的socketchannel到selector
31         socketChannel.register(selector, SelectionKey.OP_CONNECT);
32     }
33 int count =1;
34     public void connect() throws  Exception{
35         while(true){
36             //对于客户端来说  一个客户端一个channel  此时select()是一个非阻塞方法
37             //可以通过输出count,确定此时的select()是非阻塞的
38             selector.select();
39             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
40             System.out.println(count++);
41             while (iterator.hasNext()){
42                 SelectionKey selectionKey = iterator.next();
43                 iterator.remove();
44                 SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
45                 if (selectionKey.isConnectable()) {
46                     if(socketChannel.isConnectionPending()){
47                         socketChannel.finishConnect();
48                     }
49                     socketChannel.configureBlocking(false);
50                     //发送消息给服务端
51                     ByteBuffer byteBuffer = ByteBuffer.wrap("hello server".getBytes());
52                     socketChannel.write(byteBuffer);
53                     socketChannel.register(this.selector, SelectionKey.OP_READ);
54                 }else if(selectionKey.isReadable()){
55                     ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
56                     int len = socketChannel.read(byteBuffer);
57                     if(len!=-1){
58                         System.out.println("收到服务端消息:"+new String(byteBuffer.array(),0,len));
59                     }
60                 }
61                // socketChannel.close();
62             }
63         }
64     }
65 }

劣势

因为selector多路复用器  会轮询所有的channel  如果有10000个channel  只有10个有动作  那么就会造成大量的轮询浪费  是不合理的

所以,在之后的处理中 NIO使用了epoll的方式  把轮询 变成响应(监听)

标签:网络通信,Java,NIO,java,read,selector,import,socketChannel,客户端
来源: https://www.cnblogs.com/cy0523/p/16079720.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有