java Socket网络通信 BIO NIO AIO

目录

1、BIO

2、NIO

3、AIO


 

1、BIO

同步阻塞IO,一个线程处理一个连接,发起和处理IO请求都是同步的

package com.busy.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class BioServer {

    public static void main(String[] args) throws IOException {  
        ServerSocket serverSocket = new ServerSocket(10101);  
        System.out.println("服务器启动!");  
        while (true){  
            Socket socket = serverSocket.accept();  
            System.out.println("来了一个新客户端!");  
            //业务处理  
            handler(socket); // 若客户端不主动断开socket,就会阻塞在handler->while
        }
    }  
  
    /** 
     * 读取数据 
     * @param socket 
     */  
    private static void handler(Socket socket){  
    	String clientCode = null;
        try{  
            byte[] bytes = new byte[1]; 
            byte[] headByte = new byte[3];
            InputStream inputStream = socket.getInputStream();
            int infoHead = inputStream.read(headByte);
            clientCode = new String(headByte,0,infoHead);
            while (true) {
                //读取数据 阻塞  
                int read = 0;
                try {
                	read = inputStream.read(bytes);                  	
                } catch (IOException e) {
                	inputStream.close();
                	inputStream = null;
                	System.out.println("客户端主"+clientCode+"动断开socket");
                	break;
                }
                if(read != -1 && read != 0){  
                    System.out.println("客户端"+clientCode+"发来消息"+new String(bytes,0,read));
                    Thread.sleep(2000);
                }else{  
                    break;  
                }  
            }  
        }catch (Exception ex){  
        	
        }finally {  
            try{  
                System.out.println("客户端"+clientCode+"socket关闭");  
                socket.close();
            }catch (Exception ex){  
                ex.printStackTrace();  
            }  
        }  
    }  
}
package com.busy.server;

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

public class BioClientA {
	public static void main(String[] args) {
		send("[A]12345678");
	}
	
    //默认的端口号  
    private static int DEFAULT_SERVER_PORT = 10101;  
    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){   
        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);  
            out.println(expression);
        }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;  
            }  
        }  
    }
}

服务端异步接收socket请求(改main为下)【相当于同步非阻塞,同NIO对比,只是每次要开线程,CPU开销大】

    public static void main(String[] args) throws IOException { 
    	ExecutorService newCashedThreadPool = Executors.newCachedThreadPool();
        ServerSocket serverSocket = new ServerSocket(10101);  
        System.out.println("服务器启动!");  
        while (true){  
            final Socket socket = serverSocket.accept();  
            System.out.println("来了一个新客户端!");  
            //业务处理  
            //handler(socket); // 若客户端不主动断开socket,就会阻塞在handler->while
            newCashedThreadPool.execute(new Runnable() {  
                @Override  
                public void run() {  
                    //业务处理  
                    handler(socket);  
                }  
            });
        }
    }

2、NIO

同步非阻塞IO,一个线程处理多个链接,发起IO请求是非阻塞的,处理IO请求是同步的(轮询)

