重温JAVA网络编程之BIO

BIO:即为同步阻塞式编程,最常用的就是Socket。

先从最简单的例子开始:

客户端与服务端进行通信

代码实现:

//服务端
public class Service {
	public static void main(String[] args) {
		try {
			ServerSocket serversocket = new ServerSocket(8888);
			Socket socket=serversocket.accept();
			InputStream in = socket.getInputStream();
			InputStreamReader read = new InputStreamReader(in);
			BufferedReader br = new BufferedReader(read);
			String info=null;
			while((info=br.readLine())!=null) {
				System.out.println(info);
			}
			socket.shutdownInput();
			OutputStream out = socket.getOutputStream();
			PrintWriter pw = new PrintWriter(out);
			pw.write("哈哈哈");
			pw.flush();
			socket.shutdownOutput();
			in.close();
			br.close();
			read.close();
			pw.close();
			out.close();
			socket.close();
			serversocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
//用户端
public class Client {
	public static void main(String[] args) {
		try {
			Socket socket = new Socket("localhost",8888);
			OutputStream out = socket.getOutputStream();
			PrintWriter pw =new PrintWriter(out);
			pw.write("嘿嘿嘿");
			pw.flush();
			socket.shutdownOutput();
			InputStream in = socket.getInputStream();
			InputStreamReader read = new InputStreamReader(in);
			BufferedReader bf = new BufferedReader(read);
			String info=null;
			while((info=bf.readLine())!=null) {
				System.out.println(info);
			}
			socket.shutdownInput();
			bf.close();
			read.close();
			in.close();
			pw.close();
			out.close();
			socket.close();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

说明:服务端监听一个地址,通accept()方法一直进行等待。直到有客户端进行连接。然后开始通过IO进行通信。这样一次通信就完成了。

缺点:如果我有多个用户请求连接服务端,并且IO操作时非常费时的,这样就会造成大量用户等待。

解决方法:通过采用多线程的方式去进行IO操作。

//服务端
public class Sercice {
public static void main(String[] args) throws IOException {
	Socket socket;
	ServerSocket service = new ServerSocket(8888);
	while(true) {
		socket=service.accept();
		new ThreadService(socket).start(); 
		}
	}
}
//服务端IO处理
public class ThreadService extends Thread {
	private Socket socket = null;

	public ThreadService(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		InputStream in = null;
		InputStreamReader ir = null;
		BufferedReader br = null;
		PrintWriter pw =null;
		OutputStream out =null;
		try {
			in = socket.getInputStream();
			ir = new InputStreamReader(in);
			br = new BufferedReader(ir);
			String str = null;
			while ((str = br.readLine()) != null) {
				System.out.println(str);
			}
			socket.shutdownInput();
			
			out=socket.getOutputStream();
			pw = new PrintWriter(out);
			pw.write("欢迎你");
			pw.flush();
			
			socket.shutdownOutput();

		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				br.close();
				ir.close();
				in.close();
				pw.close();
				out.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
//客户端
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		Socket socket = new Socket("localhost",8888);
		OutputStream out=socket.getOutputStream();
		PrintWriter pw = new PrintWriter(out);
		pw.write("嘿嘿");
		pw.flush();
		socket.shutdownOutput();
		
		InputStream in = socket.getInputStream();
		InputStreamReader ir = new InputStreamReader(in);
		BufferedReader bf = new BufferedReader(ir);
		String str=null;
		while((str=bf.readLine())!=null) {
			System.out.println(str);
		}
		socket.shutdownInput();
		pw.close();
		out.close();
		bf.close();
		ir.close();
		in.close();
	}
}

说明:通过把每次IO操作都采用开启新的线程去执行来减少一次通信的时间。

缺点:当我连接数很多时,这样每一个连接数都对应一个线程,window的最大线程1000左右,linux加倍,那么这样的消耗也是消耗不起的,并且线程的建立与销毁都是非常消耗性能的。

解决办法:采用连接池的当时,指定连接数,保持线程长期存活。

//客户端
public class Sercice {
	public static void main(String[] args) throws IOException {
		Socket socket;
		ServerSocket service = new ServerSocket(8888);
		ExecutorService pool = new ThreadPoolExecutor(50, 1000, 120L, TimeUnit.SECONDS,
				new ArrayBlockingQueue<Runnable>(1000));
		while (true) {
			socket = service.accept();
			pool.execute(new ThreadService(socket)); 
		}
	}
}
//IO操作执行类
public class ThreadService implements Runnable{
	private Socket socket = null;

	public ThreadService(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		InputStream in = null;
		InputStreamReader ir = null;
		BufferedReader br = null;
		PrintWriter pw =null;
		OutputStream out =null;
		try {
			in = socket.getInputStream();
			ir = new InputStreamReader(in);
			br = new BufferedReader(ir);
			String str = null;
			while ((str = br.readLine()) != null) {
				System.out.println(str);
			}
			socket.shutdownInput();
			
			out=socket.getOutputStream();
			pw = new PrintWriter(out);
			pw.write("欢迎你");
			pw.flush();
			
			socket.shutdownOutput();

		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				br.close();
				ir.close();
				in.close();
				pw.close();
				out.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
//客户端
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		Socket socket = new Socket("localhost",8888);
		OutputStream out=socket.getOutputStream();
		PrintWriter pw = new PrintWriter(out);
		pw.write("嘿嘿");
		pw.flush();
		socket.shutdownOutput();
		
		InputStream in = socket.getInputStream();
		InputStreamReader ir = new InputStreamReader(in);
		BufferedReader bf = new BufferedReader(ir);
		String str=null;
		while((str=bf.readLine())!=null) {
			System.out.println(str);
		}
		socket.shutdownInput();
		pw.close();
		out.close();
		bf.close();
		ir.close();
		in.close();
	}
}

说明:我们通过线程池的方式达到了伪异步的方式,但这并不是真正的异步。没有从根本上解决阻塞的问题。

那么阻塞到底阻塞到哪了?

阻塞在了IO的read()以及write()方法上了。

read方法除了下面三个情况会一直阻塞着:

1.有数据可读

2.可用数据读取完毕

3.发生空指针IO异常

 

比如我要向服务端发消息(10字节),那么当网络比较慢的时候,开始只发过来8个字节,那么服务器会一直等待我,直到我所有的消息发送完,才会进行下一步处理。

那么我们该怎么处理这个问题呢?请看一下篇。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值