1、在没有NIO的套件的时候,我们都是使用Socket来进行网络编程
案例如下:
1.1、服务端实现:
public static void main(String[] args) {ServerSocket serverSocket = null;try {//1、构建一个SocketServer 监听端口8080,这就表示一个进程,当前线程就是main线程,也可以叫IO线程。serverSocket = new ServerSocket(8080);/*2、IO线程进行accept,accept的含义就是等待客户端连接的时候,当前IO线程会进行接受,在没有客户端进行连接的时候,IO线程就会一直处于阻塞状态,知道有客户端连接后进行处理。有客户端连接后就会返回一个Socket实例,用于表示客户端的连接信息。*/Socket socket = serverSocket.accept();//3、获取连接的客户端的输入流。InputStream inputStream = socket.getInputStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));//4、读取客户端输入流中的消息数据String message = bufferedReader.readLine();System.out.println("收到客户端消息:" + message);//5、获取客户端连接的输出流,用于向客户端输出数据。OutputStream outputStream = socket.getOutputStream();BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));bufferedWriter.write("回复客户端消息:回复客户端。\n");bufferedWriter.flush();} catch (IOException e) {e.printStackTrace();}}
1.2、客户端实现:
public static void main(String[] args) {try {//1、构建一个套接字,连接上服务端Socket socket = new Socket("127.0.0.1",8080);//2、获取当前客户端连接的输出流,用于向服务端输出数据OutputStream outputStream = socket.getOutputStream();BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));//记得结尾添加一个换行符 表示结束。bufferedWriter.write("你好,我是客户端1!\n");bufferedWriter.flush();//3、获取当前客户端连接的输入流,用于接收服务端的回复数据InputStream inputStream = socket.getInputStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String message = bufferedReader.readLine();System.out.println(message);} catch (IOException e) {e.printStackTrace();}}
以上的服务端都是阻塞的,也就是说,我们上面的实现,IO线程会一直阻塞,知道数据准备OK后才会进入到runing过程。
2、NIO中的阻塞式网络编程
2.1、NIO中阻塞式服务端实现
@Testpublic void nioBlockingServer() throws IOException {//1、打开一个网络通道,也就是会创建一个进程,当前的线程就是main线程,也可以叫IO线程ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//2、绑定当前的进程监听的端口为9898serverSocketChannel.bind(new InetSocketAddress(9898));/*3、同样IO线程进行accept,accept的含义就是等待客户端连接的时候,当前IO线程会进行接受,在没有客户端进行连接的时候,IO线程就会一直处于阻塞状态,知道有客户端连接后进行处理。有客户端连接后就会返回一个Socket实例,用于表示客户端的连接信息。*/SocketChannel socketChannel = serverSocketChannel.accept();//3、分配一个缓冲区,用于存储数据。ByteBuffer buffer = ByteBuffer.allocate(1024);//4、读取连接过来的客户端中发送的数据,然后进行打印输出。while (socketChannel.read(buffer) != -1) {buffer.flip();System.out.println(Thread.currentThread().getName() + " : " + new String(buffer.array()));buffer.clear();}//5、回复客户端。buffer.put("服务端收到你的回复了".getBytes());buffer.flip();socketChannel.write(buffer);socketChannel.close();}
2.2、NIO中阻塞式客户端实现
@Testpublic void nioBlockingClient1() throws IOException {//1、构建一个套接字连接到服务端,返回一个socket通道。SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));//2、分配一个缓冲区,且想缓冲区中存入消息。ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put("你好我是客户端1!".getBytes());buffer.flip();//3、发送消息到服务端。socketChannel.write(buffer);//4、告诉服务端发送结束。在非NIO的系统中,是使用换行符来表示发送消息结束的。socketChannel.shutdownOutput();buffer.clear();//5、读取服务端的回复消息进行进行输出。while (socketChannel.read(buffer) != -1) {buffer.flip();System.out.println(new String(buffer.array()));buffer.clear();}}
3、阻塞式的利弊分析
3.1、上面的案例中,都是阻塞式的IO网络编程实现。只要服务端启动,就会创建一个进程,监听端口,这个启动的main线程就可以理解为IO线程。如果没有客户端连接,那么就会一直阻塞在accept()方法的地方,一旦有客户端连接过来就会建立好连接并返回一个Socket 或SocketChannel实例,这个实例就包含了客户端的连接信息,可以使用此实例进行客户端发送的数据读取,以及向客户端回复数据。
3.2、使用多线程来处理多个客户端的连接以及回复,在我们的案例中只有一个客户端会连接成功,因为输出以后IO线程就被结束掉了,因此人们就想出了,使用多线程来同时处理多个客户端的请求,但是每条线程还是阻塞的。
3.3、阻塞式的这种网模型会严重影响CPU的利用率,性能很差。因此随着计算机的发展,有了非阻塞式的IO,下一遍文章做详细分析。