Java进阶作业四:使用事件驱动IO写一个EchoServer

写在开头

本文学习使用Java原生的NIO API提供的事件驱动IO模型来编写一个EchoServer,所谓EchoServer就是,客户端发送给服务器一段消息,服务器接收到之后将消息原样返回给客户端。
示例代码没有处理Channel的关闭和如何关闭服务器,重点在于学习如何使用NIO的API。

服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class EventBasedServer {

    public void start(int port) throws IOException {
        ServerSocketChannel server = ServerSocketChannel.open();
        server.bind(new InetSocketAddress(port));
        server.configureBlocking(false);
        Selector selector = Selector.open();

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        SocketChannel client;
        String msg = "NULL";
        System.out.println("server starting...");

        // 服务器的channel关心accept操作,所以注册accept事件
        server.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 阻塞选择所有的key
            selector.select();

            // 取出可处理的key
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if (key.isAcceptable()) {
                    client = ((ServerSocketChannel) key.channel()).accept();
                    client.configureBlocking(false);

                    // 收到了客户端的连接,接下来关心读操作,所以注册读事件
                    client.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    client = (SocketChannel) key.channel();
                    int len = client.read(buffer);
                    msg = readStringFromByteBuffer(buffer, len);
                    System.out.println("receive from client:" + msg);

                    // 读取数据之后,接下来关心写操作,所以注册写事件
                    client.register(selector, SelectionKey.OP_WRITE);
                } else if (key.isWritable()) {
                    client = (SocketChannel) key.channel();
                    buffer.put(msg.getBytes());
                    buffer.flip();
                    writeDataToChannel(client, buffer);

                    // 写完数据之后,接下来关心读操作,所以注册读事件
                    client.register(selector, SelectionKey.OP_READ);
                } else {
                    System.out.println("do nothing");
                }

                keyIterator.remove();
            }

        }
    }

    /**
     * 将buffer中指定数量的字节转换为字符串
     */
    private String readStringFromByteBuffer(ByteBuffer buffer, int count) {
        byte[] msgBytes = new byte[count];
        for (int i = 0; i < count; i++) {
            msgBytes[i] = buffer.get(i);
        }
        buffer.clear();
        return new String(msgBytes);
    }

    /**
     * 将buffer中的数据写入到Channel
     */
    public void writeDataToChannel(SocketChannel channel, ByteBuffer buffer) throws IOException {
        while (buffer.hasRemaining()) {
            channel.write(buffer);
        }

        buffer.clear();
    }

    public static void main(String[] args) throws IOException {
        EventBasedServer server = new EventBasedServer();
        server.start(8888);
    }
}

客户端

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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class EventBasedClient {

    public void connect(String ip, int port) throws IOException {
        SocketChannel client = SocketChannel.open(new InetSocketAddress(ip, port));
        client.configureBlocking(false);

        Selector selector = Selector.open();
        // 客户端连接上服务器之后,关心写操作,因此注册写事件
        client.register(selector, SelectionKey.OP_WRITE);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String msg;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if (key.isWritable()) {
                    System.out.println("client ready! please input msg");
                    msg = scanner.nextLine();
                    buffer.put(msg.getBytes());
                    buffer.flip();
                    writeDataToChannel(client, buffer);

                    // 写操作完成之后,关心读操作,因此注册读事件
                    client.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    client = (SocketChannel) key.channel();
                    int len = client.read(buffer);
                    msg = readStringFromByteBuffer(buffer, len);
                    System.out.println("server response:" + msg);

                    // 读操作完成之后,关心写操作,因此注册写事件
                    client.register(selector, SelectionKey.OP_WRITE);
                } else {
                    System.out.println("do nothing");
                }

                keyIterator.remove();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        EventBasedClient client = new EventBasedClient();
        client.connect("127.0.0.1", 8888);
    }

    /**
     * 将buffer中指定数量的字节转换为字符串
     */
    private String readStringFromByteBuffer(ByteBuffer buffer, int count) {
        byte[] msgBytes = new byte[count];
        for (int i = 0; i < count; i++) {
            msgBytes[i] = buffer.get(i);
        }
        buffer.clear();
        return new String(msgBytes);
    }

    /**
     * 将buffer中的数据写入到Channel
     */
    public void writeDataToChannel(SocketChannel channel, ByteBuffer buffer) throws IOException {
        while (buffer.hasRemaining()) {
            channel.write(buffer);
        }

        buffer.clear();
    }
}

运行结果

posted @ 2021-05-27 15:29  陈玉林  阅读(78)  评论(0编辑  收藏  举报