UDP和TCP编程

1. UDP

特点:不建立连接;将数据源和目的封装成数据包中;

涉及类 DatagramSocketDatagramPacket

public class DatagramSocket extends Object
描述:此类表示用来发送和接收数据报包的套接字。在 DatagramSocket 上总是启用 UDP 广播发送。

  • 构造方法
    • public DatagramSocket():构造数据报套接字并将其绑定到本地主机上任何可用的端口
    • public DatagramSocket(int port):创建数据报套接字并将其绑定到本地主机上的指定端口
    • public DatagramSocket(int port, InetAddress laddr):创建数据报套接字,将其绑定到指定的本地地址
    • public DatagramSocket(SocketAddress bindaddr):创建数据报套接字,将其绑定到指定的本地套接字地址
  • 常用方法
    • public void send(DatagramPacket p):从此套接字 发送 数据报包。
    • public void receive(DatagramPacket p):从此套接字 接收 数据报包。

public final class DatagramPacket extends Object
描述:此类表示数据报包。数据报包用来实现无连接包投递服务。

  • 构造方法
    • public DatagramPacket(byte[] buf, int length):构造 DatagramPacket,用来 接收 长度为 length 的数据包。
    • public DatagramPacket(byte[] buf, int length, InetAddress address, int port):构造数据报包,用来将长度为 length 的包 发送 到指定主机上的指定端口号。
    • public DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):构造数据报包,用来将长度为 length 偏移量为 offset 的包 发送 到指定主机上的指定端口号。
    • public DatagramPacket(byte[] buf, int length, SocketAddress address):构造数据报包,用来将长度为 length 的包 发送 到指定主机上的指定端口号。
    • public DatagramPacket(byte[] buf, int offset, int length, SocketAddress address):构造数据报包,用来将长度为 length 偏移量为 offset 的包 发送 到指定主机上的指定端口号。
  • 常用方法
    • public InetAddress getAddress():获取数据报将要发往该机器或者是从该机器接收的 InetAddress 地址(通常为 IP 地址)。
    • public SocketAddress getSocketAddress():获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
    • public byte[] getData():返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量 offset 处开始,持续 length 长度。
    • public byte[] getLength():返回数据缓冲区大小。

client发送端思路

  1. 建立udp的socket服务。
  2. 将要发送的数据封装成数据包。
  3. 通过udp的socket服务,将数据包发送出。
  4. 关闭资源。

client发送端代码

public class UdpClientDemo {
    public static void main(String[] args) throws IOException {
        // 1、创建发送端DatagramSocket对象
        DatagramSocket ds = new DatagramSocket();

        // 创建数据
        byte[] bys = "hello,udp,我来了".getBytes();
        // 长度
        int length = bys.length;
        // IP地址对象
        InetAddress address = InetAddress.getByName("192.168.0.106");
        // 端口
        int port = 10086;

        // 2、创建数据,并把数据打包
        // DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        DatagramPacket dp = new DatagramPacket(bys, length, address, port);

        // 3、调用Socket对象的发送方法发送数据包
        // public void send(DatagramPacket p)
        ds.send(dp);

        // 释放资源
        ds.close();
    }
}

server接收端思路

  1. 建立udp的socket服务。
  2. 通过receive方法接收数据。
  3. 将收到的数据存储到数据包对象中。
  4. 通过数据包对象的功能来完成对接收到数据进行解析。
  5. 可以对资源进行关闭。

server接收端代码

public class UdpServerDemo {
    public static void main(String[] args) throws IOException {
        // 1、创建接收端DatagramSocket对象
        DatagramSocket ds = new DatagramSocket(10086);

        // 2、创建一个数据包(接收容器)
        // DatagramPacket(byte[] buf, int length)
        byte[] bys = new byte[1024];
        int length = bys.length;
        DatagramPacket dp = new DatagramPacket(bys, length);

        // 3、调用Socket对象的接收方法接收数据
        // public void receive(DatagramPacket p)
        ds.receive(dp); // 阻塞式

        // 4、解析数据包,并显示在控制台
        // 获取对方的ip
        // public InetAddress getAddress()
        String ip = dp.getAddress().getHostAddress();

        // public byte[] getData():获取数据缓冲区
        // public int getLength():获取数据的实际长度
        byte[] bys2 = dp.getData();
        int len = dp.getLength();
        String s = new String(bys2, 0, len);
        System.out.println(ip + "传递的数据是:" + s);

        // 释放资源
        ds.close();
    }
}

