基于Non Blocking IO的网络Server端实现及客户端代码例子

本文仅为开发时的经验记录,以备记录相关开发的备份。
1、首先是将创建一个NIOServer的Spring IOC中的对象,如代码所示:

@Configuration
public class SocketConfig {

    @Value("${socket.server.port}")
    private int port;

    @Autowired
    private ExecutorService executorService;

    @Bean("nioServer")
    NIOServer nioServer() {
        NIOServer socketServer = new NIOServer(port, executorService);
        Thread thread = new Thread(socketServer);
        thread.start();
        return socketServer;
    }
}

2、NIOServer类的代码,如下:

package com.xxx.crm.esb.esbfunction.socket;

import com.xxx.crm.common.context.SpringContext;
import com.xxx.crm.esb.dispatch.EsbRequestDispatcher;
import com.xxx.crm.esb.esbfunction.model.qo.request.basic.BasicEsbReq;
import com.xxx.crm.esb.init.EsbErrorCode;
import com.xxx.crm.esb.util.ResponseToXml;

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;
import java.util.concurrent.ExecutorService;

public class NIOServer implements Runnable {

    private Selector selector;
    // 16kb缓冲区大小
    private ByteBuffer readBuffer = ByteBuffer.allocate(1024 * 16);
    // 16kb缓冲区大小
    private ByteBuffer writeBuffer = ByteBuffer.allocate(1024 * 16);

    // 获取针对esb请求的分发器,也可以获取具体的业务对象userServiceImpl等
    private EsbRequestDispatcher esbRequestDispatcher = SpringContext
            .getContext().getBean("esbRequestDispatcherImpl", EsbRequestDispatcher.class);

