2

网络编程中,服务端提供IP和监听端口,客户端通过服务端的IP和端口发送请求,进行通信。这篇是讲传统的同步阻塞模型--BIO。

客户端

BioClient主要是创建一个Socket,并通过Socket把请求发送服务端,代码如下:

public class BioClient {
    public static void main(String[] args) {
        try {
            // 通过服务IP,端口创建一个Socket
            Socket socket = new Socket(Const.IP, Const.PORT);
            // 获取Socket输出流
            OutputStream outputStream = socket.getOutputStream();
            // 输出流
            outputStream.write("hello world".getBytes());
            // 关闭流
            outputStream.close();
            // 关闭Socket
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端

服务端先创建一个ServerSocket,用于监听指定端口的连接请求。accept方法会一直阻塞,直到新的连接建立,建立后,创建一个Socket与客户端进行通信。在读取输入流的时候,br.readLine()也是阻塞的。

public class BioServer {
    public static void main(String[] args) {
        try {
            //获取ServerSocket,绑定服务器的端口
            ServerSocket serverSocket = new ServerSocket(Const.PORT);
            while (true) {
                // 创建一个Socket接收连接
                Socket socket = serverSocket.accept();
                // 获取输入流
                InputStream inputStream = socket.getInputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                String message;
                while (null != (message = br.readLine())) {
                    System.out.println(message);
                }
                // 关闭流
                inputStream.close();
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上面的实例中,有个比较大的缺点,就是同时只能处理一个连接,要想处理多个并发客户端,我们可以简单地用多线程来管理,每次有新的sockert,创建一个线程或则放入线程池来处理。
我们把sockert的部分抽出来:

public class BioServerHandler implements Runnable {
    private Socket socket;

    public BioServerHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            // 获取输入流
            InputStream inputStream = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            String message;
            while (null != (message = br.readLine())) {
                System.out.println(message);
            }
            // 关闭流
            inputStream.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

创建线程的方式:

public class BioServer2 {
    public static void main(String[] args) {
        try {
            //获取ServerSocket,绑定服务器的端口
            ServerSocket serverSocket = new ServerSocket(Const.PORT);
            while (true) {
                // 创建一个Socket接收连接
                Socket socket = serverSocket.accept();
                new Thread(new BioServerHandler(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

创建线程池的方式:

public class BioServer3 {
    private static ExecutorService executorService
            = Executors.newFixedThreadPool(5);

    public static void main(String[] args) {
        try {
            //获取ServerSocket,绑定服务器的端口
            ServerSocket serverSocket = new ServerSocket(Const.PORT);
            while (true) {
                // 创建一个Socket接收连接
                Socket socket = serverSocket.accept();
                executorService.execute(new BioServerHandler(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BioServer2和BioServer3通过线程,可以比BioServer同时处理更多的客户端请求,但是依然存在着不足:在任何时候,都有大量的线程处于休眠状态,只能等待输入或则输出数据就绪,浪费资源。为此,java提供了另外一直方式-NIO。


大军
847 声望183 粉丝

学而不思则罔,思而不学则殆


« 上一篇
java8系列
下一篇 »
网络编程 - NIO