运行服务端后,ds.receive(dp)该方法会一直阻塞等待数据。当客户端运行后,服务端打印 “192.168.0.106传递的数据是:hello,udp,我来了” 结束。

UDP案例

案例一

客户端:从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。
服务端:一直输出客户端发来的数据。

客户端-UdpClientDemo:
public class UdpClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建发送端的Socket对象
        DatagramSocket ds = new DatagramSocket();

        // 封装键盘录入数据
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line = null;
        while ((line = br.readLine()) != null) {
            if ("886".equals(line)) {
                break;
            }

            // 创建数据并打包
            byte[] bys = line.getBytes();
            // DatagramPacket dp = new DatagramPacket(bys, bys.length,
            // InetAddress.getByName("192.168.0.106"), 12345);
            DatagramPacket dp = new DatagramPacket(bys, bys.length,
                    InetAddress.getByName("192.168.0.255"), 12345);

            // 发送数据
            ds.send(dp);
        }

        // 释放资源
        ds.close();
    }
}

服务端-UdpServerDemo:
public class UdpServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建接收端的Socket对象
        DatagramSocket ds = new DatagramSocket(12345);

        while (true) {
            // 创建一个包裹
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length);

            // 接收数据
            ds.receive(dp);

            // 解析数据
            String ip = dp.getAddress().getHostAddress();
            String s = new String(dp.getData(), 0, dp.getLength());
            System.out.println("from " + ip + " data is : " + s);
        }

        // 释放资源
        // 接收端应该一直开着等待接收数据,是不需要关闭
        // ds.close();
    }
}

上述 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.0.255"), 12345);中,192.168.0.255为广播地址,该网段所有主机均可接收。

案例二

发送和接收程序分别用线程进行封装,完成一个UDP的聊天程序。

