先启动server 再启动client
package learn.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
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.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class NIOSServer {
private int port = 8888;
// 解码buffer
private Charset cs = Charset.forName("utf-8");
/* 接受数据缓冲区 */
private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);
/* 发送数据缓冲区 */
private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);
/* 映射客户端channel */
private Map<String, SocketChannel> clientsMap = new HashMap<String, SocketChannel>();
private static Selector selector;
public NIOSServer(int port) {
this.port = port;
try {
init();
} catch (Exception e) {
e.printStackTrace();
}
}
private void init() throws IOException {
/*
* 启动服务器端,配置为非阻塞,绑定端口,注册accept事件 ACCEPT事件:当服务端收到客户端连接请求时,触发该事件
*/
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("server start on port:" + port);
}
/**
* 服务器端轮询监听,select方法会一直阻塞直到有相关事件发生或超时
*/
private void listen() {
while (true) {
try {
selector.select();// 返回值为本次触发的事件数
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey key : selectionKeys) {
handle(key);
}
selectionKeys.clear();// 清除处理过的事件
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
/**
* 处理不同的事件
*/
private void handle(SelectionKey selectionKey) throws IOException {
ServerSocketChannel server = null;
SocketChannel client = null;
String receiveText = null;
int count = 0;
if (selectionKey.isAcceptable()) {
/*
* 客户端请求连接事件 serversocket为该客户端建立socket连接,将此socket注册READ事件,监听客户端输入
* READ事件:当客户端发来数据,并已被服务器控制线程正确读取时,触发该事件
*/
server = (ServerSocketChannel) selectionKey.channel();
client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
/*
* READ事件,收到客户端发送数据,读取数据后继续注册监听客户端
*/
client = (SocketChannel) selectionKey.channel();
rBuffer.clear();
count = client.read(rBuffer);
if (count > 0) {
rBuffer.flip();
receiveText = String.valueOf(cs.decode(rBuffer).array());
System.out.println(client.toString() + ":" + receiveText);
dispatch(client, receiveText);
client = (SocketChannel) selectionKey.channel();
// client.register(selector, SelectionKey.OP_READ);
}
}
}
/**
* 把当前客户端信息 推送到其他客户端
*/
private void dispatch(SocketChannel client, String info) throws IOException {
Socket s = client.socket();
String name = "[" + s.getInetAddress().toString().substring(1) + ":" + Integer.toHexString(client.hashCode())
+ "]";
if (!clientsMap.isEmpty()) {
for (Map.Entry<String, SocketChannel> entry : clientsMap.entrySet()) {
SocketChannel temp = entry.getValue();
if (!client.equals(temp)) {
sBuffer.clear();
sBuffer.put((name + ":" + info).getBytes());
sBuffer.flip();
// 输出到通道
temp.write(sBuffer);
}
}
}
clientsMap.put(name, client);
}
public static void main(String[] args) throws IOException {
NIOSServer server = new NIOSServer(7777);
server.listen();
}
}
client
package learn.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.SocketChannel;
import java.util.Date;
import java.util.Scanner;
import java.util.Set;
public class NIOSClient {
/* 发送数据缓冲区 */
private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);
/* 接受数据缓冲区 */
private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);
/* 服务器端地址 */
private InetSocketAddress SERVER;
private static Selector selector;
private static SocketChannel client;
private static String receiveText;
private static String sendText;
private static int count = 0;
public NIOSClient(int port) {
SERVER = new InetSocketAddress("localhost", port);
init();
}
public void init() {
try {
/*
* 客户端向服务器端发起建立连接请求
*/
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(SERVER);
/*
* 轮询监听客户端上注册事件的发生
*/
while (true) {
selector.select();
Set<SelectionKey> keySet = selector.selectedKeys();
for (final SelectionKey key : keySet) {
handle(key);
}
;
keySet.clear();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
new NIOSClient(7777);
}
private void handle(SelectionKey selectionKey) throws IOException {
if (selectionKey.isConnectable()) {
/*
* 连接建立事件,已成功连接至服务器
*/
client = (SocketChannel) selectionKey.channel();
if (client.isConnectionPending()) {
client.finishConnect();
System.out.println("connect success !");
sBuffer.clear();
sBuffer.put((new Date() + " connected!").getBytes());
sBuffer.flip();
client.write(sBuffer);// 发送信息至服务器
/*
* 原文来自站长网 启动线程一直监听客户端输入,有信息输入则发往服务器端 因为输入流是阻塞的,所以单独线程监听
*/
new Thread() {
@Override
public void run() {
while (true) {
try {
sBuffer.clear();
Scanner cin = new Scanner(System.in);
sendText = cin.nextLine();
System.out.println(sendText);
/*
* 未注册WRITE事件,因为大部分时间channel都是可以写的
*/
sBuffer.put(sendText.getBytes("utf-8"));
sBuffer.flip();
client.write(sBuffer);
} catch (IOException e) {
e.printStackTrace();
break;
}
}
};
}.start();
}
// 注册读事件
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
/*
* 读事件触发 有从服务器端发送过来的信息,读取输出到屏幕上后,继续注册读事件 监听服务器端发送信息
*/
client = (SocketChannel) selectionKey.channel();
rBuffer.clear();
count = client.read(rBuffer);
if (count > 0) {
receiveText = new String(rBuffer.array(), 0, count);
System.out.println(receiveText);
client = (SocketChannel) selectionKey.channel();
client.register(selector, SelectionKey.OP_READ);
}
}
}
}
server通道
package learn.nio;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
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.ArrayList;
import java.util.List;
import java.util.Set;
/*
* 实现了nio连接,读写传输,通道关闭操作
*/
public class ServerSocketChannelDemo {
public static boolean btemp = false;
public static String stemp = "";
public static Integer itemp = 0;
public static Selector selector;
// 装写入数据缓存,分配一个新的字节缓冲区
public static ByteBuffer wbuffer = ByteBuffer.allocate(1024);
// 装读入数据缓存,分配一个新的字节缓冲区
public static ByteBuffer rbuffer = ByteBuffer.allocate(1024);
// 装已连接的客户端
public static List<SocketChannel> clients = new ArrayList<SocketChannel>();
public static void dispose(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = null;
SocketChannel socketChannel = null;
btemp = key.isValid();
if (btemp) {
System.out.println("这事件是否有效: " + btemp);
btemp = key.isReadable();
if (btemp) {
System.out.println("是否为读取key: " + btemp);
// 获取注册该事件的通道
socketChannel = (SocketChannel) key.channel();
btemp = socketChannel.isConnected();
System.out.println("是否连接成功: " + btemp);
btemp = socketChannel.isConnectionPending();
System.out.println("是否正在连接: " + btemp);
btemp = socketChannel.isRegistered();
System.out.println("是否注册到选择器: " + btemp);
btemp = socketChannel.isOpen();
System.out.println("是否打开通道: " + btemp);
// 用之前选清空一下
rbuffer.clear();
itemp = socketChannel.read(rbuffer);
// 读取的字节数,可能为零,如果该通道已到达流的末尾,则返回 -1 大于0表示有数据
System.out.println("客户端转过来的字节长度: " + itemp);
if (itemp > 0) {
// 反转缓存
rbuffer.flip();
stemp = new String(rbuffer.array());
System.out.println("读取内容: " + stemp);
// 转发给其他客户端
int count = 1;
for (SocketChannel client : clients) {
if (!client.equals(socketChannel)) {
btemp = client.isOpen();
System.out.println("客户端通道是否打开: " + btemp);
btemp = client.isConnected();
System.out.println("客户端通道连接是否成功: " + btemp);
btemp = client.socket().isClosed();
System.out.println("客户端socket是否关闭: " + btemp);
System.out.println("服务端存入的客户端数: " + clients.size());
if (client.isOpen() && client.isConnected()) {
System.out.println("转发 " + count + " 个客户端");
count++;
// 用之前选清空一下
wbuffer.clear();
wbuffer.put(stemp.getBytes());
// 装完数据,反转下
wbuffer.flip();
client.write(wbuffer);
}
}
}
} else if (itemp == 0) {
System.out.println("无视");
} else if (itemp < 0) {
System.out.println("客户端已关闭,关闭该通道");
socketChannel.close();
// 注销这无效读事件
// key.cancel();
System.out.println("退出函数");
return;
}
}
btemp = key.isAcceptable();
if (btemp) {
System.out.println("是否接受新的套接字连接key: " + btemp);
// 获取注册该事件的通道
serverSocketChannel = (ServerSocketChannel) key.channel();
// 接收连接,返回客户端通道
socketChannel = serverSocketChannel.accept();
// 将以连接的客户端连接保存起来
clients.add(socketChannel);
System.out.println("打印客户端socket地址" + socketChannel.getRemoteAddress());
// System.out.println("处理完结束客户端这次请求是否成功: " + btemp);
// System.exit(1);
// 是否阻塞
socketChannel.configureBlocking(false);
// 将客户端注册读事件key到选择器上
socketChannel.register(selector, SelectionKey.OP_READ);
}
btemp = key.isConnectable();
if (btemp) {
System.out.println("是否连接操作key: " + btemp);
}
btemp = key.isWritable();
if (btemp) {
System.out.println("是否写入key: " + btemp);
}
} else {
System.out.println("有无效事件key");
}
}
public static void listen() {
while (true) {
try {
itemp = selector.select();
if (itemp != 0) {
System.out.println("触发事件数: " + itemp);
}
Set<SelectionKey> keys = selector.selectedKeys();
if (!keys.isEmpty()) {
System.out.println("事件key数: " + keys.size());
for (SelectionKey key : keys) {
dispose(key);
}
// 经过事件处理方法后清空
keys.clear();
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String[] args) {
try {
// 打开通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
btemp = serverSocketChannel.isOpen();
System.out.println("通道是否打开: " + btemp);
// io是否阻塞
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
// 创建通道选择器
selector = Selector.open();
/*
* 注册选择器 OP_ACCEPT 用于套接字接受操作的操作集位。 OP_CONNECT 用于套接字连接操作的操作集位 OP_READ
* 用于读取操作的操作集位 OP_WRITE 用于写入操作的操作集位。
*
*/
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
btemp = serverSocketChannel.isRegistered();
System.out.println("该通道是否有选择器注册: " + btemp);
// 绑定ip和端口
serverSocket.bind(new InetSocketAddress("localhost", 9999));
// 如果Socket已经与一个本地端口绑定, 则返回true , 否则返回false
btemp = serverSocket.isBound();
System.out.println("如果Socket已经与一个本地端口绑定, 则返回true , 否则返回false: " + btemp);
itemp = serverSocket.getLocalPort();
System.out.println("获取ServerSocket的port,没设置则为-1: " + itemp);
serverSocketChannel = serverSocket.getChannel();// 获取所属nio通道
InetAddress iaddr = serverSocket.getInetAddress();
// 没有设置地址则该对象为空
if (iaddr == null) {
System.out.println("iaddr==null");
} else {
stemp = iaddr.getHostAddress();
System.out.println("ip地址: " + stemp);
System.out.println("InetAddress实例toString" + iaddr.toString());
}
SocketAddress saddr = serverSocket.getLocalSocketAddress();
if (saddr == null) {
System.out.println("saddr==null");
} else {
System.out.println("SocketAddress对象打印: " + saddr);
}
/*
* ReceiveBufferSize
* TCP发送缓存区和接收缓存区,默认是8192,一般情况下足够了,而且就算你增加了发送缓存区,对方没有增加它对应的接收缓冲,
* 那么在TCP三握手时,最后确定的最大发送窗口还是双方最小的那个缓冲区,就算你无视,发了更多的数据,那么多出来的数据也会被丢弃。
* 除非双方都协商好。
*/
itemp = serverSocket.getReceiveBufferSize();
System.out.println("缓存区和接收缓存区默认是8192: " + itemp);
btemp = serverSocket.getReuseAddress();
System.out.println(btemp);
itemp = serverSocket.getSoTimeout();
System.out.println("serverSocket超时设置 默认0不会超时: " + itemp);
listen();
} catch (IOException e) {
e.printStackTrace();
}
}
}
client通道
package learn.nio;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Set;
public class SocketChannelDemo {
public static boolean btemp = false;
public static String stemp = "";
public static Integer itemp = 0;
public static Selector selector = null;
public static ByteBuffer wbuffer = ByteBuffer.allocate(1024);
public static ByteBuffer rbuffer = ByteBuffer.allocate(1024);
public static void dispose(SelectionKey key) throws IOException {
SocketChannel socketChannel = null;
btemp = key.isValid();
if (btemp) {
System.out.println("这事件key是否有效: " + btemp);
btemp = key.isConnectable();
if (btemp) {
System.out.println("是否连接服务端成功: " + btemp);
socketChannel = (SocketChannel) key.channel();
// 确认连接建立完成,没有这个服务端会一直提示有个客户端有个连接请求没有处理
btemp = socketChannel.finishConnect();
System.out.println("对服务端表示客户端连接是否完成: " + btemp);
System.out.println("连接服务端成功");
if (btemp) {
// System.exit(1);
// System.out.println("退出");
}
// 给客户端发数据
stemp = new Date() + "一个客户端连接服务端成功";
// 用之前先清空一下
wbuffer.clear();
// 添加写内容
wbuffer.put(stemp.getBytes());
// 反转一下,不然服务端读不出来
wbuffer.flip();
// 通过通道客户端通道传给服务端
socketChannel.write(wbuffer);
// 注册读事件key
socketChannel.register(selector, SelectionKey.OP_READ);
}
btemp = key.isReadable();
if (btemp) {
System.out.println("客户端处理读事件");
socketChannel = (SocketChannel) key.channel();
btemp = socketChannel.isOpen();
System.out.println("客户端通道是否打开: " + btemp);
btemp = socketChannel.isConnected();
System.out.println("客户端通道是否连接成功: " + btemp);
if (!socketChannel.isOpen() || !socketChannel.isConnected()) {
System.out.println("该通道以关闭");
}
// 用之前先清空一下
rbuffer.clear();
itemp = socketChannel.read(rbuffer);
if (itemp > 0) {
// 装完数据反转一下
rbuffer.flip();
stemp = new String(rbuffer.array());
System.out.println("服务器传过来的消息" + stemp);
btemp = socketChannel.socket().isClosed();
System.out.println("客户端socket是否关闭: " + btemp);
} else if (itemp == 0) {
} else if (itemp < 0) {
System.out.println("服务端关闭了通道");
socketChannel.close();
}
if (socketChannel.isOpen()) {
// 接收服务端的消息退出
socketChannel.close();
// key.cancel();
System.out.println("客户端关闭通道,关闭客户端程序");
System.exit(1);
}
}
} else {
System.out.println("有无效事件key");
}
}
public static void listen() throws IOException {
while (true) {
itemp = selector.select();
if (itemp != 0) {
System.out.println("客户端事件数: " + itemp);
itemp = 0;
}
Set<SelectionKey> keys = selector.selectedKeys();
if (!keys.isEmpty()) {
System.out.println("事件key数: " + keys.size());
for (SelectionKey key : keys) {
dispose(key);
}
// 经过事件处理方法后清空
keys.clear();
}
}
}
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
// 是否阻塞
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
// 绑定客户端网络地址,不绑定则随机分配
// socketChannel.bind(new InetSocketAddress("localhost", 6666));
// 连接服务端网络地址
socketChannel.connect(new InetSocketAddress("localhost", 9999));
// 返回相关socket
Socket socket = socketChannel.socket();
// 获取所属nio通道
socket.getChannel();
btemp = socket.getKeepAlive();
System.out.println("是否为保留socket: " + btemp);
InetAddress iaddr = socket.getLocalAddress();
if (iaddr == null) {
System.err.println("iaddr==null");
} else {
stemp = iaddr.getHostAddress();
System.out.println("获取InetAddress实例hostAddress 默认 0.0.0.0: " + stemp);
stemp = iaddr.getHostName();
System.out.println("获取InetAddress实例hostName 默认 0.0.0.0: " + stemp);
}
itemp = socket.getLocalPort();
System.out.println("socket端口,没设置为-1: " + itemp);
SocketAddress socketAddress = socket.getRemoteSocketAddress();
System.out.println("socketAddress实例打印没设置为null: " + socketAddress);
itemp = socket.getSendBufferSize();
System.out.println("tcp发送缓存区: " + itemp);
itemp = socket.getReceiveBufferSize();
System.out.println("tcp接收缓存区: " + itemp);
btemp = socket.isBound();
System.out.println("是否绑定网络地址" + btemp);
btemp = socket.isClosed();
System.out.println("是否以关闭" + btemp);
btemp = socket.isConnected();
System.out.println("是否在连接" + btemp);
btemp = socket.isInputShutdown();
System.out.println("是否关闭输入流" + btemp);
btemp = socket.isOutputShutdown();
System.out.println("是否关闭输出流" + btemp);
listen();
}
}