先啓動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();
}
}