Java?NIO實(shí)現(xiàn)聊天室功能
本文實(shí)例為大家分享了Java NIO實(shí)現(xiàn)聊天室功能的具體代碼,供大家參考,具體內(nèi)容如下
代碼里面已經(jīng)包含了必要的注釋,這里不詳述了。實(shí)現(xiàn)了基本的聊天室功能。
常量類:
public class Constant {
public static final int serverPort = 44444;
}
服務(wù)端:
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("服務(wù)端open");
while (serverSocketSelector.select() > 0) {
Iterator<SelectionKey> iterator = serverSocketSelector.selectedKeys().iterator();
// 為什么這里要用迭代器,而不用增強(qiáng)for循環(huán)之類的呢?是因?yàn)檫@里獲得一個(gè)key之后,要對(duì)其進(jìn)行移除,避免二次處理,造成影響
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ā)生在服務(wù)器這邊的,所以這邊的通道要強(qiáng)轉(zhuǎn)為ServerSocketChannel
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
// 同時(shí)再給該通道注冊(cè)選擇器,監(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)容(實(shí)際上還在里面,只是處于等待被覆蓋狀態(tài))
buffer.compact();
// 這里可以根據(jù)業(yè)務(wù)邏輯,設(shè)置不設(shè)置都可以,但是這里想接受到消息后立馬回復(fù)一條消息,所以設(shè)置下一次感興趣的(監(jiān)聽)事件為寫
key.interestOps(SelectionKey.OP_WRITE);
// 設(shè)置系統(tǒng)回復(fù)信息
key.attach("系統(tǒng)已經(jīng)收到你的消息\n");
// 開始廣播這個(gè)客戶端的內(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ā)送給自己,也不要服務(wù)器自己本身對(duì)這個(gè)有反應(yīng)
if (key != self && key != serverRegisterKey) {
String oldMessage = (String) key.attach(null);
// 如果有舊消息的話,在下一次發(fā)送時(shí),連同舊消息一起發(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ā)給這個(gè)客戶端的消息,并清空消息
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();
// 設(shè)置為非阻塞模式
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;
}
}
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java?NIO實(shí)現(xiàn)多人聊天室
- Java基于NIO實(shí)現(xiàn)聊天室功能
- Java實(shí)現(xiàn)NIO聊天室的示例代碼(群聊+私聊)
- java基于netty NIO的簡(jiǎn)單聊天室的實(shí)現(xiàn)
- Java NIO實(shí)戰(zhàn)之聊天室功能詳解
- Java NIO Selector用法詳解【含多人聊天室實(shí)例】
- 使用Java和WebSocket實(shí)現(xiàn)網(wǎng)頁(yè)聊天室實(shí)例代碼
- java聊天室的實(shí)現(xiàn)代碼
- Java基于socket實(shí)現(xiàn)簡(jiǎn)易聊天室實(shí)例
- JAVA NIO實(shí)現(xiàn)簡(jiǎn)單聊天室功能
相關(guān)文章
Springboot定時(shí)任務(wù)Scheduled重復(fù)執(zhí)行操作
這篇文章主要介紹了Springboot定時(shí)任務(wù)Scheduled重復(fù)執(zhí)行操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
解決springboot報(bào)錯(cuò)找不到自動(dòng)注入的service問題
這篇文章主要介紹了解決springboot報(bào)錯(cuò)找不到自動(dòng)注入的service問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
詳解如何使用MyBatis簡(jiǎn)化JDBC開發(fā)
JavaEE?企業(yè)級(jí)?Java?項(xiàng)目中的經(jīng)典三層架構(gòu)為表現(xiàn)層,業(yè)務(wù)層和持久層.MyBatis?對(duì)?JDBC?代碼進(jìn)行了封裝,作為一款優(yōu)秀的持久層框架,專門用于簡(jiǎn)化JDBC開發(fā).本文主要介紹一下如何使用MyBatis簡(jiǎn)化JDBC開發(fā),需要的可以參考一下2023-01-01
Java EE項(xiàng)目中的異常處理總結(jié)(一篇不得不看的文章)
什么是異常?運(yùn)行時(shí)發(fā)生的可被捕獲和處理的錯(cuò)誤。這篇文章主要介紹了Java EE項(xiàng)目中的異常處理總結(jié),有需要的可以了解一下。2016-11-11
mybatis簡(jiǎn)介與配置_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了mybatis簡(jiǎn)介與配置,介紹了MyBatis+Spring+MySql簡(jiǎn)單配置,有興趣的可以了解一下2017-09-09