    public NIOServer(int port, ExecutorService threadPool) {
        try {
            // 打开多路复用器
            this.selector = Selector.open();
            // 打开服务器通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 设置服务器通道为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            // 绑定端口ip地址
            serverSocketChannel.bind(new InetSocketAddress(port));
            // 把服务器通道注册到多路复用器上,并且监听阻塞事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 输出启动文字
            System.out.println("NIO server start...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while(true) {
            try {
                // 多路复用器开始监听
                this.selector.select();
                // 返回多路复用器已经选择的结果集
                Iterator<SelectionKey> selectionKeyIterator = this.selector
                        .selectedKeys().iterator();
                // 遍历
                while(selectionKeyIterator.hasNext()) {
                    // 获取一个选择的元素
                    SelectionKey key = selectionKeyIterator.next();
                    // 选择之后从iterator容器中移除
                    selectionKeyIterator.remove();

                    if(key.isValid()) {
                        // 阻塞状态
                        if(key.isAcceptable()) {
                            this.accept(key);
                        }
                        // 可读装填
                        if(key.isReadable()) {
                            this.read(key);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 功能描述: serverSocketChannel读取数据,可以在此写好反馈
     * @param key
     */
    private void read(SelectionKey key) {
        try {
            // 清空缓冲区的旧数据
            this.readBuffer.clear();
            // 获取之前注册的socket通道对象,这个对象就是与客户端通信的管道
            SocketChannel channel = (SocketChannel)key.channel();
            // 读取数据,非阻塞好处在于,触发事件的时候,数据就都准备好了
            int count = channel.read(readBuffer);
            // 若没有数据,关闭通道,移除注册的key
            if(count == -1) {
                key.channel().close();
                key.cancel();
                return;
            }
            // 若有数据则进行读取,需要先切换到读取的模式
            this.readBuffer.flip();
            // 创建byte数组,接受缓冲区的数据
            byte[] bytes = new byte[this.readBuffer.remaining()];
            // 接收数据
            this.readBuffer.get(bytes);
            // 调取具体的业务逻辑代码内容
            String resultXml = this.processSocket(new String(bytes));
            // 读取完成处理之后返回的数据
            afterRead(channel, resultXml);
        } catch (Exception e) {

        }
    }

    /**
     * 功能描述:读取完之后返回数据
     * @param channel
     * @param resultXml
     * @throws IOException
     */
    private void afterRead(SocketChannel channel, String resultXml) throws IOException {
        // 数据方法缓冲区
        writeBuffer.put(resultXml.getBytes());
        // 复位buffer
        writeBuffer.flip();
        // 写出数据
        channel.write(writeBuffer);
        // 清空数据
        writeBuffer.clear();
    }

    /**
     * 功能描述:实现监听状态的处理方法,一般是针对serverSocketChannel对象
     * 拿出监听状态的服务器端channel,该状态的channel已经监听到channel,准备好将
     * 这个监听的channel转换为等待读取的状态,注册到多路复用器上。
     * @param key
     */
    private void accept(SelectionKey key) {
        try {
            // 获取服务器通道
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
            // 执行阻塞方法,获得与客户端的通道
            SocketChannel socketChannel = serverSocketChannel.accept();
            // 设置阻塞模式-非阻塞
            socketChannel.configureBlocking(false);
            // 注册到多路复用器上,并设置读取标识
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 具体业务代码,
     * @param requestStr 客户端请求参数
     * @return 业务处理后的返回数据
     */
    private String processSocket(String requestStr) {
        String outputStr;
        BasicEsbReq basicEsbReq = new BasicEsbReq();
        try {
            outputStr = esbRequestDispatcher.dataBeanFix(requestStr, basicEsbReq);
        } catch (Exception e) {
            basicEsbReq.setReturnShortCode(EsbErrorCode.SYS_ERROR);
            e.printStackTrace();
            outputStr = ResponseToXml.errorResponseXml(e.getMessage(), basicEsbReq);
        }
        return outputStr;
    }
}

3、客户端代码简写:

public static void main(String[] args) throws UnknownHostException, IOException {
        //设置Url和端口, 127.0.0.1代表本机
        String url = "127.0.0.1";
        int port = 6666;
        //与服务端建立连接
        Socket socket = new Socket(url,port);
        System.out.println("已经连接到服务端");
        //建立连接后输出流到服务端
        OutputStream outputStream = socket.getOutputStream();
        String message = "我是Shares,666";
        outputStream.write(message.getBytes("UTF-8"));

        InputStream in = socket.getInputStream();
        int len = 0;

        byte[] bytes = new byte[1024];
        StringBuilder sb = new StringBuilder();
        while((len=in.read(bytes)) != -1) {
            sb.append(new String(bytes, 1, len, "UTF-8"));
            if(len < 1024) {
                break;
            }
        }
        System.out.println("Server says "+ sb.toString());
        outputStream.close();
        in.close();
        socket.close();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java网络IO实现方式有BIONIO、AIO三种。 1. BIOBlocking I/O) BIO是Java最早的一种网络IO实现方式。它通过阻塞式的方式来进行网络IO操作,即一个线程在进行IO操作时会一直阻塞,直到IO操作完成。这种方式的优点是编程简单易懂,但缺点是并发性能较差,不能满足高并发的需求。 2. NIONon-blocking I/O) NIO是Java在1.4版本引入的一种新的网络IO实现方式。NIO的核心是Selector(选择器),它能够监控多个通道的状态,当某个通道有数据可读或可写时,Selector会通知相应的线程进行处理。NIO采用非阻塞式的方式进行IO操作,可以支持更高的并发性能。但是,NIO编程难度较大,需要掌握较多的概念和技巧。 3. AIO(Asynchronous I/O) AIO是Java在1.7版本引入的一种新的网络IO实现方式。AIO采用异步的方式进行IO操作,即一个线程在进行IO操作时不需要一直阻塞,而是可以继续执行其他任务,当IO操作完成后再由系统通知线程进行处理。AIO可以支持更高的并发性能,并且编程模型比NIO更加简单。但是,AIO的兼容性不如NIO,需要在操作系统和JVM等多个方面进行支持。 ### 回答2: 在Java网络IO可以通过不同的方式实现,这些方式包括BIO(阻塞IO)、NIO(非阻塞IO)和AIO(异步IO)。 BIO是最早的实现方式,也是最简单直观的方式。它的特点是使用阻塞模式,即当一个线程在进行IO操作时,其他线程必须等待IO操作完成才能继续执行。在BIO,每个连接都需要一个独立的线程来处理,这可能导致服务器资源浪费,无法满足高并发的需求。 NIO是在Java 1.4引入的新IO模型,相较于BIO,它具有更高的并发处理能力。NIO使用了多路复用器(Selector)来管理多个连接,一个线程可以通过一个选择器同时处理多个连接的IO操作,从而避免了每个连接都需要一个独立线程的问题。 AIO是在Java 1.7引入的新IO模型,也被称为NIO.2。AIO是基于事件和回调机制的,它的特点是IO操作后不需要对应的线程进行阻塞等待,而是通过回调方式来通知IO操作已经完成。AIO适合处理连接数较多且连接时间较长的场景,例如聊天服务器。 综上所述,BIONIO和AIO是Java实现网络IO的三种方式。BIO适用于连接数较小的场景,NIO适用于连接数较多但连接时间较短的场景,而AIO适用于连接数较多且连接时间较长的场景。选择适合的IO模型能够提高服务器的并发处理能力和效率。 ### 回答3: JAVA有三种主要的网络IO实现方式,分别是BIOBlocking IO)、NIONon-blocking IO)和AIO(Asynchronous IO)。 BIO是JAVA IO最传统的模型,采用阻塞方式进行IO操作。在BIO模型,每个连接都需要一个独立的线程来处理,当有大量的连接时,就需要大量线程,会导致性能下降。 NIO是JAVA IO的改进版本,引入了通道(Channel)和缓冲区(Buffer)的概念。NIO采用非阻塞方式进行IO操作,可以提高IO的效率。在NIO模型,一个线程可以处理多个连接,通过选择器(Selector)来监听多个通道的事件,当有事件发生时,线程可以进行处理。 AIO是JAVA NIO的进一步改进,引入了异步通道(AsynchronousChannel)和回调机制。AIO采用异步方式进行IO操作,可以在IO操作完成之后再通知线程进行处理,而不需要线程一直等待IO操作完成。AIO适用于高并发的场景,可以大大提高系统的吞吐量和性能。 总结来说,BIO适用于连接数较少且业务处理较简单的场景;NIO适用于连接数较多但每个连接的并发处理量不大的场景;AIO适用于连接数较多且每个连接的并发处理量大的场景。选择合适的IO模型可以根据业务需求和实际场景进行选择,来提高系统的性能和并发能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值