Java IO编程

Java IO

      Java有非常丰富的IO接口,从流类型可划分为字节流和字符流,从流操作的位置可划分为网络IO和本地IO。

本地IO
  1. 本地IO时,普通IO和BIO最为常用,BIO较普通IO有更丰富的流操作组件,而NIO、AIO对于本地IO来说反而会在效率上大打折扣
  1. 那么buffer有什么作用呢?它会在内存中开辟一块缓冲区,用于优化对流的操作,读流时:不间断地从设备读到缓冲区中,同时对缓冲区进行间断(每次1024字节)地读取操作,速度加快。写流时:先将流间断(每次1024字节)地写入到缓冲区中,再统一对设备写入,速度加快,mark/reset就是通过这种方式实现的
网络IO
  • 网络IO时,普通IO效率最低,BIO效率较普通IO更高,但BIO每个连接都需要起一个监控线程,而NIO通过selector完美地解决了这个问题,AIO是并发量较大网络IO的解决方案,它的内部其实是通过线程池去并发处理各个请求。那么是不是意味着普通BIO和NIO中加入线程池就可以替代AIO呢?毋庸置疑不是的,BIO 和 NIO 的使用中加线程池不是什么好主意,因为有channel的限制,所以AIO这套框架并没有白做。
  1. IO:普通的IO,每次操作stream,都需要和磁盘交互,效率最低
  1. BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善
  1. NIO:一个请求一个线程,通过selector对多个channel监听,并统一响应和处理,selector轮询到连接有I/O请求时才启动一个线程进行处理
  1. AIO:一个请求一个线程,客户端的I/O请求都是由OS先完成(将IO直接读到缓冲区)了并返回结果给Channel,再通知服务器应用去启动线程进行处理,处理完成后再主动将结果推送给客户端,那么客户端需要设置接收信息的端口。没有selector的概念,并发情况下效率高于NIO
以下是几种IO的技术架构图
BIO
在这里插入图片描述
NIO
在这里插入图片描述
AIO

在这里插入图片描述

接下来上代码

  • 普通IO

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.PushbackInputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Date;
import java.util.zip.ZipInputStream;

