栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

网络编程 阻塞模式、非阻塞模式、多路复用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

网络编程 阻塞模式、非阻塞模式、多路复用

网络编程 阻塞模式

我们必定接触过的JDK提供的Socket网络编程,它其实就是比较典型的阻塞模式。接下来就详细讲讲这阻塞二字。
我们服务器端启动后,通过accept()方法来处理客户端的连接,当客户端连接成功后,会通过read()方法来接收客户端发送的数据。这两个方法都是阻塞的,程序运行到accept()方法就会被阻塞住,当有客户端连接成功才会解除阻塞继续运行,然后运行到read()方法又会阻塞,直到客户端发送数据菜户解除阻塞。
那么就会出现这样的一个情况,当一个客户端连接成功后,在read()方法阻塞住,这时候另一个客户端是连接不上的。
如果执行accept()方法等待客户端连接时,这个时候其他已连接的客户端发送数据是接收不到的。这就是阻塞。当然也就解决方法,那就是来一个客户端连接就开一个线程。
服务器端代码:

package com.hs.netty.network;

import com.google.common.base.Charsets;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;


public class ServerSocketChannelTest {
    public static void main(String[] args) throws IOException {
        // 首先创建一个缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(16);
        // 创建服务器
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 设置端口为8080
        serverSocketChannel.bind(new InetSocketAddress(8080));
        // 创建一个集合,存放每个客户端的SocketChannel、
        List socketChannelList = new ArrayList<>();

        // 建立客户端的连接,因为不会连接一个客户端程序就结束了,所以需要放在死循环中
        while (true){
            // accept() 建立与客户端之间的连接,程序运行到这里会阻塞
            SocketChannel socketChannel = serverSocketChannel.accept();
            System.out.println("客户端成功连接服务器!!!");
            // 得到一个客户端连接后存入list集合中
            socketChannelList.add(socketChannel);
            // 然后再遍历所有客户端连接,从SocketChannel中获取客户端传递过来的数据
            for (SocketChannel channel : socketChannelList) {
                // read()方法也会让程序阻塞
                channel.read(byteBuffer);
                byteBuffer.flip();
                System.out.println("客户端传递的数据为:" + Charsets.UTF_8.decode(byteBuffer).toString());
                byteBuffer.clear();
            }
        }
    }
}

客户端代码

package com.hs.netty.network;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;


public class SocketChannelTest {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        // 连接服务器
        socketChannel.connect(new InetSocketAddress("localhost",8080));
        System.out.println("客户端已连接服务器!!!");
    }
}
非阻塞模式

因为阻塞模式下,出现的问题很明显,就有了非阻塞模式的概念,它就是让accept() 方法和 read() 方法 在运行时不会阻塞。
具体做法就是让ServerSocketChannel和SocketChannel都调用configureBlocking(false);方法即可设置为非阻塞模式

public class ServerSocketChannelTest {
    public static void main(String[] args) throws IOException {
        // 首先创建一个缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(16);
        // 创建服务器
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 设置为非阻塞模式,影响的就是serverSocketChannel.accept()方法------------------------
        serverSocketChannel.configureBlocking(false);
        // 设置端口为8080
        serverSocketChannel.bind(new InetSocketAddress(8080));
        // 创建一个集合,存放每个客户端的SocketChannel
        List socketChannelList = new ArrayList<>();

        // 建立客户端的连接,因为不会连接一个客户端程序就结束了,所以需要放在死循环中
        while (true) {
            // accept() 建立与客户端之间的连接,阻塞模式下程序运行到这里会阻塞,有客户端连接了才会继续往下执行
            // 如果是非阻塞状态下,没有客户端连接这里就会返回一个null---------------------------
            SocketChannel socketChannel = serverSocketChannel.accept();
            
            if (socketChannel != null) {
                System.out.println("客户端成功连接服务器--->" + socketChannel);
                // 客户端SocketChannel也可以设置为非阻塞状态 方法还是一样的,这里影响的就是下面read()方法------------
                socketChannel.configureBlocking(false);
                // 得到一个客户端连接后存入list集合中
                socketChannelList.add(socketChannel);
            }
            // 然后再遍历所有客户端连接,从SocketChannel中获取客户端传递过来的数据
            for (SocketChannel channel : socketChannelList) {
                // 阻塞模式下read()方法也会让程序阻塞,不过上面设置了客户端SocketChannel为非阻塞模式
                // 非阻塞模式下,如果没有读取到数据,该方法会返回0
                int read = channel.read(byteBuffer);
                if (read != 0) {
                    byteBuffer.flip();
                    System.out.println("客户端传递的数据为:" + Charsets.UTF_8.decode(byteBuffer).toString());
                    byteBuffer.clear();
                }
            }
        }
    }
}

非阻塞模式下也有一个很严重的问题,那就是即使没有连接事件 或者是 客户端没有向服务器发送数据,但是服务器端的程序还是会一直执行死循环,这样会很消耗cpu的资源。

多路复用

紧接着就有了多路复用来解决非阻塞模式下不停进行死循环的问题。
单线程可以配合Selector完成对多个Channel可读写事件的监控,这称之为多路复用。它在非阻塞的基础上加了事件概念,只有事件发生了才会让线程继续运行,如果没有事件发生就是阻塞状态。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/490169.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号