package com.busy.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
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 NIOServer {
	private Selector selector; // 创建一个选择器
	private final static int port = 8686;
	private final static int BUF_SIZE = 1;

	private void initServer() throws IOException {
		// 创建通道管理器对象selector
		this.selector = Selector.open();
		// 创建一个通道对象channel
		ServerSocketChannel channel = ServerSocketChannel.open();
		channel.configureBlocking(false); // 将通道设置为非阻塞
		// channel.configureBlocking(true); // 将通道设置为阻塞
		channel.socket().bind(new InetSocketAddress(port)); // 将通道绑定在8686端口
		// 将上述的通道管理器和通道绑定,并为该通道注册OP_ACCEPT事件
		// 注册事件后,当该事件到达时,selector.select()会返回(一个key),如果该事件没到达selector.select()会一直阻塞
		SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_ACCEPT); // 阻塞通道不能注册

		while (true) { // 轮询
			//handler(channel.socket().accept()); // 阻塞通道接收
			selector.select(); // 这是一个阻塞方法,一直等待直到有数据可读,返回值是key的数量(可以有多个)
			Set keys = selector.selectedKeys(); // 如果channel有数据了,将生成的key访入keys集合中
			Iterator iterator = keys.iterator(); // 得到这个keys集合的迭代器
			while (iterator.hasNext()) { // 使用迭代器遍历集合
				SelectionKey key = (SelectionKey) iterator.next(); // 得到集合中的一个key实例
				iterator.remove(); // 拿到当前key实例之后记得在迭代器中将这个元素删除,非常重要,否则会出错
				if (key.isAcceptable()) { // 判断当前key所代表的channel是否在Acceptable状态,如果是就进行接收
					doAccept(key);
				} else if (key.isReadable()) {
					doRead(key);
				} else if (key.isWritable() && key.isValid()) {
					doWrite(key);
				} else if (key.isConnectable()) {
					System.out.println("连接成功!");
				}
			}
		}
	}

	public void doAccept(SelectionKey key) throws IOException {
		ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
		System.out.println("ServerSocketChannel正在循环监听");
		SocketChannel clientChannel = serverChannel.accept();
		clientChannel.configureBlocking(false);
		clientChannel.register(key.selector(), SelectionKey.OP_READ);
	}

	public void doRead(SelectionKey key) throws IOException {
		try {
			SocketChannel clientChannel = (SocketChannel) key.channel();
			ByteBuffer byteBuffer = ByteBuffer.allocate(BUF_SIZE);
			long bytesRead = clientChannel.read(byteBuffer);
			while (bytesRead > 0) {
				byteBuffer.flip();
				byte[] data = byteBuffer.array();
				String info = new String(data).trim();
				System.out.println("从客户端发送过来的消息是:" + info);
				Thread.sleep(2000);
				byteBuffer.clear();
				bytesRead = clientChannel.read(byteBuffer);
			}
			if (bytesRead == -1) {
				clientChannel.close();
			}
		} catch (Exception ex) {

		}
	}

	public void doWrite(SelectionKey key) throws IOException {
		ByteBuffer byteBuffer = ByteBuffer.allocate(BUF_SIZE);
		byteBuffer.flip();
		SocketChannel clientChannel = (SocketChannel) key.channel();
		while (byteBuffer.hasRemaining()) {
			clientChannel.write(byteBuffer);
		}
		byteBuffer.compact();
	}

	/**
	 * 读取数据
	 * 
	 * @param socket
	 */
	private static void handler(Socket socket) {
		String clientCode = null;
		try {
			byte[] bytes = new byte[1];
			InputStream inputStream = socket.getInputStream();
			while (true) {
				// 读取数据 阻塞
				int read = 0;
				try {
					read = inputStream.read(bytes);
				} catch (IOException e) {
					inputStream.close();
					inputStream = null;
					System.out.println("客户端主" + clientCode + "动断开socket");
					break;
				}
				if (read != -1 && read != 0) {
					System.out.println("客户端" + clientCode + "发来消息" + new String(bytes, 0, read));
					Thread.sleep(2000);
				} else {
					break;
				}
			}
		} catch (Exception ex) {

		} finally {
			try {
				System.out.println("客户端" + clientCode + "socket关闭");
				socket.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		NIOServer myNioServer = new NIOServer();
		myNioServer.initServer();
	}
}
package com.busy.server;

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;

public class NIOClientA {
    private Selector selector;          //创建一个选择器
    private final static int port = 8686;
    private final static int BUF_SIZE = 10240;
    private static ByteBuffer byteBuffer = ByteBuffer.allocate(BUF_SIZE);

    private void  initClient() throws IOException {
        this.selector = Selector.open();
        SocketChannel clientChannel = SocketChannel.open();
        clientChannel.configureBlocking(false);
        clientChannel.connect(new InetSocketAddress(port));
        clientChannel.register(selector, SelectionKey.OP_CONNECT);
        while (true){
            selector.select();
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isConnectable()){
                    doConnect(key);
                }else if (key.isReadable()){
                    doRead(key);
                }
            }
        }
    }

    public void doConnect(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        if (clientChannel.isConnectionPending()){
            clientChannel.finishConnect();
        }
        clientChannel.configureBlocking(false);
        String info = "12345678";
        byteBuffer.clear();
        byteBuffer.put(info.getBytes("UTF-8"));
        byteBuffer.flip();
        clientChannel.write(byteBuffer);
        //clientChannel.register(key.selector(),SelectionKey.OP_READ);
        clientChannel.close();
    }

    public void doRead(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        clientChannel.read(byteBuffer);
        byte[] data = byteBuffer.array();
        String msg = new String(data).trim();
        System.out.println("服务端发送消息:"+msg);
        clientChannel.close();
        key.selector().close();
    }

    public static void main(String[] args) throws IOException {
        NIOClientA myNioClient = new NIOClientA();
        myNioClient.initClient();
    }
}

