非 LISTEN 状态
表示的当前等待服务端调用 accept 来三次握手的 listen backlog 数值,即图中的全连接队列值(最大值为Send_Q+1),当客户端通过connect() 去连接正在 listen() 的服务端时,这些连接会经过半连接队列,接收到ACK后,进入accept queue(全连接队列) ,在里面等待直到被服务端 accept();
Send-Q 表示的则是最大的 listen backlog 数值,这就是 min(backlog, /proc/sys/net/core/somaxconn)的值
启动服务端执行命令:(注意:在连接事件发生后|accept操作执行前会睡眠Thread.sleep(100000);)
使用NIO作为服务端监听TCP端口:9779
~/recv_q_send_q# /usr/bin/java NIOServer
NOIServer start run in port 9779
可以看到默认Send-Q值为50;
如果并发请求的链接数超多50,多余的怎样?感兴趣的可以自己修改代码测试mark
如果等待时间过长客户端也会收到服务端的拒绝反馈,正常情况下如果超出的连接数多不并且等待时间不多,可以得到服务端的正常响应
启动客户端执行命令:
/usr/bin/java NIOClient
等待100秒后:
参考NIO代码:
server端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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;
import java.util.Set;
public class NIOServer {
private int num;
private static final int BLOCK = 4096;
private static final ByteBuffer sendB = ByteBuffer.allocate(BLOCK);
private static final ByteBuffer receB = ByteBuffer.allocate(BLOCK);
private Selector selector;
public NIOServer(int port) throws IOException {
//开启ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//设置为非阻塞
serverSocketChannel.configureBlocking(false);
//获取ServerSocket
ServerSocket serverSocket = serverSocketChannel.socket();
//绑定ServerSocket提供服务的端口
serverSocket.bind(new InetSocketAddress(port));
//开启选择器
selector = Selector.open();
//将ServerSocketChannel注册到选择器上
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NOIServer start run in port " + port);
}
/**
* 监听选择器的数据
*
* @throws IOException
*/
private void listen() throws IOException {
//循环监听,事件驱动模式
while (true) {
//select()阻塞,等待有事件发生时唤醒
selector.select();
Set selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//处理完后移除该事件
iterator.remove();
//处理该事件
handleKey(selectionKey);
}
}
}
/**
* 处理选择器的监听事件
*
* @param selectionKey 选择器的监听事件key
* @throws IOException
*/
private void handleKey(SelectionKey selectionKey) throws IOException {
ServerSocketChannel serverSocketChannel = null;
SocketChannel socketChannel = null;
int count = 0;
//客户端新连接
if (selectionKey.isAcceptable()) {
//开启通道连接
serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
try {
System.out.println(System.currentTimeMillis() + "收到请求我在睡眠100秒...");
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
socketChannel = serverSocketChannel.accept();
System.out.println(System.currentTimeMillis() + "accept...");
//设置为非阻塞
socketChannel.configureBlocking(false);
//将通道注册到选择器
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
//获取读事件通道
socketChannel = (SocketChannel) selectionKey.channel();
//清除原先读缓存
receB.clear();
//读取通道缓存
count = socketChannel.read(receB);
if (count > 0) {
//解析通道缓存数据
String receMsg = new String(receB.array(), 0, count);
System.out.println("receive from client " + receMsg);
//注册切到写事件
socketChannel.register(selector, SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()) {
//获取写事件通道
socketChannel = (SocketChannel) selectionKey.channel();
//清除发送缓存数据
sendB.clear();
String sendMsg = "num " + num++;
//设置待发送的数据
sendB.put(sendMsg.getBytes());
//准备写
sendB.flip();
int write = socketChannel.write(sendB);
System.out.println("send to client " + sendMsg);
//注册切到读事件
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
public static void main(String[] args) throws Exception {
new NIOServer(9779).listen();
}
}
客户端代码:
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.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOClient {
private static final int BLOCK = 4096;
private static final ByteBuffer sendB = ByteBuffer.allocate(BLOCK);
private static final ByteBuffer receB = ByteBuffer.allocate(BLOCK);
private SocketChannel socketChannel;
private Selector selector;
public NIOClient(String ip, int port) throws IOException {
//开启通道
socketChannel = SocketChannel.open();
//设置为非阻塞
socketChannel.configureBlocking(false);
//开启选择器
selector = Selector.open();
//将通道注册到选择器
socketChannel.register(selector, SelectionKey.OP_CONNECT);
//连接服务端
socketChannel.connect(new InetSocketAddress(ip, port));
}
/**
* 连接服务器
*/
public void connect() throws IOException {
Set selectionKeys;
Iterator iterator;
SelectionKey selectionKey;
int index = 0;
while (true && index < 1000) {
index++;
selector.select();
selectionKeys = selector.selectedKeys();
iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
selectionKey = iterator.next();
handleKey(selectionKey, index);
}
selectionKeys.clear();
}
//交互10次后关闭连接
socketChannel.close();
}
/**
* 处理选择器监听事件
*
* @param selectionKey
*/
public void handleKey(SelectionKey selectionKey, int index) throws IOException {
SocketChannel client;
int count = 0;
// 连接事件
if (selectionKey.isConnectable()) {
System.out.println("client connect......");
client = (SocketChannel) selectionKey.channel();
if (client.isConnectionPending()) {
client.finishConnect();
sendB.clear();
sendB.put("Hello, Server".getBytes());
sendB.flip();
client.write(sendB);
}
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 读事件
client = (SocketChannel) selectionKey.channel();
receB.clear();
count = client.read(receB);
if (count > 0) {
String receMsg = new String(receB.array(), 0, count);
System.out.println("receive from server " + receMsg);
client.register(selector, SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()) {
// 给客户端注册写事件
client = (SocketChannel) selectionKey.channel();
sendB.clear();
String sendMsg = "index " + index;
sendB.put(sendMsg.getBytes());
sendB.flip();
client.write(sendB);
System.out.println("send to server " + sendMsg);
client.register(selector, SelectionKey.OP_READ);
}
}
// 模拟10个请求,并发10秒后同时执行connection
public static void main(String[] args) throws Exception {
for(int i = 0; i<10;i++) {
final int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("========"+ finalI);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
new NIOClient("127.0.0.1", 9779).connect();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
System.in.read();
}
}