NIOServer
package com.ye.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.Channel;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author yeWanQing
* @since 2019/3/27
*/
public class NIOServer {
//selector
private static Selector selector;
//字元編碼
private Charset charset = Charset.forName("UTF-8");
//內容協議
private static String USER_CONTENT_SPLIT = "#@#";
//使用者存在的提示
private static String USER_EXIST = "系統提示:該暱稱已經存在,請換一個暱稱";
//用來記錄線上人數,以及暱稱
private static Set<String> users = new HashSet<>();
public NIOServer(InetSocketAddress address){
try {
//獲取selector
selector = Selector.open();
//獲取ServerSocketChannel
ServerSocketChannel chanel = ServerSocketChannel.open();
//繫結地址和埠
chanel.bind(address);
//設定為非阻塞的,jdk為了相容,預設為阻塞的
chanel.configureBlocking(false);
//將ServerSocketChannel註冊到selector上,感興趣的事件為接受連線
chanel.register(selector, SelectionKey.OP_ACCEPT);
} catch (Exception e) {
e.printStackTrace();
}
}
public void listen(){
try {
while (true){
//獲取事件,這一步是阻塞的,所以用while(true)沒有關係,返回的是基於上一次select之後的事件
int select = selector.select();
if(select == 0){
continue;
}
//返回就緒事件列表 SelectionKey包含事件資訊
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//處理事件業務
processKey(selectionKey);
//移除事件 因為selector不會自動移除,如果不收到移除,下次selectedKeys()還會繼續存在該selectionKey
iterator.remove();
}
}
}catch (Exception e){
}
}
private void processKey(SelectionKey selectionKey) throws IOException {
if(selectionKey.isAcceptable()){//事件型別為接受連線
//獲取對應的ServerSocketChannel
ServerSocketChannel server = (ServerSocketChannel)selectionKey.channel();
//為每一個連線建立一個SocketChannel,這個SocketChannel用來讀寫資料
SocketChannel client = server.accept();
//設定為非阻塞
client.configureBlocking(false);
//註冊selector 感興趣事件為讀資料,意思就是客戶端傳送寫資料時,selector就可以接收並讀取資料
client.register(selector, SelectionKey.OP_READ);
//繼續可以接收連線事件
selectionKey.interestOps(SelectionKey.OP_ACCEPT);
}else if(selectionKey.isReadable()){//事件型別為讀取資料
//得到SocketChannel
SocketChannel client = (SocketChannel)selectionKey.channel();
//定義緩衝區
ByteBuffer buffer = ByteBuffer.allocate(1024);
StringBuilder content = new StringBuilder();
while (client.read(buffer) > 0){
//buffer由寫模式變成讀模式,因為client.read(buffer)是從管道寫資料到緩衝區中
buffer.flip();
content.append(charset.decode(buffer));
}
//清空緩衝區
buffer.clear();
//繼續註冊讀事件型別
selectionKey.interestOps(SelectionKey.OP_READ);
//業務處理
if(content.length() > 0){
String[] arrayContent = content.toString().split(USER_CONTENT_SPLIT);
if(arrayContent != null && arrayContent.length == 1) {
String nickName = arrayContent[0];
if(users.contains(nickName)) {
client.write(charset.encode(USER_EXIST));
} else {
users.add(nickName);
int onlineCount = onlineCount();
String message = "歡迎 " + nickName + " 進入聊天室! 當前線上人數:" + onlineCount;
broadCast(null, message);
}
}
else if(arrayContent != null && arrayContent.length > 1) {
String nickName = arrayContent[0];
String message = arrayContent[1];
message = nickName + " 說 " + message;
if(users.contains(nickName)) {
//不回發給傳送此內容的客戶端
broadCast(client, message);
}
}
}
}
}
public void broadCast(SocketChannel client, String content) throws IOException {
//廣播資料到所有的SocketChannel中
for(SelectionKey key : selector.keys()) {
java.nio.channels.Channel targetChannel = key.channel();
//如果client不為空,不回發給傳送此內容的客戶端
if(targetChannel instanceof SocketChannel && targetChannel != client) {
SocketChannel target = (SocketChannel)targetChannel;
target.write(charset.encode(content));
}
}
}
public int onlineCount() {
int res = 0;
for(SelectionKey key : selector.keys()){
Channel target = key.channel();
if(target instanceof SocketChannel){
res++;
}
}
return res;
}
public static void main(String[] args) {
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8081);
new NIOServer(address).listen();
}
}
複製程式碼
NIOClient
package com.ye.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.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
/**
* @author yeWanQing
* @since 2019/3/27
*/
public class NIOClient {
private Selector selector = null;
private SocketChannel socketChannel = null;
private String nickName = "";
private Charset charset = Charset.forName("UTF-8");
private static String USER_EXIST = "系統提示:該暱稱已經存在,請換一個暱稱";
private static String USER_CONTENT_SPLIT = "#@#";
public NIOClient(InetSocketAddress serverAddress){
try {
//獲取selector
selector = Selector.open();
//獲取socketChannel
socketChannel = SocketChannel.open();
//連線到服務
socketChannel.connect(serverAddress);
//設定為非阻塞
socketChannel.configureBlocking(false);
//向selector註冊感興趣事件 讀資料型別
socketChannel.register(selector, SelectionKey.OP_READ);
} catch (Exception e) {
e.printStackTrace();
}
}
public void session(){
//開闢一個新執行緒從伺服器端讀資料
new Reader().start();
//開闢一個新執行緒往伺服器端寫資料
new Writer().start();
}
private class Reader extends Thread {
public void run() {
try {
//輪詢
while(true) {
int readyChannels = selector.select();
if(readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys(); //可以通過這個方法,知道可用通道的集合
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
process(key);
}
}
}
catch (IOException io){
}
}
private void process(SelectionKey key) throws IOException {
if(key.isReadable()){
SocketChannel sc = (SocketChannel)key.channel();
ByteBuffer buff = ByteBuffer.allocate(1024);
String content = "";
while(sc.read(buff) > 0)
{
buff.flip();
content += charset.decode(buff);
}
//若系統傳送通知名字已經存在,則需要換個暱稱
if(USER_EXIST.equals(content)) {
nickName = "";
}
System.out.println(content);
key.interestOps(SelectionKey.OP_READ);
}
}
}
private class Writer extends Thread{
@Override
public void run() {
try{
//在主執行緒中 從鍵盤讀取資料輸入到伺服器端
Scanner scan = new Scanner(System.in);
while(scan.hasNextLine()){
String line = scan.nextLine();
if("".equals(line)) continue; //不允許發空訊息
if("".equals(nickName)) {
nickName = line;
line = nickName + USER_CONTENT_SPLIT;
} else {
line = nickName + USER_CONTENT_SPLIT + line;
}
socketChannel.write(charset.encode(line));//client既能寫也能讀,這邊是寫
}
scan.close();
}catch(Exception e){
}
}
}
public static void main(String[] args) {
InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 8081);
new NIOClient(serverAddress).session();
}
}
複製程式碼