您的位置:首页 > 理论基础 > 计算机网络

《深入分析JavaWeb技术内幕》读书笔记三.网络IO和NIO

2017-04-29 10:27 330 查看
前面我介绍了磁盘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.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的文件访问方式





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息