/**
 * 字节IO
 */
 public class IOByteStream {
	public static void main(String[] args) {
		File fin = new File("C:\\Users\\Administrator\\Desktop\\test\\J0.java");
		File fout = new File("C:\\Users\\Administrator\\Desktop\\test\\J1.java");

		level1(fin, fout);
		level2(fin);
		level4Object(fout);
	}

	public static void level1(File fin, File fout) {
		try (
				FileInputStream fis = new FileInputStream(fin);
				BufferedInputStream bis = new BufferedInputStream(fis);
				FileOutputStream fos = new FileOutputStream(fout, false);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
				) {
			System.out.println("stream markSupported - " + fis.markSupported());
			System.out.println("buffer markSupported - " + bis.markSupported());
			//mark后读取超过readlimit字节数据,mark标记就会失效
			bis.mark(40960);
			byte[] b = new byte[1024];
            int i = 0;
            while ((i = bis.read(b)) > 0) {
            	bos.write(b, 0, i);
            }
            bos.flush();
            
            bis.reset();
			byte[] b2 = new byte[1024];
            int i2 = 0;
            while ((i2 = bis.read(b2)) > 0) {
            	bos.write(b2, 0, i2);
            }
            bos.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void level2(File f) {
		try (
				FileInputStream fis = new FileInputStream(f);
				BufferedInputStream bis = new BufferedInputStream(fis);
				PushbackInputStream pbis = new PushbackInputStream(bis);
				DataInputStream dis = new DataInputStream(pbis);
				) {
			int s1 = dis.read();System.out.println(s1);
			pbis.unread(s1);
			int s2 = dis.read();System.out.println(s2);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void level3Zip(File f) {
		try (
				FileInputStream fis = new FileInputStream(f);
				BufferedInputStream bis = new BufferedInputStream(fis);
				ZipInputStream zis = new ZipInputStream(bis);
				PushbackInputStream pbis = new PushbackInputStream(zis);
				DataInputStream dis = new DataInputStream(pbis);
				) {
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void level4Object(File f) {
		try (
				FileOutputStream fos = new FileOutputStream(f, true);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
				ObjectOutputStream oos = new ObjectOutputStream(bos);
				) {
			Object o = new Object();
			oos.writeObject(o);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

/**
 * 字符IO
 */
class IOCharStream {
	public static void main(String[] args) {
		File f = new File("C:\\Users\\Administrator\\Desktop\\test\\J0.java");
		File f2 = new File("C:\\Users\\Administrator\\Desktop\\test\\J1.java");
		level2(f2);
		level1(f);
	}
	
	public static void level1(File f) {
		try (
				FileInputStream fis = new FileInputStream(f);
				InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
				BufferedReader br = new BufferedReader(isr);
				) {
			br.lines().forEach(line -> System.out.println(line));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	
	public static void level2(File f) {
		try (
				FileOutputStream fos = new FileOutputStream(f, true);
				OutputStreamWriter osw = new OutputStreamWriter(fos);
				BufferedWriter bw = new BufferedWriter(osw);
				PrintWriter pw = new PrintWriter(osw, true);
				) {
			pw.print("abcs");
			pw.println("ab");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}

/**
 * NIO读写
 */
class NIOStream {
	public static void main(String[] args) {
		File fin = new File("C:\\Users\\Administrator\\Desktop\\test\\J0.java");
		File fout = new File("C:\\Users\\Administrator\\Desktop\\test\\J1.java");
		level1(fin, fout);
	}
	
	public static void level1(File fin, File fout) {
		try (
				RandomAccessFile fis = new RandomAccessFile(fin, "rw");
				FileChannel channelIn = fis.getChannel();
				RandomAccessFile fos = new RandomAccessFile(fout, "rw");
				FileChannel channelOut = fos.getChannel();
				) {
			Charset charset = Charset.forName("UTF-8");//Java.nio.charset.Charset处理了字符转换问题。它通过构造CharsetEncoder和CharsetDecoder将字符序列转换成字节和逆转换。  
	        CharsetDecoder decoder = charset.newDecoder();
			MappedByteBuffer byteBufferIn = channelIn.map(FileChannel.MapMode.READ_WRITE, 0, channelIn.size());
			channelOut.write(byteBufferIn);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
/**
 * 三种流之间的对比
 */
class CompareWithStream {
	public static void main(String[] args) {
		File fin = new File("C:\\Users\\Administrator\\Desktop\\test\\J0.java");
		File fout = new File("C:\\Users\\Administrator\\Desktop\\test\\J1.java");

		level1NoBuffer(fin, fout);	// 普通IO 读写5000次 耗时12毫秒
		level1buffer(fin, fout);	// 缓冲IO 读写5000次 耗时13毫秒
		level1NIO(fin, fout);		// NIO 读写5000次 耗时70毫秒
	}
	
	public static void level1NoBuffer(File fin, File fout) {
		try (
				FileInputStream fis = new FileInputStream(fin);
				FileOutputStream fos = new FileOutputStream(fout, false);
				) {
			byte[] b = new byte[1024];
			long time3 = new Date().getTime();
			for (int i = 0; i < 5000; i++) {
	            int c = 0;
	            while ((c = fis.read(b)) > 0) {
	            	fos.write(b, 0, c);
	            }
	            fos.flush();
			}
			long time4 = new Date().getTime();
			System.out.println("普通IO:" + (time4 - time3));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void level1buffer(File fin, File fout) {
		try (
				FileInputStream fis = new FileInputStream(fin);
				BufferedInputStream bis = new BufferedInputStream(fis);
				FileOutputStream fos = new FileOutputStream(fout, false);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
				) {
			byte[] b = new byte[1024];
			long time1 = new Date().getTime();
			for (int i = 0; i < 5000; i++) {
	            int c = 0;
	            while ((c = bis.read(b)) > 0) {
	            	bos.write(b, 0, c);
	            }
	            bos.flush();
			}
			long time2 = new Date().getTime();
			System.out.println("缓冲IO:" + (time2 - time1));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void level1NIO(File fin, File fout) {
		try (
				RandomAccessFile fis = new RandomAccessFile(fin, "rw");
				FileChannel channelIn = fis.getChannel();
				RandomAccessFile fos = new RandomAccessFile(fout, "rw");
				FileChannel channelOut = fos.getChannel();
				) {
			long time5 = new Date().getTime();
			for (int i = 0; i < 5000; i++) {
				MappedByteBuffer byteBufferIn = channelIn.map(FileChannel.MapMode.READ_WRITE, 0, channelIn.size());
				channelOut.write(byteBufferIn);
			}
			long time6 = new Date().getTime();
			System.out.println("磁盘映射IO:" + (time6 - time5));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

  • BIO

服务端



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 final class ServerNormal {
	//默认的端口号
	private static int DEFAULT_PORT = 12345;
	//单例的ServerSocket
	private static ServerSocket server;
	//根据传入参数设置监听端口,如果没有参数调用以下方法并使用默认值
	public static void start() throws IOException{
		//使用默认值
		start(DEFAULT_PORT);
	}
	//这个方法不会被大量并发访问,不太需要考虑效率,直接进行方法同步就行了
	public synchronized static void start(int port) throws IOException{
		if(server != null) return;
		try{
			//通过构造函数创建ServerSocket
			//如果端口合法且空闲,服务端就监听成功
			server = new ServerSocket(port);
			System.out.println("PID:"+ Thread.currentThread().getId()+ " 服务器已启动,端口号:" + port);
			//通过无限循环监听客户端连接
			//如果没有客户端接入,将阻塞在accept操作上。
			while(true){
				Socket socket = server.accept();
				//当有新的客户端接入时,会执行下面的代码
				//然后创建一个新的线程处理这条Socket链路
				new ServerHandler(socket).run();
			}
		}finally{
			//一些必要的清理工作
			if(server != null){
				System.out.println("服务器已关闭。");
				server.close();
				server = null;
			}
		}
	}
	public static void main(String[] args) {
		try {
			start();
			System.out.println("I have been out.");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class ServerHandler implements Runnable{
	private Socket socket;
	public ServerHandler(Socket socket) {
		this.socket = socket;
	}
	@Override
	public void run() {
		BufferedReader in = null;
		PrintWriter out = null;
		try{
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			out = new PrintWriter(socket.getOutputStream(),true);
			String expression;
			String result;
			while(true){
				//通过BufferedReader读取一行
				//如果已经读到输入流尾部,返回null,退出循环
				//如果得到非空值,就尝试计算结果并返回
				if((expression = in.readLine())==null) break;
				System.out.println("ID:" + Thread.currentThread().getId()+" 服务器收到消息:" + expression);
				try{
					result = expression+" got it!";
				}catch(Exception e){
					result = "计算错误:" + e.getMessage();
				}
				out.println(result);
			}
		}catch(Exception 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;
			}
		}
	}
}


客户端



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 客户端
 */
public class Client {
	//默认的端口号
	private static int DEFAULT_SERVER_PORT = 12345;
	private static String DEFAULT_SERVER_IP = "127.0.0.1";
	public static void send(String expression){
		send(DEFAULT_SERVER_PORT,expression);
	}
	public static void send(int port,String expression){
		System.out.println("算术表达式为:" + expression);
		Socket socket = null;
		BufferedReader in = null;
		PrintWriter out = null;
		try {
			socket = new Socket(DEFAULT_SERVER_IP,port);
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			out = new PrintWriter(socket.getOutputStream(),true);
			for (int i = 0; i < 10; i++) {
				Thread.sleep(1000);
				out.println(expression);
				System.out.println("___结果为:" + in.readLine());
				out.println(expression+" again");
				System.out.println("___结果为:" + in.readLine());
			}
		} catch (Exception e){
			e.printStackTrace();
		} finally{
			//长连接:关闭输出流代表传输完毕,保留输入流代表不关闭当前socket,与服务端保持连接
			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;
//			}
		}
	}
	public static void main(String[] args) {
		send("发送的值");
	}
}


  • NIO

    客户端


package com.td.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 客户端
 */
public class Client {
	private static String DEFAULT_HOST = "127.0.0.1";
	private static int DEFAULT_PORT = 12345;
	private static ClientHandle clientHandle;
	public static void startByPort(String ip,int port){
		start(DEFAULT_HOST,DEFAULT_PORT);
	}
	public static synchronized void start(String ip,int port){
		if(clientHandle!=null)
			clientHandle.stop();
		clientHandle = new ClientHandle(ip,port);
		new Thread(clientHandle,"Server").start();
	}
	//向服务器发送消息
	public static boolean sendMsg(String msg) throws Exception{
		if(msg.equals("q")) return false;
		clientHandle.sendMsg(msg);
		return true;
	}
}
class ClientHandle implements Runnable {
	private String host;
	private int port;
	private Selector selector;
	private SocketChannel socketChannel;
	private volatile boolean started;
 
	public ClientHandle(String ip,int port) {
		this.host = ip;
		this.port = port;
		try{
			//创建选择器
			selector = Selector.open();
			//打开监听通道
			socketChannel = SocketChannel.open();
			//如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
			socketChannel.configureBlocking(false);//开启非阻塞模式
			started = true;
		}catch(IOException e){
			e.printStackTrace();
			System.exit(1);
		}
	}
	public void stop(){
		started = false;
	}
	@Override
	public void run() {
		try{
			doConnect();
		}catch(IOException e){
			e.printStackTrace();
			System.exit(1);
		}
		//循环遍历selector
		while(started){
			try{
				//无论是否有读写事件发生,selector每隔1s被唤醒一次
				selector.select(1000);
				//阻塞,只有当至少一个注册的事件发生的时候才会继续.
//				selector.select();
				Set<SelectionKey> keys = selector.selectedKeys();
				Iterator<SelectionKey> it = keys.iterator();
				SelectionKey key = null;
				while(it.hasNext()){
					key = it.next();
					it.remove();
					try{
						handleInput(key);
					}catch(Exception e){
						if(key != null){
							key.cancel();
							if(key.channel() != null){
								key.channel().close();
							}
						}
					}
				}
			}catch(Exception e){
				e.printStackTrace();
				System.exit(1);
			}
		}
		//selector关闭后会自动释放里面管理的资源
		if(selector != null)
			try{
				selector.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
	}
	private void handleInput(SelectionKey key) throws IOException{
		if(key.isValid()){
			SocketChannel sc = (SocketChannel) key.channel();
			if(key.isConnectable()){
				if(sc.finishConnect());
				else System.exit(1);
			}
			//读消息
			if(key.isReadable()){
				//创建ByteBuffer,并开辟一个1M的缓冲区
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				//读取请求码流,返回读取到的字节数
				int readBytes = sc.read(buffer);
				//读取到字节,对字节进行编解码
				if(readBytes>0){
					//将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
					buffer.flip();
					//根据缓冲区可读字节数创建字节数组
					byte[] bytes = new byte[buffer.remaining()];
					//将缓冲区可读字节数组复制到新建的数组中
					buffer.get(bytes);
					String result = new String(bytes,"UTF-8");
					System.out.println("客户端收到消息:" + result);
				}
				//没有读取到字节 忽略
//				else if(readBytes==0);
				//链路已经关闭,释放资源
				else if(readBytes<0){
					key.cancel();
					sc.close();
				}
			}
		}
	}
	//异步发送消息
	private void doWrite(SocketChannel channel,String request) throws IOException{
		//将消息编码为字节数组
		byte[] bytes = request.getBytes();
		//根据数组容量创建ByteBuffer
		ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
		//将字节数组复制到缓冲区
		writeBuffer.put(bytes);
		//flip操作
		writeBuffer.flip();
		//发送缓冲区的字节数组
		channel.write(writeBuffer);
		//****此处不含处理“写半包”的代码
	}
	private void doConnect() throws IOException{
		if(socketChannel.connect(new InetSocketAddress(host,port)));
		else socketChannel.register(selector, SelectionKey.OP_CONNECT);
	}
	public void sendMsg(String msg) throws Exception{
		socketChannel.register(selector, SelectionKey.OP_READ);
		doWrite(socketChannel, msg);
	}

}

服务端


package com.td.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 服务端
 */
public class Server {
	private static int DEFAULT_PORT = 12345;
	private static ServerHandle serverHandle;
	public static void startByPort(int ... port){
		start(port);
	}
	public static synchronized void start(int ... port){
		if(serverHandle!=null)
			serverHandle.stop();
		serverHandle = new ServerHandle(port);
		new Thread(serverHandle,"Server").start();
	}
}

class ServerHandle implements Runnable{
	private Selector selector;
	private volatile boolean started;
	/**
	 * 构造方法
	 * @param port 指定要监听的端口号
	 */
	public ServerHandle(int ...port) {
		try{
			//创建选择器
			selector = Selector.open();
			for (int i = 0; i < port.length; i++) {
				
				ServerSocketChannel serverChannel = null;
				//打开监听通道
				serverChannel = ServerSocketChannel.open();
				//如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
				serverChannel.configureBlocking(false);//开启非阻塞模式
				//绑定端口 backlog设为1024
				serverChannel.socket().bind(new InetSocketAddress(port[i]),1024);
				//监听客户端连接请求
				serverChannel.register(selector, SelectionKey.OP_ACCEPT);
			}
			
			//标记服务器已开启
			started = true;
			System.out.println("服务器已启动,端口号:" + port);
		}catch(IOException e){
			e.printStackTrace();
			System.exit(1);
		}
	}
	public void stop(){
		started = false;
	}
	@Override
	public void run() {
		//循环遍历selector
		while(started){
			try{
				//无论是否有读写事件发生,selector每隔1s被唤醒一次
				selector.select(1000);
				//阻塞,只有当至少一个注册的事件发生的时候才会继续.
//				selector.select();
				Set<SelectionKey> keys = selector.selectedKeys();
				Iterator<SelectionKey> it = keys.iterator();
				SelectionKey key = null;
				while(it.hasNext()){
					key = it.next();
					it.remove();
					try{
						handleInput(key);
					}catch(Exception e){
						if(key != null){
							key.cancel();
							if(key.channel() != null){
								key.channel().close();
							}
						}
					}
				}
			}catch(Throwable t){
				t.printStackTrace();
			}
		}
		//selector关闭后会自动释放里面管理的资源
		if(selector != null)
			try{
				selector.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
	}
	private void handleInput(SelectionKey key) throws IOException{
		if(key.isValid()){
			//处理新接入的请求消息
			if(key.isAcceptable()){
				ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
				//通过ServerSocketChannel的accept创建SocketChannel实例
				//完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
				SocketChannel sc = ssc.accept();
				//设置为非阻塞的
				sc.configureBlocking(false);
				//注册为读
				sc.register(selector, SelectionKey.OP_READ);
			}
			//读消息
			if(key.isReadable()){
				SocketChannel sc = (SocketChannel) key.channel();
				//创建ByteBuffer,并开辟一个1M的缓冲区
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				//读取请求码流,返回读取到的字节数
				int readBytes = sc.read(buffer);
				//读取到字节,对字节进行编解码
				if(readBytes>0){
					//将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
					buffer.flip();
					//根据缓冲区可读字节数创建字节数组
					byte[] bytes = new byte[buffer.remaining()];
					//将缓冲区可读字节数组复制到新建的数组中
					buffer.get(bytes);
					String expression = new String(bytes,"UTF-8");
					System.out.println("服务器收到消息:" + expression);
					//处理数据
					String result = null;
					try{
						result = "result : "+ expression;
					}catch(Exception e){
						result = "计算错误:" + e.getMessage();
					}
					//发送应答消息
					doWrite(sc,result);
				}
				//没有读取到字节 忽略
//				else if(readBytes==0);
				//链路已经关闭,释放资源
				else if(readBytes<0){
					key.cancel();
					sc.close();
				}
			}
		}
	}
	//异步发送应答消息
	private void doWrite(SocketChannel channel,String response) throws IOException{
		//将消息编码为字节数组
		byte[] bytes = response.getBytes();
		//根据数组容量创建ByteBuffer
		ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
		//将字节数组复制到缓冲区
		writeBuffer.put(bytes);
		//flip操作
		writeBuffer.flip();
		//发送缓冲区的字节数组
		channel.write(writeBuffer);
		//****此处不含处理“写半包”的代码
	}
}


启动器


package com.td.io.nio;

public class Sender {
		public static void main(String[] args) throws Exception{
			//运行服务器
			Server.start(12345, 12346);
			//避免客户端先于服务器启动前执行代码
			Thread.sleep(100);
			//运行客户端 
			Client.start("127.0.0.1", 12345);
			Thread.sleep(100);
			Client.sendMsg("value");
			//运行客户端 
			Client.start("127.0.0.1", 12346);
			Thread.sleep(100);
			Client.sendMsg("value");
		}
}


  • AIO

客户端



import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;

public class Client {
	private static String DEFAULT_HOST = "127.0.0.1";
	private static int DEFAULT_PORT = 12345;
	private static AsyncClientHandler clientHandle;
	public static void start(){
		start(DEFAULT_HOST,DEFAULT_PORT);
	}
	public static synchronized void start(String ip,int port){
		if(clientHandle!=null)
			return;
		clientHandle = new AsyncClientHandler(ip,port);
		new Thread(clientHandle,"Client").start();
	}
	//向服务器发送消息
	public static boolean sendMsg(String msg) throws Exception{
		if(msg.equals("q")) return false;
		clientHandle.sendMsg(msg);
		return true;
	}
	@SuppressWarnings("resource")
	public static void main(String[] args) throws Exception{
		Client.start();
		System.out.println("请输入请求消息:");
		Scanner scanner = new Scanner(System.in);
		while(Client.sendMsg(scanner.nextLine()));
	}

}

class AsyncClientHandler implements CompletionHandler<Void, AsyncClientHandler>, Runnable {
	private AsynchronousSocketChannel clientChannel;
	private String host;
	private int port;
	private CountDownLatch latch;
	public AsyncClientHandler(String host, int port) {
		this.host = host;
		this.port = port;
		try {
			//创建异步的客户端通道
			clientChannel = AsynchronousSocketChannel.open();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		//创建CountDownLatch等待
		latch = new CountDownLatch(1);
		//发起异步连接操作,回调参数就是这个类本身,如果连接成功会回调completed方法
		clientChannel.connect(new InetSocketAddress(host, port), this, this);
		try {
			latch.await();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		try {
			clientChannel.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	//连接服务器成功
	//意味着TCP三次握手完成
	@Override
	public void completed(Void result, AsyncClientHandler attachment) {
		System.out.println("客户端成功连接到服务器...");
	}
	//连接服务器失败
	@Override
	public void failed(Throwable exc, AsyncClientHandler attachment) {
		System.err.println("连接服务器失败...");
		exc.printStackTrace();
		try {
			clientChannel.close();
			latch.countDown();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	//向服务器发送消息
	public void sendMsg(String msg){
		byte[] req = msg.getBytes();
		ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
		writeBuffer.put(req);
		writeBuffer.flip();
		//异步写
		clientChannel.write(writeBuffer, writeBuffer,new WriteHandler(clientChannel, latch));
	}
}

class WriteHandler implements CompletionHandler<Integer, ByteBuffer> {
	private AsynchronousSocketChannel clientChannel;
	private CountDownLatch latch;
	public WriteHandler(AsynchronousSocketChannel clientChannel,CountDownLatch latch) {
		this.clientChannel = clientChannel;
		this.latch = latch;
	}
	@Override
	public void completed(Integer result, ByteBuffer buffer) {
		//完成全部数据的写入
		if (buffer.hasRemaining()) {
			clientChannel.write(buffer, buffer, this);
		}
		else {
			//读取数据
			ByteBuffer readBuffer = ByteBuffer.allocate(1024);
			clientChannel.read(readBuffer,readBuffer,new ReadHandler(clientChannel, latch));
		}
	}
	@Override
	public void failed(Throwable exc, ByteBuffer attachment) {
		System.err.println("数据发送失败...");
		try {
			clientChannel.close();
			latch.countDown();
		} catch (IOException e) {
		}
	}
}

class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
	private AsynchronousSocketChannel clientChannel;
	private CountDownLatch latch;
	public ReadHandler(AsynchronousSocketChannel clientChannel,CountDownLatch latch) {
		this.clientChannel = clientChannel;
		this.latch = latch;
	}
	@Override
	public void completed(Integer result,ByteBuffer buffer) {
		buffer.flip();
		byte[] bytes = new byte[buffer.remaining()];
		buffer.get(bytes);
		String body;
		try {
			body = new String(bytes,"UTF-8");
			System.out.println("客户端收到结果:"+ body);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void failed(Throwable exc,ByteBuffer attachment) {
		System.err.println("数据读取失败...");
		try {
			clientChannel.close();
			latch.countDown();
		} catch (IOException e) {
		}
	}
}


服务端



import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;

public class Server {
	private static int DEFAULT_PORT = 12345;
	private static AsyncServerHandler serverHandle;
	public volatile static long clientCount = 0;
	public static void start() {
		start(DEFAULT_PORT);
	}
	public static synchronized void start(int port) {
		if (serverHandle != null)
			return;
		serverHandle = new AsyncServerHandler(port);
		new Thread(serverHandle, "Server").start();
	}
	public static void main(String[] args) {
		Server.start();
	}
}

class AsyncServerHandler implements Runnable {
	public CountDownLatch latch;
	public AsynchronousServerSocketChannel channel;
	public AsyncServerHandler(int port) {
		try {
			//创建服务端通道
			channel = AsynchronousServerSocketChannel.open();
			//绑定端口
			channel.bind(new InetSocketAddress(port));
			System.out.println("服务器已启动,端口号:" + port);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		//CountDownLatch初始化
		//它的作用:在完成一组正在执行的操作之前,允许当前的现场一直阻塞
		//此处,让现场在此阻塞,防止服务端执行完成后退出
		//也可以使用while(true)+sleep 
		//生成环境就不需要担心这个问题,以为服务端是不会退出的
		latch = new CountDownLatch(1);
		//用于接收客户端的连接
		channel.accept(this,new AcceptHandler());
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncServerHandler> {
	@Override
	public void completed(AsynchronousSocketChannel channel,AsyncServerHandler serverHandler) {
		//继续接受其他客户端的请求
		Server.clientCount++;
		System.out.println("连接的客户端数:" + Server.clientCount);
		serverHandler.channel.accept(serverHandler, this);
		//创建新的Buffer
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		//异步读  第三个参数为接收消息回调的业务Handler
		channel.read(buffer, buffer, new ReadHandler(channel));
	}
	@Override
	public void failed(Throwable exc, AsyncServerHandler serverHandler) {
		exc.printStackTrace();
		serverHandler.latch.countDown();
	}
}
class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
	//用于读取半包消息和发送应答
	private AsynchronousSocketChannel channel;
	public ReadHandler(AsynchronousSocketChannel channel) {
			this.channel = channel;
	}
	//读取到消息后的处理
	@Override
	public void completed(Integer result, ByteBuffer attachment) {
		//flip操作
		attachment.flip();
		//根据
		byte[] message = new byte[attachment.remaining()];
		attachment.get(message);
		try {
			String expression = new String(message, "UTF-8");
			System.out.println("服务器收到消息: " + expression);
			String calrResult = null;
			try{
				calrResult = "result" + expression;
			}catch(Exception e){
				calrResult = "计算错误:" + e.getMessage();
			}
			//向客户端发送消息
			doWrite(calrResult);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	//发送消息
	private void doWrite(String result) {
		byte[] bytes = result.getBytes();
		ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
		writeBuffer.put(bytes);
		writeBuffer.flip();
		//异步写数据 参数与前面的read一样
		channel.write(writeBuffer, writeBuffer,new CompletionHandler<Integer, ByteBuffer>() {
			@Override
			public void completed(Integer result, ByteBuffer buffer) {
				//如果没有发送完,就继续发送直到完成
				if (buffer.hasRemaining())
					channel.write(buffer, buffer, this);
				else{
					//创建新的Buffer
					ByteBuffer readBuffer = ByteBuffer.allocate(1024);
					//异步读  第三个参数为接收消息回调的业务Handler
					channel.read(readBuffer, readBuffer, new ReadHandler(channel));
				}
			}
			@Override
			public void failed(Throwable exc, ByteBuffer attachment) {
				try {
					channel.close();
				} catch (IOException e) {
				}
			}
		});
	}
	@Override
	public void failed(Throwable exc, ByteBuffer attachment) {
		try {
			this.channel.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


启动器



import java.util.Scanner;

import com.td.io.aio.client.Client;
import com.td.io.aio.server.Server;

public class Sender {
	@SuppressWarnings("resource")
	public static void main(String[] args) throws Exception{
		//运行服务器
		Server.start();
		//避免客户端先于服务器启动前执行代码
		Thread.sleep(100);
		//运行客户端 
		Client.start();
		System.out.println("请输入请求消息:");
		Scanner scanner = new Scanner(System.in);
		while(Client.sendMsg(scanner.nextLine()));
	}

}


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值