客户端-UdpClientDemopublic class UdpClientDemo implements Runnable{
    public DatagramSocket ds;

    public UdpClientDemo(DatagramSocket ds) {
        this.ds = ds;
    }

    @Override
    public void run() {
        try {
            // 封装键盘录入数据
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    System.in));
            String line = null;
            while ((line = br.readLine()) != null) {
                if ("886".equals(line)) {
                    break;
                }

                // 创建数据并打包
                byte[] bys = line.getBytes();
                DatagramPacket dp = new DatagramPacket(bys, bys.length,
                        InetAddress.getByName("192.168.0.255"), 12306);

                // 发送数据
                ds.send(dp);
            }

            // 释放资源
            ds.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端-UdpServerDemopublic class UdpServerDemo implements Runnable {
    public DatagramSocket ds;

    public UdpServerDemo(DatagramSocket ds) {
        this.ds = ds;
    }

    @Override
    public void run() {
        try {
            while (true) {
                // 创建一个包裹
                byte[] bys = new byte[1024];
                DatagramPacket dp = new DatagramPacket(bys, bys.length);

                // 接收数据
                ds.receive(dp);

                // 解析数据
                String ip = dp.getAddress().getHostAddress();
                String s = new String(dp.getData(), 0, dp.getLength());
                System.out.println("from " + ip + " data is : " + s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

聊天室:
public class ChatRoom {
    public static void main(String[] args) throws IOException {
        DatagramSocket dsSend = new DatagramSocket();
        DatagramSocket dsReceive = new DatagramSocket(12306);

        UdpClientDemo client = new UdpClientDemo(dsSend);
        UdpServerDemo server = new UdpServerDemo(dsReceive);

        Thread t1 = new Thread(client);
        Thread t2 = new Thread(server);

        t1.start();
        t2.start();
    }
}

2. TCP

特点:建立连接;形成传输数据的通道,在连接中进行大数据量传输;

涉及类SocketServerSocket

public class Socket extends Object
描述:此类实现客户端套接字

  • 构造函数
    • public Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。
    • public Socket(InetAddress address, int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号
    • public Socket(String host, int port, InetAddress localAddr, int localPort):创建一个套接字并将其连接到指定远程主机上的指定远程端口。
      • host - 远程主机名,或者为 null,表示回送地址。
      • port - 远程端口
      • localAddr - 要将套接字绑定到的本地地址
      • localPort - 要将套接字绑定到的本地端口
    • public Socket(InetAddress address, int port, InetAddress localAddr, int localPort):创建一个套接字并将其连接到指定远程地址上的指定远程端口。
  • 常用方法
    • public void connect(SocketAddress endpoint):将此套接字连接到服务器。
    • public SocketChannel getChannel():返回与此数据报套接字关联的唯一 SocketChannel 对象(如果有)。
    • public InputStream getInputStream():返回此套接字的输入流。
    • public OutputStream getOutputStream():返回此套接字的输出流。
    • public void close():关闭此套接字。

public class ServerSocket extends Object
描述:此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。

  • 构造函数
    • public ServerSocket(int port):创建绑定到特定端口的服务器套接字。
    • public ServerSocket(int port, int backlog, InetAddress bindAddr):使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
  • 常用方法
    • public Socket accept():侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞
    • public void close():关闭此套接字。
    • public ServerSocketChannel getChannel():返回与此套接字关联的唯一 ServerSocketChannel 对象(如果有)。

client发送端思路

  1. 建立客户端的socket服务,并明确要连接的服务器。
  2. 如果连接建立成功,就表明已经建立了数据传输的通道。就可以在该通道通过IO进行数据的读取和写入。该通道称为Socket流,Socket流中既有读取流,也有写入流。
  3. 通过Socket对象的方法,获取读取流、写入流。
  4. 通过流对象可以对数据进行传输。
  5. 如果传输数据完毕,关闭资源。

client发送端代码

public class TcpClientDemo {
    public static void main(String[] args) throws IOException {
        // 1、创建客户端Socket对象
        Socket s = new Socket("192.168.0.106", 22222);

        // 键盘录入数据
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        // 2、包装通道内的输出流
        PrintWriter printWriter = new PrintWriter(s.getOutputStream(), true);

        String line = null;
        while ((line = br.readLine()) != null) {
            // 键盘录入数据要自定义结束标记
            if ("886".equals(line)) {
                break;
            }

            // 3、往通道内写入数据
            printWriter.println(line);

            // 4、包装通道内的输入流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(s.getInputStream()));

            // 5、读取通道内数据
            String readLine = bufferedReader.readLine();
            System.out.println("接收数据:" + readLine);
        }

        // 释放资源
        // bw.close();
        // br.close();
        s.close();
    }
}

server接收端思路

  1. 建立服务器端的socket服务,绑定一个端口。
  2. 通过accept()方法获取客户端对象,再通过获取到的客户端对象的流和客户端进行通信。
  3. 通过客户端的获取流对象的方法,读取数据或者写入数据。
  4. 如果服务完成,需要关闭客户端,然后关闭服务器。但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的。

server接收端代码

public class TcpServerDemo {
    public static void main(String[] args) throws IOException {
        // 1、创建服务器Socket对象
        ServerSocket ss = new ServerSocket(22222);

        // 2、监听客户端连接
        Socket s = ss.accept();

        // 3、包装通道内容的输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String line = null;

        while ((line = br.readLine()) != null) {
            System.out.println(s.getInetAddress().getHostAddress() + "---" + line);

            // 4、包装通道内的输出流
            PrintWriter printWriter = new PrintWriter(s.getOutputStream(), true);
            // 5、往通道内写入数据
            printWriter.println("服务器响应数据" + line);
        }

        // br.close();
        s.close();
        // ss.close();
    }
}

先启动服务端,再启动客户端。
111
接收数据:服务器响应数据111
22
接收数据:服务器响应数据22
886

TCP案例

上传文件

UploadClient:
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 1、创建客户端Socket对象
        Socket s = new Socket("192.168.0.106", 11111);

        // 2、封装文本文件
        BufferedReader br = new BufferedReader(new FileReader(
                "E:\\demo\\1.txt"));

        // 3、封装通道内流
        PrintWriter printWriter = new PrintWriter(s.getOutputStream(),true);

        String line = null;
        while( (line = br.readLine()) != null){
            printWriter.println(line);
        }

        s.close();
        br.close();
    }
}

UploadServer:
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 1、创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(11111);

        // 2、监听客户端连接
        Socket s = ss.accept();

        // 3、封装通道内的流
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        // 4、封装文本文件
        PrintWriter printWriter = new PrintWriter("E:\\demo\\download.java");

        String line = null;
        while((line = br.readLine()) != null){
            printWriter.println(line);
        }

        s.close();
        printWriter.close();
    }
}

通过Socket通讯,将文件远程下载到本地。

上传图片

图片属于二进制文件,需要使用字节流

UploadClient:
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.0.106", 11111);

        // 封装文本文件
        BufferedInputStream br = new BufferedInputStream(new FileInputStream(
                "E:\\demo\\林青霞.jpg"));

        // 封装通道内流
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());

        byte[] bytes = new byte[1024];
        int len = 0;
        while( (len = br.read(bytes)) != -1){
            bos.write(bytes);
            bos.flush();
        }

        s.close();
        br.close();
    }
}

