Reactor模式-多线程

本文详细介绍了多线程Reactor模式的工作流程,对比单线程模式,通过线程池管理多个线程处理socket连接的读写请求,提高并发处理能力。探讨了socket在不同条件下可读和可写的状态,以及提供了简单的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.流程图

在这里插入图片描述
可以看出 相比单线程的reactor模式来说,多线程的模式使用了线程池来管理多个线程来处理各个socket连接的读写请求,而比较简单的连接请求还是交给一个主线程来处理。只有发生读写时间的时候才会分发给各个线程来处理读写逻辑。

2.什么情况下socket是可读和可写的

引用的是https://blog.youkuaiyun.com/nosix/article/details/77484562这篇文章的介绍
可读的条件
1.socket的接收缓冲区中的数据字节大于等于该socket的接收缓冲区低水位标记的当前大小。对这样的socket的读操作将不阻塞并返回一个大于0的值(也就是返回准备好读入的数据)。对于TCP和UDP的socket而言,其缺省值为1.
2.该连接的读这一半关闭(也就是接收了FIN的TCP连接),也就是四次握手里面的服务端接收到服务端的FIN之后并且给客户端返回ack之后,对这样的socket的读操作将不阻塞并返回0
3.给监听套接字准备好新连接。就是说这个套接字属于一个监听套接字。
4.有一个socket有异常错误条件待处理.对于这样的socket的读操作将不会阻塞,并且返回一个错误(-1),errno则设置成明确的错误条件。
可写的条件
1.该套接字的发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的大小;
2.该套接字的写半部关闭,继续写会产生SIGPIPE信号;(https://blog.youkuaiyun.com/u010982765/article/details/79038062)
3.非阻塞模式下,connect返回之后,该套接字连接成功或失败;
4.该套接字有错误待处理,对这样的套接字的写操作将返回-1。

3.简单的代码实现

相比于上一个单线程模式下的Reactor模式,我只是把TcpHandler类进行了一下改变,主要就是使用多线程来处理多个socket的读写事件 下面是代码实现

public class TcpHanler implements  Procrss {

    private SelectionKey selectionKey;

    private SocketChannel socketChannel;
  //当前处于的状态  初始化就是还没有被处理 使用valotile关键字保持可见性和顺序性
    public   volatile  boolean  statu_now ;

    //所有的类共同使用这个线程池
    public  static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(20, 1000, 5, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy());

    public TcpHanler(SelectionKey selectionKey, SocketChannel socketChannel) {
        this.selectionKey = selectionKey;
        this.socketChannel=socketChannel;
        statu_now= true;
    }



    @Override
    public  void process(){
                try {
                    //如果有别的线程正在操作那么就不创建新的线程去处理它
                    if (!statu_now){
                        return;
                    }else{
                        threadPoolExecutor.execute(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    readAndSend();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    //如果服务端关闭了就会出现java.net.SocketException: Software caused connection abort: recv failed异常
                    IOUtils.closeQuietly(socketChannel);
                }
    }


    private synchronized void readAndSend() throws IOException {
        if (!statu_now){
            return;
        }
        statu_now=false;
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        int read = 0;
        try {
            read = socketChannel.read(byteBuffer);
            System.out.println("处理线程名字是"+Thread.currentThread().getName());
        } catch (IOException e) {
            System.out.println("发生异常的线程名字是"+Thread.currentThread().getName());
        }
        //即使客户端已经关闭 但是服务端还是会接收到客户端的读状态  但是此时读的值为-1
        if (read==-1){
            System.out.println(socketChannel.socket().getLocalAddress()+"客户端已经关闭");
            selectionKey.cancel();
            socketChannel.close();
            return ;
        }
        //这里需要注意 read的返回值问题
        //如果对方主动关闭了socket 那么会返回-1
        //如果对方没有主动关闭 它不会返回-1

        //有三种情况会返回0
        // 1.socketChannel里面没有数据可读了
        // 2.Buffer里面position和limit相等为0了
        // 3. 客户端的程序发送完毕
        while (read > 0) {
            System.out.println(read);
            //切换状态 记得调用flip方法 否则无法读取到数据
            byteBuffer.flip();
            System.out.println(socketChannel.socket().getRemoteSocketAddress().toString() + ":" + Charset.forName("utf-8").decode(byteBuffer).toString());
            byteBuffer.compact();
            read = socketChannel.read(byteBuffer);
            send();
        }
        //如果是1 代表socket客户端已经关闭 就不发送消息了
        statu_now=true;
    }
    private void send() throws IOException {
        String str = "Your message has sent to "
                + socketChannel.socket().getLocalSocketAddress().toString() + "\r\n";
        System.out.println(" start sending back");
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put(str.getBytes("utf-8"));
        while (byteBuffer.hasRemaining()){
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
        }
        System.out.println(" finish sending back");
       /* state=0;*/
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值