目录
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