在单线程BIO中,如果前一个请求正在接收执行中,第二个请求将不会接收到;

在单线程NIO中,如果前一个请求正在接收执行中,之后的请求将会按顺序接收;

channel.configureBlocking(false); 如果设置为阻塞,则channel不能被注册,同样可以跟BIO一样来处理请求

用:handler(channel.socket().accept()); // 阻塞通道接收

3、AIO

异步非阻塞IO,一个有效请求一个线程,发起和处理IO请求都是异步的。

package com.busy.server;

import java.io.IOException;
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.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AIOServer {
	public final static int PORT = 8001;
	public final static String IP = "127.0.0.1";
	private int countMsg = 0;

	private AsynchronousServerSocketChannel server = null;

	public AIOServer() {
		try {
			// 同样是利用工厂方法产生一个通道,异步通道 AsynchronousServerSocketChannel
			server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(IP, PORT));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 使用这个通道(server)来进行客户端的接收和处理
	public void start() {
		System.out.println("Server listen on " + PORT);
		// 注册事件和事件完成后的处理器,这个CompletionHandler就是事件完成后的处理器
		server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
			
			@Override
			public void completed(AsynchronousSocketChannel result, Object attachment) {
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				//System.out.println(Thread.currentThread().getName());
				Future<Integer> writeResult = null;
				try {
					buffer.clear();
					result.read(buffer).get(10000, TimeUnit.SECONDS);
					countMsg ++;
					System.out.println("msg:"+countMsg +", In server: " +  new String(buffer.array()) );
					Thread.sleep(100);
					// 将数据写回客户端
					buffer.flip();
					writeResult = result.write(buffer);
				} catch (InterruptedException | ExecutionException | TimeoutException e) {
					e.printStackTrace();
				} finally {
					server.accept(null, this);
					try {
						writeResult.get();
						result.close();
					} catch (IOException e) {
						e.printStackTrace();
					} catch (InterruptedException | ExecutionException e) {
						e.printStackTrace();
					}
				}
			}

			@Override
			public void failed(Throwable exc, Object attachment) {
				System.out.println("failed:" + exc);
			}
		});
	}

	public static void main(String[] args) {
		new AIOServer().start();
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
package com.busy.server;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class AIOSimpleServer {
	static final int PORT = 8001;

	public static void main(String[] args) throws Exception {

		// ①创建AsynchronousServerSocketChannel对象。
		AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();

		// ②指定在指定地址、端口监听。
		serverChannel.bind(new InetSocketAddress(PORT));
		int countMsg = 0;
		while (true) {
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			buffer.clear();
			// ③采用循环接受来自客户端的连接
			Future<AsynchronousSocketChannel> future = serverChannel.accept();
			// 获取连接完成后返回的AsynchronousSocketChannel
			AsynchronousSocketChannel socketChannel = future.get();
			socketChannel.read(buffer).get(100, TimeUnit.SECONDS);
			countMsg++;
			System.out.println("msg:" + countMsg + ", In server: " + new String(buffer.array()));
			Thread.sleep(200);
			// 执行输出。
			socketChannel.write(ByteBuffer.wrap("欢迎你来自AIO的世界!".getBytes("UTF-8"))).get();
		}
	}
}
package com.busy.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AIOClientA {
	
	static private int count = 0;  
	private InetSocketAddress serverAddress = null;
	private AsynchronousSocketChannel client = null;
	
	public AIOClientA() {
		try {
			serverAddress = new InetSocketAddress("127.0.0.1", 8001);
			client = AsynchronousSocketChannel.open();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void sendMsg() {
		count ++;
		client.write(ByteBuffer.wrap(("100【"+count).getBytes()));
	}
	
	public void connServer() {
		CompletionHandler<Void, ? super Object> handler = new CompletionHandler<Void, Object>() {

			@Override
			public void completed(Void result, Object attachment) {
				sendMsg();
			}

			@Override
			public void failed(Throwable exc, Object attachment) {
			}
			
		};
		client.connect(serverAddress, null, handler);
	}

	public static void main(String[] args) throws IOException {
		
		int i = 100;
		while (i -- > 0) {
			System.out.println(i);
			new AIOClientA().connServer();
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		while (true) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

对与服务端延迟接收,会出现丢包,客户端已经全部发送了????why

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值