传统BIO编程
传统的BIO编程中,服务端提供IP和端口,客户端通过连接操作向服务端发起连接请求,通过三次握手连接,如果连接成功,双方可以通过套接字进行通信。在BIO编程中,通常由一个独立的Acceptor线程负责监听客户端的连接,它收到客户端的连接请求后为每个客户端创建一个新的线程进行请求处理,通过输出流返回处理结果给客户端,处理完成后,线程销毁。
BIO最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加,服务端的线程数也随之1:1的增加。当客户端访问数量较大时,由于线程在java中是开销比较大的资源,系统的性能将急剧下降。
以下是验证的demo代码。
Server类代码:
packagecom.coder.netty.bio;
importjava.io.IOException;
importjava.net.ServerSocket;
importjava.net.Socket;
public classServer {
private static intPORT=6666;
private staticServerSocketserverSocket;
public synchronized static voidstart()throwsIOException {
try{
serverSocket=newServerSocket(PORT);
System.out.println("服务端启动,端口是:"+PORT+"时间是:"+ System.currentTimeMillis());
while(true) {
Socket socket =serverSocket.accept();
newThread(newServerHandler(socket)).start();
}
}finally{
if(serverSocket!=null) {
serverSocket.close();
serverSocket=null;
System.out.println("服务端终止");
}
}
}
public static voidmain(String[]args) {
try{
Server.start();
}catch(IOException e) {
e.printStackTrace();
}
}
}
ServerHandler代码:
packagecom.coder.netty.bio;
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.PrintWriter;
importjava.net.Socket;
public classServerHandlerimplementsRunnable{
privateSocketsocket;
publicServerHandler(Socket socket) {
this.socket= socket;
}
public voidrun() {
BufferedReader in =null;
PrintWriter out =null;
try{
in =newBufferedReader(newInputStreamReader(socket.getInputStream()));
out =newPrintWriter(socket.getOutputStream(),true);
String clientMsg;
String reponseMsg;
while(true) {
if((clientMsg = in.readLine()) ==null) {
break;
}
System.out.println("服务端接受到客户端的消息是:"
+ clientMsg + " 时间是:" + System.currentTimeMillis());
reponseMsg ="-------"+clientMsg;
out.println(reponseMsg);
System.out.println("服务端发送成功"+"时间是:"
+ System.currentTimeMillis());
}
}catch(IOException e) {
e.printStackTrace();
}finally{
if(in !=null) {
try{
in.close();
}catch(IOException e) {
e.printStackTrace();
}
in =null;
}
if(out !=null) {
out.close();
out =null;
}
if(socket!=null) {
try{
socket.close();
}catch(IOException e) {
e.printStackTrace();
}
socket=null;
}
}
}
}
Client端代码:
packagecom.coder.netty.bio;
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.PrintWriter;
importjava.net.Socket;
importjava.net.UnknownHostException;
importjava.util.Scanner;
public classClient {
private static intPORT=6666;
private staticStringIP="127.0.0.1";
public static voidsend() {
System.out.println("客户端输入:");
Scanner scanner =newScanner(System.in);
String message = scanner.nextLine();
System.out.println("客户端发送的消息是:"+ message +"时间是:"
+ System.currentTimeMillis());
Socket socket =null;
BufferedReader in =null;
PrintWriter out =null;
try{
socket =newSocket(IP,PORT);
in =newBufferedReader(newInputStreamReader(
socket.getInputStream()));
out =newPrintWriter(socket.getOutputStream(),true);
out.println(message);
System.out.println("客户端接受到的是:"+ in.readLine() +
"时间是:"+ System.currentTimeMillis());
}catch(UnknownHostException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}finally{
if(in !=null) {
try{
in.close();
}catch(IOException e) {
e.printStackTrace();
}
}
if(out !=null) {
out.close();
}
if(socket !=null) {
try{
socket.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
}
public static voidmain(String[] args)throwsInterruptedException {
Client.send();
while(true) {
Thread.sleep(1000);
}
}
}
伪异步编程
为了解决传统BIO编程1:1创建线程的弊端,一种简单的优化思路是通过线程池来管理线程,这也就是通常所说的“伪异步编程”。实现起来也很简单,只需要将新建线程的地方改为线程池实现。
但是,由于限制了线程的数量,如果发生大量的请求,超过最大线程数限制部分的请求就只能等待,知道线程池中有空闲的线程可以被使用,此时对于客户端来说,是处于阻塞状态的。这也是为什么称这种方式的IO编程称为“伪”异步编程。
PS. 哪位仁兄知道公众号里代码的格式怎么调,调代码格式太痛苦了。
领取专属 10元无门槛券
私享最新 技术干货