@ruoli
2016-09-09T17:01:06.000000Z
字数 3036
阅读 1947
Java基础
传统经典的基于Socket网络编程,是采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端的连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完之后再通过输出流返回应答给客户端,线程销毁,这就是典型的一次请求一次应答的通信模型。
此种模式最简单最方便也最原始,在并发量很小的时候可以使用此种方案,但是并发量一旦增加,在短时间内创建大量线程,系统的性能就会急剧下降,系统发生线程堆栈溢出,创建新线程失败,最终导致系统宕机,不能对外提供服务。
服务端代码示例,TimeServer.java
package test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TimeServer {
public static void main(String[] args) throws IOException {
int port = 8080;
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("服务端已启动,端口为 : " + port);
Socket socket = null;
while (true) {
socket = server.accept();
new Thread(new TimeServerHandler(socket)).start();
}
}
finally {
if (server != null) {
System.out.println("服务端已关闭");
server.close();
server = null;
}
}
}
}
class TimeServerHandler implements Runnable {
private Socket socket;
public TimeServerHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket
.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String currentTime = null;
String body = null;
while (true) {
body = in.readLine();
if (body == null)break;
System.out.println("接收到客户请求 : " + body);
currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(
System.currentTimeMillis()).toString()
: "BAD ORDER";
out.println(currentTime);
}
} catch (Exception e) {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (out != null) {
out.close();
out = null;
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
客户端代码示例,TimeClient.java
package test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TimeClient {
public static void main(String[] args) {
int port = 8080;
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", port);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
String resp = in.readLine();
System.out.println("Now is : " + resp);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
out = null;
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
in = null;
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
}
}
}
为了解决上述第一种方式带来的资源耗尽问题,后来有人对它的线路模型进行了优化,后端通过一个线程池来处理多个客户端的请求结束,形成一个比例关系
线程池中的最大线程数 N:客户端连接数 M
其中M可远远大于N,通过线程池可以灵活的调配线程资源,设置线程的最大数量,防止并发过高导致的服务器宕机,此种模式的优点是无论客户端多少个并发访问,都不会导致服务器宕机。
此种模式依然存在问题,此问题是Java IO 本身机制的问题,当客户端并发大量请求的时候,由于部分客户端发送请求比较缓慢,读取输入流一方的通行将长时间阻塞,如果对方需要60秒才能够将数据发送完成,读取一方的IO线程也将会被同步阻塞60秒,在此期间 ,其他接入请求只能在消息队列中排队,最终导致所有大量线程被阻塞,接入请求处理缓慢。
此种模式 仅为 过渡模式,不做代码分析。
为了解决上述第二种问题,Sun公司在JKD1.4中引入了NIO 1.0,弥补原来同步阻塞IO的不足,后来JDK1.7 推出了NIO 2.0。
NIO被称为 new IO,或者非阻塞式IO(Non-block IO)。
NIO从根本上解决了IO阻塞的问题。