前言
最近在学习java网络编程。用原生jdk接口做套接字编程开发,感觉劳心且费神。java项目套接字编程,首选还是使用netty。jdk的接口仅做入门和掌握netty架构设计之用。
基本概念
首先说几个基本概念。与去饭店吃饭做类比。简单说下java bio,nio和aio三种io模式的区别。
1.bio,nio ,aio
bio
等待数据就序,再对数据进行操作 ->去饭堂排队打饭,等待饭菜再拿走。
nio
不等待数据就序而是直接返回,注册事件等待通知,数据就序通知程序后再做处理 ->前台下单拿号,等饭菜准备好叫号通知取餐。
aio
不等待数据就序直接返回。数据就序后回调->饭店点菜,菜做好后服务员端菜到客人桌上。
这里饭店相当于服务器,点菜相当于io连接,饭菜相当于数据,饭菜做好相当于数据就序。端菜相当于读取数据。
2.阻塞和非阻塞
数据就序前是否需要死等。对于阻塞操作。没有数据传过来,读操作就会阻塞死等,知道数据过来。写操作同理,当缓冲区满的时候,也会阻塞等待直到缓存区刷新。
类比 -> 饭菜没做好,需不需要在窗口死等
3.同步和异步
数据就序后需要自己去操作是同步。数据直接回调给程序是异步。
类比->饭菜做好后,是否需要自己去端
4.channel,buffer和selector
channel(管道)
java nio 将原来socket的连接,读写等操作抽象到channel当中。根据用途不同,主要有以下四种实现。
FileChannel - 文件io
DatagramChannel - udp io
SocketChannel - tcp客户端 io 和服务器读写
ServerSocketChannel - tcp服务器accept请求
SocketChannel & ServerSocketChannel
server/client | SocketChannel/ServerSocketChannel | accept | read | write | connect |
---|---|---|---|---|---|
server | ServerSocketChannel | V | – | – | – |
server | SocketChannel | – | V | V | – |
client | SocketChannel | – | V | V | V |
buffer(缓冲区)
channel 数据读写都要通过buffer
selector
selector回扫描注册监听事件中那些channel是就序的,再通知操作。
demo
client使用nio。服务器端分别粘上bio和nio的实现做对比
服务器端 bio实现
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
public class BioSocketServer {
public static void server(){
ServerSocket serverSocket = null;
InputStream in = null;
try
{
serverSocket = new ServerSocket(8080);
int recvMsgSize = 0;
byte[] recvBuf = new byte[1024];
while(true){
Socket clientSocket = serverSocket.accept();
SocketAddress addr = clientSocket.getRemoteSocketAddress();
System.out.println("Handling client at "+addr);
in = clientSocket.getInputStream();
while((recvMsgSize=in.read(recvBuf))!=-1){
byte[] temp = new byte[recvMsgSize];
System.arraycopy(recvBuf, 0, temp, 0, recvMsgSize);
System.out.println(new String(temp));
// System.out.println(new String(recvBuf));
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally{
try{
if(serverSocket!=null){
serverSocket.close();
}
if(in!=null){
in.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
server();
}
}
服务器端 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;
public class NioSocketServer {
private static final int BUF_SIZE=1024;
private static final int PORT = 8080;
private static final int TIMEOUT = 3000;
public static void main(String[] args)
{
selector();
}
public static void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
SocketChannel sc = ssChannel.accept();
sc.configureBlocking(false);
sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocateDirect(BUF_SIZE));
}
public static void handleRead(SelectionKey key) throws IOException{
SocketChannel sc = (SocketChannel)key.channel();
ByteBuffer buf = (ByteBuffer)key.attachment();
long bytesRead = sc.read(buf);
while(bytesRead>0){
buf.flip();
while(buf.hasRemaining()){
System.out.print((char)buf.get());
}
System.out.println();
buf.clear();
bytesRead = sc.read(buf);
}
if(bytesRead == -1){
sc.close();
}
}
public static void handleWrite(SelectionKey key) throws IOException{
ByteBuffer buf = (ByteBuffer)key.attachment();
buf.flip();
SocketChannel sc = (SocketChannel) key.channel();
while(buf.hasRemaining()){
sc.write(buf);
}
buf.compact();
}
public static void selector() {
Selector selector = null;
ServerSocketChannel ssc = null;
try{
selector = Selector.open();
ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(PORT));
ssc.configureBlocking(false);
ssc.register(selector,SelectionKey.OP_ACCEPT);
while(true){
if(selector.select(TIMEOUT) == 0){
System.out.println("==");
continue;
}
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while(iter.hasNext()){
SelectionKey key = iter.next();
if(key.isAcceptable()){
handleAccept(key);
}
if(key.isReadable()){
handleRead(key);
}
if(key.isWritable() && key.isValid()){
handleWrite(key);
}
if(key.isConnectable()){
System.out.println("isConnectable = true");
}
iter.remove();
}
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(selector!=null){
selector.close();
}
if(ssc!=null){
ssc.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
客户端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.TimeUnit;
public class NioSocketClient {
public static void client(){
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = null;
try
{
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1",8080));
if(socketChannel.finishConnect()){
int i = 0;
while(true)
{
TimeUnit.SECONDS.sleep(1);
String info = "I'm "+i+++"-th information from client";
buffer.clear();
buffer.put(info.getBytes());
buffer.flip();
while(buffer.hasRemaining()){
System.out.println(buffer);
socketChannel.write(buffer);
}
}
}
}
catch (IOException | InterruptedException e)
{
e.printStackTrace();
}
finally{
try{
if(socketChannel!=null){
socketChannel.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
client();
}
}
最后
参考:
Java NIO?看这一篇就够了!.
demo代码:
java-test.