java共支持三种网络编程模式:BIO,NIO,AIO
三种IO模式适用场景
- BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发有局限性,JDK1.4以前是唯一的选择,好处是编码实现方式简单,且也容易理解。
- NIO方式适用于连接数目多且连接比较段的架构,比如聊天服务器,弹幕系统等,相比BIO编码较复杂,JDK1.4以后开始支持。
- AIO方式适用于连接数据多且连接较长的场景,比如相册服务器等,编程较复杂,JDK1.7才开始支持。目前好像并为得到广泛使用。
BIO(blocking I/O) 基本介绍
Java BIO是传统的Java io编程,相关的类和接口在java.io包中 Java BIO:同步阻塞,一个连接为一个线程,连接一个客户端就需要启动一个线程进行处理,如果连接未断开且未做任何事,会造会不必要的开销。可以通过线程池优化。 Java BIO:适用于连接数目较小且相对固定的架构,对服务器的要求比较高,对并发有局限性。JDK1.4以前唯一的选择,简单易理解。
BIO的原理示意图
流程
- 服务器启动ServerSoket。
- 客户端启动Socket与服务器通信,默认情况下服务器需要对每个客户端建立一个线程与之通信。
- 客户端发出请求与服务器通信。
- 如果请求成功,客户端会等待请求结束后继续执行。
Java BIO应用实例
实例要求:
使用NIO编写服务端,监听8888端口号,当有客户端连接时,启动一个线程与之通信。 使用线程池改进,可以连接多个客户端。
服务端
package com.crazy.io.bio;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BIOServer {
public static void main(String[] args) throws IOException {
/**
* 1.创建一个线程池
* 如果有客户端连接了,就创建一个线程与之通信。
*/
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
// 创建ServerSocket
ServerSocket serverSocket = new ServerSocket(6668);
System.out.println("服务器启动了");
while (true) {
// 监听,等待客户端连接
final Socket socket = serverSocket.accept();
System.out.println("连接到了一个客户端");
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
// 可以和客户端通讯
handler(socket);
}
});
}
}
// 编写一个handler方法,与客户端通讯
public static void handler(Socket socket) {
// 通过socket获取输入流
try {
System.out.println("线程信息 id=" + Thread.currentThread().getId() + "线程名字=" + Thread.currentThread().getName());
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
// 循环的读取客户端发送的数据
while (true) {
System.out.println("线程信息 id=" + Thread.currentThread().getId() + "线程名字=" + Thread.currentThread().getName());
int read = inputStream.read(bytes);
if (read != -1) {
// 输出客户端发送的数据
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭和客户端的连接
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
客户端
package com.crazy.io.zerocopy;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.Socket;
public class OldIOClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 6668);
String fileName = "1.txt";
InputStream inputStream = new FileInputStream(fileName);
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
byte[] buffer = new byte[4096];
long readCount;
long total = 0;
long startTime = System.currentTimeMillis();
while ((readCount = inputStream.read(buffer)) >= 0) {
total += readCount;
dataOutputStream.write(buffer);
}
System.out.println("发送总字节数: " + total + ", 耗时: " + (System.currentTimeMillis() - startTime));
dataOutputStream.close();
socket.close();
inputStream.close();
}
}
有小伙伴说,我就只想写服务器,不想写客户端,能不能测试。能,我都给你们准备好了。可以使用windows的命令行telnet命令来测试。
telnet 127.0.0.1 6668
连接成功后通过按 Ctrl+] 符号进入发送数据界面
send hello world
查看服务端收到的消息
完成了简单的以BIO实现的客户端与服务器之间的交互。