Java?NIO實現(xiàn)聊天室功能
更新時間:2021年11月24日 09:13:22 作者:歲月如歌似夢
這篇文章主要為大家詳細介紹了Java?NIO實現(xiàn)聊天室功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
本文實例為大家分享了Java NIO實現(xiàn)聊天室功能的具體代碼,供大家參考,具體內(nèi)容如下
代碼里面已經(jīng)包含了必要的注釋,這里不詳述了。實現(xiàn)了基本的聊天室功能。
常量類:
public class Constant {
public static final int serverPort = 44444;
}
服務端:
package server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
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.Iterator;
import java.util.Set;
import constant.Constant;
public class SocketServer {
private Charset charset = Charset.forName("UTF-8");
private ServerSocketChannel serverSocketChannel;
private Selector serverSocketSelector;
private SelectionKey serverRegisterKey;
private ByteBuffer buffer = ByteBuffer.allocate(1024);
public static void main(String[] args) throws IOException {
new SocketServer().openServer(new InetSocketAddress(Constant.serverPort));
}
public void openServer(SocketAddress address) throws IOException {
init(address);
handle();
}
private void init(SocketAddress address) throws IOException {
serverSocketSelector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverRegisterKey = serverSocketChannel.register(serverSocketSelector, SelectionKey.OP_ACCEPT);
serverSocketChannel.socket().bind(address);
}
private void handle() throws IOException {
System.out.println("服務端open");
while (serverSocketSelector.select() > 0) {
Iterator<SelectionKey> iterator = serverSocketSelector.selectedKeys().iterator();
// 為什么這里要用迭代器,而不用增強for循環(huán)之類的呢?是因為這里獲得一個key之后,要對其進行移除,避免二次處理,造成影響
while (iterator.hasNext()) {
dispatch(iterator.next());
iterator.remove();
}
}
}
private void dispatch(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
readMessage(key);
} else if (key.isValid() && key.isWritable()) {
writeMessage(key);
}
}
private void accept(SelectionKey key) throws IOException, ClosedChannelException {
// 主要的是,接收事件是發(fā)生在服務器這邊的,所以這邊的通道要強轉(zhuǎn)為ServerSocketChannel
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
// 同時再給該通道注冊選擇器,監(jiān)聽的內(nèi)容的讀取
client.register(serverSocketSelector, SelectionKey.OP_READ);
}
private void readMessage(SelectionKey key) throws IOException {
SocketChannel client = (SocketChannel) key.channel();
client.read(buffer);
// 調(diào)整為讀取模式
buffer.flip();
String content = charset.decode(buffer).toString();
// 壓縮空間,即拋棄已經(jīng)讀取的內(nèi)容(實際上還在里面,只是處于等待被覆蓋狀態(tài))
buffer.compact();
// 這里可以根據(jù)業(yè)務邏輯,設置不設置都可以,但是這里想接受到消息后立馬回復一條消息,所以設置下一次感興趣的(監(jiān)聽)事件為寫
key.interestOps(SelectionKey.OP_WRITE);
// 設置系統(tǒng)回復信息
key.attach("系統(tǒng)已經(jīng)收到你的消息\n");
// 開始廣播這個客戶端的內(nèi)容到其他客戶端
broadcast(key, content);
}
private void broadcast(SelectionKey self, String content) throws IOException {
Set<SelectionKey> selectedKeys = self.selector().keys();
for (SelectionKey key : selectedKeys) {
// 不能發(fā)送給自己,也不要服務器自己本身對這個有反應
if (key != self && key != serverRegisterKey) {
String oldMessage = (String) key.attach(null);
// 如果有舊消息的話,在下一次發(fā)送時,連同舊消息一起發(fā)送
key.attach(oldMessage != null ? oldMessage + content : content);
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
}
}
}
private void writeMessage(SelectionKey key) throws IOException {
SocketChannel client = (SocketChannel) key.channel();
// 獲取發(fā)給這個客戶端的消息,并清空消息
client.write(charset.encode((String) key.attach(null)));
key.interestOps(SelectionKey.OP_READ);
}
}
客戶端(包含了Socket版本和SocketChannel版本):
package client;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
import constant.Constant;
public class SocketClient {
public static void main(String[] args) throws IOException {
nioVersion();
// ioVersion();
}
private static void ioVersion() throws UnknownHostException, IOException {
System.out.println("客戶端");
final Socket socket = new Socket();
socket.connect(new InetSocketAddress(Constant.serverPort));
new Thread() {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String line = scanner.nextLine();
try {
socket.getOutputStream().write((line + "\n").getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
scanner.close();
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
new Thread() {
@Override
public void run() {
try {
Scanner scanner = new Scanner(socket.getInputStream(), "utf-8");
while (scanner.hasNext()) {
String line = scanner.nextLine();
System.out.println("收到消息:" + line);
}
scanner.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
private static void nioVersion() throws IOException {
Charset charset = Charset.forName("UTF-8");
System.out.println("客戶端");
SocketChannel socketChannel = SocketChannel.open();
// 設置為非阻塞模式
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(Constant.serverPort));
while (true) {
if (socketChannel.finishConnect()) {
new Thread() {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String input = scanner.nextLine();
try {
socketChannel.write(charset.encode(input));
} catch (IOException e) {
e.printStackTrace();
}
}
scanner.close();
}
}.start();
new Thread() {
ByteBuffer dst = ByteBuffer.allocate(1024);
@Override
public void run() {
while (true) {
try {
int len = socketChannel.read(dst);
if (len > 0) {
dst.flip();
System.out.println("收到消息:" + charset.decode(dst));
dst.compact();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
return;
}
}
}
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Springboot定時任務Scheduled重復執(zhí)行操作
這篇文章主要介紹了Springboot定時任務Scheduled重復執(zhí)行操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
解決springboot報錯找不到自動注入的service問題
這篇文章主要介紹了解決springboot報錯找不到自動注入的service問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Java EE項目中的異常處理總結(jié)(一篇不得不看的文章)
什么是異常?運行時發(fā)生的可被捕獲和處理的錯誤。這篇文章主要介紹了Java EE項目中的異常處理總結(jié),有需要的可以了解一下。2016-11-11
mybatis簡介與配置_動力節(jié)點Java學院整理
這篇文章主要介紹了mybatis簡介與配置,介紹了MyBatis+Spring+MySql簡單配置,有興趣的可以了解一下2017-09-09

