Java的BIO案例教程
上一节我们对
Socket编程步骤
服务器监听:服务器启动后,它会有一个线程一直启动,等待着客户端端连接。它会定义好自己的端口号。
客户端请求:客户端端套接字提出连接请求,要连接的目标是服务端的套接字。客户端必须要指明服务端套接字的地址和端口号。
连接确认:当服务端收到客户端的连接请求就会响应客户端套接字的请求,建立一个新的线程处理客户端的请求。
案例
我们新建两个 java 项目,一个是服务端,一个是客户端。代码结构如下:
服务端:
客户端:
服务端 BioServer
package net.haicoder.server;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class BioServer{
public static void main(String[] args){
int port = 9999;
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
//一直监听,是否有客户端请求过来
while (true) {
Socket socket = serverSocket.accept();
//每次都会新建一个线程,来处理接收到到请求
new Thread(new SocketHandler(socket)).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
serverSocket = null;
}
}
}
static class SocketHandler implements Runnable{
Socket socket = null;
public SocketHandler(Socket socket){
this.socket = socket;
}
@Override
public void run(){
BufferedReader reader = null;
PrintWriter writer = null;
try {
//读取数据,BIO 是面向流到,所以定义流 BufferedReader 来读取数据
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
//将收到的数据返回给客户端
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
String readMessage = null;
while (true) {
if ((readMessage = reader.readLine()) == null) {
break;
}
System.out.println("server reading ........" + readMessage);
//将数据返回给客户端
writer.println("server recive : " + readMessage);
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
reader = null;
}
if (writer != null) {
writer.close();
writer = null;
}
}
}
}
}
客户端 BioClient
package net.haicoder.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class BioClient{
public static void main(String[] args){
//服务端到 ip 地址
String host = "127.0.0.1";
//和服务端到端口号一致
int port = 9999;
Socket socket = null;
BufferedReader reader = null;
PrintWriter writer = null;
//接收键盘输入数据
Scanner scanner = new Scanner(System.in);
try {
socket = new Socket(host, port);
String message = null;
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
writer = new PrintWriter(socket.getOutputStream(), true);
while (true) {
message = scanner.nextLine();
if (message.equals("exit")) {
break;
}
//数据发送服务端
writer.println("客户端输入:" + message);
writer.flush();
System.out.println(reader.readLine());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
reader = null;
}
if (writer != null) {
writer.close();
writer = null;
}
}
}
}
项目启动的时候,我们先启动服务端,再启动客户端。如果先启动客户端,会报连接拒绝异常。因为服务端启动的时候,需要知道服务端的地址和端口号。然后在客户端输入 你好,嗨客网! 效果如下
客户端输出
服务端输出
这样,客户端和服务端就通信了,我们看到服务端和客户端一直没有退出,一直启动着。
伪异步IO
我们知道,伪异步 IO 是将 server 端的线程处理,维护到线程池当中,客户端不会改变,所以我们就改变一下 Server 端的代码即可,代码如下:
package net.haicoder.server;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BioThreadPoolServer{
public static void main(String[] args){
int port = 9999;
ServerSocket serverSocket = null;
ExecutorService service = Executors.newFixedThreadPool(50);
try {
serverSocket = new ServerSocket(port);
//一直监听,是否有客户端请求过来
while (true) {
Socket socket = serverSocket.accept();
//每次都会新建一个线程,来处理接收到到请求
service.execute(new SocketHandler(socket));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
serverSocket = null;
}
}
}
static class SocketHandler implements Runnable{
Socket socket = null;
public SocketHandler(Socket socket){
this.socket = socket;
}
@Override
public void run(){
BufferedReader reader = null;
PrintWriter writer = null;
try {
//读取数据,BIO 是面向流到,所以定义流 BufferedReader 来读取数据
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
//将收到的数据返回给客户端
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
String readMessage = null;
while (true) {
if ((readMessage = reader.readLine()) == null) {
break;
}
System.out.println("server reading ........" + readMessage);
//将数据返回给客户端
writer.println("server recive : " + readMessage);
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
reader = null;
}
if (writer != null) {
writer.close();
writer = null;
}
}
}
}
}
总结
本章节,我们将 socket 的 BIO 编程的客户端和服务端代码编写,将运行效果展示。客户端每输入一次请求,服务端都会处理,然后再回写给客户端。我们也将伪异步 IO 端代码列出。执行效果和 Server 端的代码一样的,有兴趣的同学可以按照上面的代码试一下,找一下感觉。