前面我介绍了磁盘IO,但我们现在是互联网时代,肯定得有网络IO呀。先截段原文:
我们知道HTTP是基于TCP的,我们先来看一下TCP状态转换图:
TCP状态转换图:
了解TCP状态是有一定作用的。
影响网络传输的因素:
将一份数据
从一个地方正确地传输到另一个地方所需要的时间我们称之为
响应时间。影响这个时间的因素有很多:
Socket:
Java Socket的工作机制
Socket这个概念没有对应到一个具体的实体,它描述计算机之间相互通信的一种抽象功能。打个比方,可以把Socket比作两个城市之间的交通工具,有了它,就可以在城市之间来回穿梭了。交通工具有多种,每种交通工具也有相应的交通规则。Socket也一样,也有多种。大部分情况下我们用的都是基于TCP/IP的流套接字,它是一种稳定的通信协议。
建立通信链路
数据传输
NIO的工作方式
BIO带来的挑战
NIO的工作机制
下面放一个示例:
- package 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.nio.channels.spi.SelectorProvider;
- import java.util.Iterator;
- /**
- * TCP/IP的NIO非阻塞方式
- * 服务器端
- * */
- public class Server implements Runnable {
- //第一个端口
- private Integer port1 = 8099;
- //第二个端口
- private Integer port2 = 9099;
- //第一个服务器通道 服务A
- private ServerSocketChannel serversocket1;
- //第二个服务器通道 服务B
- private ServerSocketChannel serversocket2;
- //连接1
- private SocketChannel clientchannel1;
- //连接2
- private SocketChannel clientchannel2;
- //选择器,主要用来监控各个通道的事件
- private Selector selector;
- //缓冲区
- private ByteBuffer buf = ByteBuffer.allocate(512);
- public Server() {
- init();
- }
- /**
- * 这个method的作用
- * 1:是初始化选择器
- * 2:打开两个通道
- * 3:给通道上绑定一个socket
- * 4:将选择器注册到通道上
- * */
- public void init() {
- try {
- //创建选择器
- this.selector = SelectorProvider.provider().openSelector();
- //打开第一个服务器通道
- this.serversocket1 = ServerSocketChannel.open();
- //告诉程序现在不是阻塞方式的
- this.serversocket1.configureBlocking(false);
- //获取现在与该通道关联的套接字
- this.serversocket1.socket().bind(new InetSocketAddress(“localhost”, this.port1));
- //将选择器注册到通道上,返回一个选择键
- //OP_ACCEPT用于套接字接受操作的操作集位
- this.serversocket1.register(this.selector, SelectionKey.OP_ACCEPT);
- //然后初始化第二个服务端
- this.serversocket2 = ServerSocketChannel.open();
- this.serversocket2.configureBlocking(false);
- this.serversocket2.socket().bind(new InetSocketAddress(“localhost”, this.port2));
- this.serversocket2.register(this.selector, SelectionKey.OP_ACCEPT);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 这个方法是连接
- * 客户端连接服务器
- * @throws IOException
- * */
- public void accept(SelectionKey key) throws IOException {
- ServerSocketChannel server = (ServerSocketChannel) key.channel();
- if (server.equals(serversocket1)) {
- clientchannel1 = server.accept();
- clientchannel1.configureBlocking(false);
- //OP_READ用于读取操作的操作集位
- clientchannel1.register(this.selector, SelectionKey.OP_READ);
- } else {
- clientchannel2 = server.accept();
- clientchannel2.configureBlocking(false);
- //OP_READ用于读取操作的操作集位
- clientchannel2.register(this.selector, SelectionKey.OP_READ);
- }
- }
- /**
- * 从通道中读取数据
- * 并且判断是给那个服务通道的
- * @throws IOException
- * */
- public void read(SelectionKey key) throws IOException {
- this.buf.clear();
- //通过选择键来找到之前注册的通道
- //但是这里注册的是ServerSocketChannel为什么会返回一个SocketChannel??
- SocketChannel channel = (SocketChannel) key.channel();
- //从通道里面读取数据到缓冲区并返回读取字节数
- int count = channel.read(this.buf);
- if (count == -1) {
- //取消这个通道的注册
- key.channel().close();
- key.cancel();
- return;
- }
- //将数据从缓冲区中拿出来
- String input = new String(this.buf.array()).trim();
- //那么现在判断是连接的那种服务
- if (channel.equals(this.clientchannel1)) {
- System.out.println(”欢迎您使用服务A”);
- System.out.println(”您的输入为:” + input);
- } else {
- System.out.println(”欢迎您使用服务B”);
- System.out.println(”您的输入为:” + input);
- }
- }
- @Override
- public void run() {
- while (true) {
- try {
- System.out.println(”running … ”);
- //选择一组键,其相应的通道已为 I/O 操作准备就绪。
- this.selector.select();
- //返回此选择器的已选择键集
- //public abstract Set<SelectionKey> selectedKeys()
- Iterator selectorKeys = this.selector.selectedKeys().iterator();
- while (selectorKeys.hasNext()) {
- System.out.println(”running2 … ”);
- //这里找到当前的选择键
- SelectionKey key = (SelectionKey) selectorKeys.next();
- //然后将它从返回键队列中删除
- selectorKeys.remove();
- if (!key.isValid()) { // 选择键无效
- continue;
- }
- if (key.isAcceptable()) {
- //如果遇到请求那么就响应
- this.accept(key);
- } else if (key.isReadable()) {
- //读取客户端的数据
- this.read(key);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public static void main(String[] args) {
- Server server = new Server();
- Thread thread = new Thread(server);
- thread.start();
- }
- }
package 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.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
/**
* TCP/IP的NIO非阻塞方式
* 服务器端
* */
public class Server implements Runnable {
//第一个端口
private Integer port1 = 8099;
//第二个端口
private Integer port2 = 9099;
//第一个服务器通道 服务A
private ServerSocketChannel serversocket1;
//第二个服务器通道 服务B
private ServerSocketChannel serversocket2;
//连接1
private SocketChannel clientchannel1;
//连接2
private SocketChannel clientchannel2;
//选择器,主要用来监控各个通道的事件
private Selector selector;
//缓冲区
private ByteBuffer buf = ByteBuffer.allocate(512);
public Server() {
init();
}
/**
* 这个method的作用
* 1:是初始化选择器
* 2:打开两个通道
* 3:给通道上绑定一个socket
* 4:将选择器注册到通道上
* */
public void init() {
try {
//创建选择器
this.selector = SelectorProvider.provider().openSelector();
//打开第一个服务器通道
this.serversocket1 = ServerSocketChannel.open();
//告诉程序现在不是阻塞方式的
this.serversocket1.configureBlocking(false);
//获取现在与该通道关联的套接字
this.serversocket1.socket().bind(new InetSocketAddress("localhost", this.port1));
//将选择器注册到通道上,返回一个选择键
//OP_ACCEPT用于套接字接受操作的操作集位
this.serversocket1.register(this.selector, SelectionKey.OP_ACCEPT);
//然后初始化第二个服务端
this.serversocket2 = ServerSocketChannel.open();
this.serversocket2.configureBlocking(false);
this.serversocket2.socket().bind(new InetSocketAddress("localhost", this.port2));
this.serversocket2.register(this.selector, SelectionKey.OP_ACCEPT);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 这个方法是连接
* 客户端连接服务器
* @throws IOException
* */
public void accept(SelectionKey key) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
if (server.equals(serversocket1)) {
clientchannel1 = server.accept();
clientchannel1.configureBlocking(false);
//OP_READ用于读取操作的操作集位
clientchannel1.register(this.selector, SelectionKey.OP_READ);
} else {
clientchannel2 = server.accept();
clientchannel2.configureBlocking(false);
//OP_READ用于读取操作的操作集位
clientchannel2.register(this.selector, SelectionKey.OP_READ);
}
}
/**
* 从通道中读取数据
* 并且判断是给那个服务通道的
* @throws IOException
* */
public void read(SelectionKey key) throws IOException {
this.buf.clear();
//通过选择键来找到之前注册的通道
//但是这里注册的是ServerSocketChannel为什么会返回一个SocketChannel??
SocketChannel channel = (SocketChannel) key.channel();
//从通道里面读取数据到缓冲区并返回读取字节数
int count = channel.read(this.buf);
if (count == -1) {
//取消这个通道的注册
key.channel().close();
key.cancel();
return;
}
//将数据从缓冲区中拿出来
String input = new String(this.buf.array()).trim();
//那么现在判断是连接的那种服务
if (channel.equals(this.clientchannel1)) {
System.out.println("欢迎您使用服务A");
System.out.println("您的输入为:" + input);
} else {
System.out.println("欢迎您使用服务B");
System.out.println("您的输入为:" + input);
}
}
@Override
public void run() {
while (true) {
try {
System.out.println("running ... ");
//选择一组键,其相应的通道已为 I/O 操作准备就绪。
this.selector.select();
//返回此选择器的已选择键集
//public abstract Set<SelectionKey> selectedKeys()
Iterator selectorKeys = this.selector.selectedKeys().iterator();
while (selectorKeys.hasNext()) {
System.out.println("running2 ... ");
//这里找到当前的选择键
SelectionKey key = (SelectionKey) selectorKeys.next();
//然后将它从返回键队列中删除
selectorKeys.remove();
if (!key.isValid()) { // 选择键无效
continue;
}
if (key.isAcceptable()) {
//如果遇到请求那么就响应
this.accept(key);
} else if (key.isReadable()) {
//读取客户端的数据
this.read(key);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server server = new Server();
Thread thread = new Thread(server);
thread.start();
}
}
- package nio;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.nio.ByteBuffer;
- import java.nio.channels.SocketChannel;
- import java.net.InetAddress;
- /**
- * TCP/IP的NIO非阻塞方式
- * 客户端
- * */
- public class Client {
- //创建缓冲区
- private ByteBuffer buffer = ByteBuffer.allocate(512);
- //访问服务器
- public void query(String host, int port) throws IOException {
- InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(host), port);
- SocketChannel socket = null;
- byte[] bytes = new byte[512];
- while (true) {
- try {
- System.in.read(bytes);
- socket = SocketChannel.open();
- socket.connect(address);
- buffer.clear();
- buffer.put(bytes);
- buffer.flip();
- socket.write(buffer);
- buffer.clear();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (socket != null) {
- socket.close();
- }
- }
- }
- }
- public static void main(String[] args) throws IOException {
- new Client().query(“localhost”, 8099);
- }
- }
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.net.InetAddress;
/**
* TCP/IP的NIO非阻塞方式
* 客户端
* */
public class Client {
//创建缓冲区
private ByteBuffer buffer = ByteBuffer.allocate(512);
//访问服务器
public void query(String host, int port) throws IOException {
InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(host), port);
SocketChannel socket = null;
byte[] bytes = new byte[512];
while (true) {
try {
System.in.read(bytes);
socket = SocketChannel.open();
socket.connect(address);
buffer.clear();
buffer.put(bytes);
buffer.flip();
socket.write(buffer);
buffer.clear();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
}
public static void main(String[] args) throws IOException {
new Client().query("localhost", 8099);
}
}
NIO主要原理与使用
Buffer的工作方式
NIO的文件访问方式
</div>
</div>