BIO、NIO、AIO的区别:
BIO就是基于Threadper Request的传统server/client实现模式,
NIO通常采用Reactor模式,
AIO通常采用Proactor模式,
AIO简化了程序的编写,stream的读取和写入都有OS来完成,不需要像NIO那样子遍历Selector。Windows基于IOCP实现AIO,Linux只有eppoll模拟实现了AIO。
Java7之前的JDK只支持NIO和BIO,从7开始支持AIO。
4种通信方式:TCP/IP+BIO, TCP/IP+NIO, UDP/IP+BIO, UDP/IP+NIO。
一.TCP/IP+BIO
Socket和ServerSocket实现,ServerSocket实现Server端端口监听,Socket用于建立网络IO连接。
服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
- package test;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.net.ServerSocket;
- import java.net.Socket;
- public class Server {
- private Socket socket;
- private ServerSocket ss;
- public Server() throws IOException {
- ss = new ServerSocket(7777);
- while (true) {
- socket = ss.accept();
- BufferedReader br = new BufferedReader(new InputStreamReader(socket
- .getInputStream()));
- System.out.println("you input is : " + br.readLine());
- }
- }
- public static void main(String[] args) {
- try {
- new Server();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。
Java代码
1. package test;
2.
3. import java.io.BufferedReader;
4. import java.io.IOException;
5. import java.io.InputStreamReader;
6. import java.io.PrintWriter;
7. import java.net.Socket;
8. import java.net.UnknownHostException;
9.
10.public class Client {
11. Socket client;
12. PrintWriter pw;
13. public Client() throws UnknownHostException, IOException {
14. client=new Socket("Socket服务器IP",7777);
15. pw=new PrintWriter(client.getOutputStream());
16. BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
17. pw.write(br.readLine());
18. pw.close();
19. br.close();
20. }
21. public static void main(String[] args) {
22. try {
23. new Client();
24. } catch (UnknownHostException e) {
25. e.printStackTrace();
26. } catch (IOException e) {
27. e.printStackTrace();
28. }
29. }
30.}
不适用于处理多个请求 1.生成Socket会消耗过多的本地资源。2.Socket连接的建立一般比较慢。
BIO情况下,能支持的连接数有限,一般都采取accept获取Socket以后采用一个thread来处理,oneconnection one thread。无论连接是否有真正数据请求,都需要独占一个thread。
可以通过设立Socket池来一定程度上解决问题,
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
importjava.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BIOPoolServer {
ExecutorService pool = null;
public BIOPoolServer(){
try {
ServerSocket server = newServerSocket(29001);
pool = Executors.newFixedThreadPool(1);
while(true){
pool.execute(new Handler(server.accept()));
}
} catch (IOException e) {
e.printStackTrace();
}finally{
pool.shutdown();
}
}
class Handlerimplements Runnable{
Socketsocket;
publicHandler(Socket socket){
this.socket =socket;
}
public void run() {
try {
BufferedReader in = new BufferedReader(newInputStreamReader(socket.getInputStream()));
PrintWriter out = newPrintWriter(socket.getOutputStream(),true);
String msg= in.readLine();
System.out.println("The client send the msg : "+msg);
out.println("Theserver has received!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public staticvoid main(String[] args) {
new BIOPoolServer();
}
}
但是使用池需要注意的问题是:1. 竞争等待比较多。 2. 需要控制好超时时间。
二.TCP/IP+NIO
使用Channel(SocketChannel和ServerSocketChannel)和Selector。
Server端通常由一个thread来监听connect事件,另外多个thread来监听读写事件。这样做的好处是这些连接只有在真是请求的时候才会创建thread来处理,one request one thread。这种方式在server端需要支持大量连接但这些连接同时发送请求的峰值不会很多的时候十分有效。
server端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
importjava.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
importjava.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
ServerSocketChannel channel = null;
publicNIOServer(){
try {
openChannel();
waitForConnection();
} catch (IOException e) {
e.printStackTrace();
}
}
private voidopenChannel() throws IOException{
channel = ServerSocketChannel.open();
//绑定监听端口
channel.socket().bind(newInetSocketAddress(29000));
//设置为非阻塞形式
channel.configureBlocking(false);
}
private voidwaitForConnection() throws IOException{
Selector acceptSelector =SelectorProvider.provider().openSelector();
channel.register(acceptSelector,SelectionKey.OP_ACCEPT);
int keyAdded = 0;
while((keyAdded=acceptSelector.select())>0){
// 某客户已经准备好可以进行I/O操作了,获取其ready键集合
Set readKeys =acceptSelector.selectedKeys();
Iterator iter =readKeys.iterator();
while(iter.hasNext()){
SelectionKey sk =(SelectionKey)iter.next();
iter.remove();
if(sk.isAcceptable()){
ServerSocketChannel server =(ServerSocketChannel) sk.channel();
SocketChannel socket =server.accept();
ByteBuffer _buffer =ByteBuffer.allocate(8);
IntBuffer _intBuffer =_buffer.asIntBuffer();
_buffer.clear();
socket.read(_buffer);
int result = _intBuffer.get(0)+ _intBuffer.get(1);
_buffer.flip();
_buffer.clear();
_intBuffer.put(0, result);
socket.write(_buffer);
}
}
}
}
public staticvoid main(String[] args) {
new NIOServer();
}
}
client端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.SocketChannel;
public class NIOClient {
public void start(int first, intsecond){
SocketChannel channel = null;
try {
InetSocketAddress socketAddress = newInetSocketAddress("localhost",29000);
channel =SocketChannel.open(socketAddress);
channel.configureBlocking(false);
ByteBuffer _buffer =ByteBuffer.allocate(8);
IntBuffer _intBuffer =_buffer.asIntBuffer();
_buffer.clear();
_intBuffer.put(0, first);
_intBuffer.put(1, second);
channel.write(_buffer);
System.out.println("发送加法请求" + first + "+" + second);
_buffer.clear();
channel.read(_buffer);
int result =_intBuffer.get(0);
System.out.println("运算结果:"+result);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
}
}
}
}
public static void main(String[]args) {
new NIOClient().start(3, 23);
}
}
三.UDP/IP+BIO
DatagramSocket和DatagramPacket。DatagramSocket负责监听端口以及读写数据,DatagramPacket作为数据流对象进行传输。
UDP/IP是无连接的,无法进行双向通信,除非双方都成为UDP Server。
四.UDP/IP+NIO
通过DatagramChannel和ByteBuffer实现。DatagramChannel负责端口监听及读写。ByteBuffer负责数据流传输。
如果要将消息发送到多台机器,如果为每个目标机器都建立一个连接的话,会有很大的网络流量压力。这时候可以使用基于UDP/IP的Multicast协议传输,Java中可以通过MulticastSocket和DatagramPacket来实现。
Multicast一般多用于多台机器的状态同步,比如JGroups。SRM, URGCP都是Multicast的实现方式。eBay就采用SRM来实现将数据从主数据库同步到各个搜索节点机器。