UploadServer:
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(11111);

        // 监听客户端连接
        Socket s = ss.accept();

        // 封装通道内的流
        BufferedInputStream br = new BufferedInputStream(s.getInputStream());
        // 封装文本文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\demo\\mn.jpg"));

        byte[] bytes = new byte[1024];
        int len = 0;
        while((len = br.read(bytes)) != -1){
            bos.write(bytes);
        }

        s.close();
        bos.close();
    }
}

下载图片并完成通知

服务器接收图片完成后,通知客户端上传成功。

UploadClientpublic class UploadClient {
    public static void main(String[] args) throws IOException {
        // 1、创建客户端Socket对象
        Socket s = new Socket("192.168.0.106", 11111);

        // 2、封装文本文件
        BufferedInputStream br = new BufferedInputStream(new FileInputStream(
                "E:\\demo\\林青霞.jpg"));

        // 3、封装通道内流
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());

        byte[] bytes = new byte[1024];
        int len = 0;
        while( (len = br.read(bytes)) != -1){
            bos.write(bytes);
            bos.flush();
        }

        // 4、接收反馈
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine(); // 阻塞
        System.out.println(client);

        s.close();
        br.close();
    }
}

UploadServerpublic class UploadServer {
    public static void main(String[] args) throws IOException {
        // 1、创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(11111);

        // 2、监听客户端连接
        Socket s = ss.accept();

        // 3、封装通道内的流
        BufferedInputStream br = new BufferedInputStream(s.getInputStream());
        // 4、封装文本文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\demo\\mn.jpg"));

        byte[] bytes = new byte[1024];
        int len = 0;
        while((len = br.read(bytes)) != -1){  // 阻塞
            bos.write(bytes);
            bos.flush();
        }

        // 5、给出反馈
        BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
        bwServer.write("图片上传成功");
        bwServer.newLine();
        bwServer.flush();

        s.close();
        bos.close();
    }
}

服务器增加反馈,并往通道写入数据;客户端从通道读取数据,并打印。
运行发现,E:\demo\mn.jpg图片复制成功,但是服务端、客户端一直阻塞,且没有任何数据打印。

为什么呢?
我们加入打印语句

UploadClientpublic class UploadClient {
    public static void main(String[] args) throws IOException {
        ...
        while( (len = br.read(bytes)) != -1){
            bos.write(bytes);
            bos.flush();
        }
        System.out.println("client 111");

        // 接收反馈
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine(); // 阻塞
        System.out.println(client);
        System.out.println("client 222");

        s.close();
        br.close();
    }
}

UploadServer:
public class UploadServer {
    public static void main(String[] args) throws IOException {
        ...
        while((len = br.read(bytes)) != -1){  // 阻塞
            bos.write(bytes);
            bos.flush();
            System.out.println("server 111");
        }
        System.out.println("server 222");
        ...
    }
}

运行结果:
UploadClient:
client 111

UploadServer:
server 111
server 111
server 111
....
server 111

运行结果发现,客户端发送数据完成后并没有从通道内获取到反馈的数据,一直阻塞等待。服务器从通道内while循环读取数据并没有退出循环,所以没有打印“server 222”。

