java基于NIO實(shí)現(xiàn)群聊模式
本文實(shí)例為大家分享了java基于NIO實(shí)現(xiàn)群聊模式的具體代碼,供大家參考,具體內(nèi)容如下
Client
package com.qst.chat;
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.Scanner;
public class GroupChatClient {
private final int PORT = 9999;
private final String HOST = "localhost";
private SocketChannel channel;
private static Selector selector;
private String name;
public GroupChatClient() throws IOException {
selector = Selector.open();
// 連接服務(wù)器
channel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
// 設(shè)置非阻塞
channel.configureBlocking(false);
// 將channel 注冊(cè)到selector
channel.register(selector, SelectionKey.OP_READ);
name = channel.getLocalAddress().toString().substring(1);
System.out.println(name + "is ok ....");
}
// 向服務(wù)器發(fā)送消息
public void sendTO(String msg) {
ByteBuffer buffer = ByteBuffer.wrap((name+":"+msg).getBytes());
try {
channel.write(buffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 讀取從服務(wù)器端回復(fù)的消息
public static void getInfo() {
try {
if(selector.select() >0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()) {
SelectionKey key = iterator.next();
if(key.isReadable()) {
// 得到通道
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len;
// 把讀到的緩沖區(qū)的數(shù)據(jù)轉(zhuǎn)成字符串
while((len = sc.read(buffer)) > 0) {
System.out.println(new String(buffer.array()));
}
}
}
// 刪除當(dāng)前的selectionKey, 防止重復(fù)操作
iterator.remove();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
GroupChatClient client = new GroupChatClient();
new Thread() {
public void run() {
while(true)
{
try {
Thread.sleep(3000);
GroupChatClient.getInfo();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}.start();
Scanner sc = new Scanner(System.in);
// while(true) {
// String name = sc.nextLine();
// client.sendTO(name);
// }
while(sc.hasNextLine()) {
String s = sc.nextLine();
client.sendTO(s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Server端
package com.qst.chat;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.time.chrono.IsoChronology;
import java.util.Iterator;
import com.sun.accessibility.internal.resources.accessibility;
import sun.print.resources.serviceui;
public class GroupChatServer {
private static ServerSocketChannel socketChannel;
private static Socket socket;
private static Selector selector;
private static SocketChannel accept;
public GroupChatServer() throws IOException {
socketChannel = ServerSocketChannel.open();
selector = Selector.open();
// 綁定端口
socketChannel.socket().bind(new InetSocketAddress(9999));
// 設(shè)置非阻塞模式
socketChannel.configureBlocking(false);
// 將該通道 注冊(cè)到selector
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
// 監(jiān)聽
public static void listen() {
System.out.println("監(jiān)聽線程: " + Thread.currentThread().getName());
try {
while (selector.select() > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
if (iterator.hasNext()) {
// 遍歷得到selectionKey 集合
SelectionKey next = iterator.next();
if (next.isAcceptable()) {
next.channel();
// socketChannel = (ServerSocketChannel) next.channel();
SocketChannel accept = socketChannel.accept();
accept.configureBlocking(false);
accept.register(selector, SelectionKey.OP_READ);
System.out.println(accept.getRemoteAddress()+" 上線 了。。。");
}
if (next.isReadable()) {
readDate(next);
}
// 移除當(dāng)前的next,防止重復(fù)處理
iterator.remove();
// System.out.println("未發(fā)現(xiàn)");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 讀取客戶端消息
public static void readDate(SelectionKey key) {
try {
accept = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = accept.read(buffer);
if (len > 0) {
buffer.flip();
String msg = new String(buffer.array());
System.out.println("user = " + msg);
// 向其它的客戶端轉(zhuǎn)發(fā)消息(去掉自己)
sendToAll(msg, accept);
buffer.clear();
}
} catch (IOException e) {
// TODO Auto-generated catch block
try {
String msg = accept.getRemoteAddress().toString();
// 取消注冊(cè)
key.cancel();
// 關(guān)閉通道
accept.close();
System.out.println(msg + "離線了");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// e.printStackTrace();
} finally {
// TODO: handle finally clause
}
}
public static void sendToAll(String msg, SocketChannel ssc) {
for (SelectionKey ss : selector.keys()) {
// 通過 key 取出對(duì)應(yīng)的 SocketChannel
SelectableChannel channel = ss.channel();
// 排除自己
if (channel instanceof SocketChannel && channel != ssc) {
// 轉(zhuǎn)型
SocketChannel sh = (SocketChannel) channel;
// 轉(zhuǎn)存到buffer
ByteBuffer wrap = ByteBuffer.wrap(msg.getBytes());
try {
// 寫入通道
sh.write(wrap);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
GroupChatServer server = new GroupChatServer();
GroupChatServer.listen();
}
}
key.isAcceptable()進(jìn)行接入 操作的時(shí)候, 獲取通道有兩種方式
1、 通過selector獲取 (Selector key) socketChannel = (ServerSocketChannel) key.channel();
建立連接 socketChannel .accept();
2、定義一個(gè)全局變量
在進(jìn)行初始化的時(shí)候,存儲(chǔ)(socketChannel = ServerSocketChannel.open();)
建立連接 socketChannel .accept();
key.isReadable() 當(dāng)進(jìn)行到讀入操作的時(shí)候( ) SelectionKey key accept = (SocketChannel) key.channel();
演示
服務(wù)器啟動(dòng),客戶端啟動(dòng)


客戶端發(fā)送消息

啟動(dòng)第二個(gè)客戶端


兩個(gè)客戶端相互通信



離線信息顯示

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JAVA實(shí)現(xiàn)Date日期加一天具體方法
這篇文章主要給大家介紹了關(guān)于JAVA實(shí)現(xiàn)Date日期加一天的相關(guān)資料,因?yàn)樵陧?xiàng)目中遇到了需要將日期進(jìn)行加減一些天數(shù)的操作,文中給出了簡(jiǎn)單的代碼示例,需要的朋友可以參考下2023-07-07
SpringBoot四大神器之Actuator的使用小結(jié)
這篇文章主要介紹了SpringBoot四大神器之Actuator的使用小結(jié),詳細(xì)的介紹了Actuator的使用和端點(diǎn)的使用,有興趣的可以了解一下2017-11-11
Kafka?日志存儲(chǔ)實(shí)現(xiàn)過程
這篇文章主要為大家介紹了Kafka?日志存儲(chǔ)的實(shí)現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
詳解Spring中singleton?bean如何同時(shí)服務(wù)多個(gè)請(qǐng)求
這篇文章主要介紹了詳解Spring中singleton?bean如何同時(shí)服務(wù)多個(gè)請(qǐng)求2023-02-02
Springboot整合實(shí)現(xiàn)郵件發(fā)送的原理詳解
SpringBoot集成郵件服務(wù)非常簡(jiǎn)單,通過簡(jiǎn)單的學(xué)習(xí)即可快速掌握郵件業(yè)務(wù)類的核心邏輯和企業(yè)郵件的日常服務(wù),本文給大家分享Springboot整合實(shí)現(xiàn)郵件發(fā)送的原理,一起看看吧2021-06-06
Java手機(jī)號(hào)碼工具類示例詳解(判斷運(yùn)營(yíng)商、獲取歸屬地)
這篇文章主要介紹了Java手機(jī)號(hào)碼工具類示例詳解,通過手機(jī)號(hào)碼來判斷運(yùn)營(yíng)商獲取歸屬地,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
詳解Java去除json數(shù)據(jù)中的null空值問題
這篇文章主要介紹了詳解Java去除json數(shù)據(jù)中的null空值問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
SpringBoot+Redis執(zhí)行l(wèi)ua腳本的方法步驟
這篇文章主要介紹了SpringBoot+Redis執(zhí)行l(wèi)ua腳本的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11

