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中阻塞式服务端实现
@Test
public void nioBlockingServer() throws IOException {
//1、打开一个网络通道,也就是会创建一个进程,当前的线程就是main线程,也可以叫IO线程
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2、绑定当前的进程监听的端口为9898
serverSocketChannel.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中阻塞式客户端实现
@Test
public 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,下一遍文章做详细分析。