读取文本文件是可以以null作为结束信息的,但是,通道内是不能这样结束信息的。所以,服务器不知道客户端结束,就会一直read阻塞等待客户端发送数据,不会执行后续的写入操作。而客户端一直read阻塞等待服务器反馈,不会执行关闭操作,形成了死锁等待

Socket对象提供了一种解决方案:

  • public void shutdownOutput():关闭输出流。
  • public void shutdownInput():关闭输入流。

改进后:

UploadClientpublic class UploadClient {
    public static void main(String[] args) throws IOException {
        ...
        while( (len = br.read(bytes)) != -1){
            bos.write(bytes);
            bos.flush();
            System.out.println("len="+len);
        }
        
        //Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了。
        s.shutdownOutput();
        
        // 接收反馈
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine();
        System.out.println(client);

        s.close();
        br.close();
    }
}

UploadServer:
不需要改变

图片复制成功,并且客户端收到反馈 “图片上传成功”。

多线程下载服务器图片

思路:
服务端循环接收请求,如果收到请求,启动一个线程处理。

服务器-UploadServerpublic class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(11111);

        while (true) {
            Socket s = ss.accept();
            new Thread(new UserThread(s)).start();
        }
    }
}

处理线程:
public class UserThread implements Runnable {
    private Socket s;

    public UserThread(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {
        try {
            // 封装通道内的流
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    s.getInputStream()));
            // 封装文本文件
            // BufferedWriter bw = new BufferedWriter(new
            // FileWriter("Copy.java"));

            // 为了防止名称冲突
            String newName = System.currentTimeMillis() + ".java";
            BufferedWriter bw = new BufferedWriter(new FileWriter(newName));

            String line = null;
            while ((line = br.readLine()) != null) { // 阻塞
                bw.write(line);
                bw.newLine();
                bw.flush();
            }

            // 给出反馈
            BufferedWriter bwServer = new BufferedWriter(
                    new OutputStreamWriter(s.getOutputStream()));
            bwServer.write("文件上传成功");
            bwServer.newLine();
            bwServer.flush();

            // 释放资源
            bw.close();
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

客户端连接服务器,读取文件上传,完成后通知服务器。

客户端-UploadClientpublic class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.0.106", 11111);

        // 封装文本文件
        // BufferedReader br = new BufferedReader(new FileReader(
        // "InetAddressDemo.java"));
        BufferedReader br = new BufferedReader(new FileReader(
                "ReceiveDemo.java"));
        // 封装通道内流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));

        String line = null;
        while ((line = br.readLine()) != null) { // 阻塞
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        // Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了
        s.shutdownOutput();

        // 接收反馈
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine(); // 阻塞
        System.out.println(client);

        // 释放资源
        br.close();
        s.close();
    }
}

3. BIO/NIO/AIO

  • BIO 编程:

Blocking IO: 同步阻塞的编程方式。
BIO编程方式通常是在 JDK1.4 版本之前常用的编程方式。编程实现过程为:首先在服务端启动一个 ServerSocket 来监听网络请求,客户端启动 Socket 发起网络请求,默认情况下 ServerSocket 会建立一个线程来处理此请求,如果服务端没有线程可用,客户端则会阻塞等待或遭到拒绝。且建立好的连接,在通讯过程中,是同步的。在并发处理效率上比较低。

同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

  • NIO 编程:

NIO 编程:Unblocking IO(New IO): 同步非阻塞的编程方式。
NIO 本身是基于事件驱动思想来完成的,其主要想解决的是 BIO 的大并发问题,NIO基于Reactor,当 socket 有流可读或可写入 socket 时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。

在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有 BIO 一样的问题。

  • AIO编程:

Asynchronous IO: 异步非阻塞的编程方式。
AIO 基于Proactor,与 NIO 不同,当进行读写操作时,只须直接调用 API 的read()或write()方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入 read 方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将 write 方法传递的流写入完毕时,操作系统主动通知应用程序。

即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:AsynchronousSocketChannelAsynchronousServerSocketChannelAsynchronousFileChannelAsynchronousDatagramChannel

Java NIO浅析
带你彻底搞懂高性能网络模式Reactor 和 Proactor

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会叫的狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值