TCP状态转换图
影响网络传输的因素
将一份数据从一个地方正确地传输到另一个地方所需要的时间我们称之为响应时间。
影响这个时间的因素有很多:
Socket
Java Socket的工作机制
Socket这个概念没有对应到一个具体的实体,它描述计算机之间相互通信的一种抽象功能。
打个比方,可以把Socket比作两个城市之间的交通工具,有了它,就可以在城市之间来回穿梭了。
交通工具有多种,每种交通工具也有相应的交通规则。
Socket也一样,也有多种。大部分情况下我们用的都是基于TCP/IP的流套接字,它是一种稳定的通信协议。
建立通信链路
数据传输
NIO的工作方式
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;
/**
* Created by 18351 on 2018/10/23.
*
* TCP/IP的非阻塞方式
* 服务端
*/
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();
}
}
/**
* 客户端连接服务器
* @param key
* @throws IOException
*/
public void accept(SelectionKey key) throws IOException{
ServerSocketChannel server = (ServerSocketChannel) key.channel();
if (server.equals(serversocket1)) {
clientchannel1 = server.accept(); //连接到服务端1
clientchannel1.configureBlocking(false);
//OP_READ用于读取操作的操作集位
clientchannel1.register(this.selector, SelectionKey.OP_READ);
} else {
clientchannel2 = server.accept();////连接到服务端2
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();
}
}
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/**
* Created by 18351 on 2018/10/23.
* TCP/IP非阻塞方式
* 客户端
*/
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);
new Client().query("localhost", 9099);
}
}
网友评论