Java?NIO實(shí)現(xiàn)多人聊天室
本文實(shí)例為大家分享了Java NIO實(shí)現(xiàn)多人聊天室的具體代碼,供大家參考,具體內(nèi)容如下
1. 服務(wù)器端代碼
ChatServer類:
package nio.test.server;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Set;
public class ChatServer {
private static final int DEFAULT_PORT = 8888;
private static final String QUIT = "quit";
private static final int BUFFER = 1024;
private ServerSocketChannel serverSocketChannel; //服務(wù)器端用于處理IO的通道
private Selector selector;
private ByteBuffer byteBufferReader = ByteBuffer.allocate(BUFFER); //用來(lái)讀取消息
private ByteBuffer byteBufferWriter = ByteBuffer.allocate(BUFFER); //用來(lái)轉(zhuǎn)發(fā)消息時(shí)寫入其他通道的緩沖區(qū)
private Charset charset = Charset.forName("UTF-8"); //標(biāo)準(zhǔn)化編碼解碼
private int port;
public ChatServer(){
this(DEFAULT_PORT);
}
public ChatServer(int port){
this.port = port;
}
private void start(){
try {
serverSocketChannel = ServerSocketChannel.open(); //創(chuàng)建服務(wù)器套接字通道
serverSocketChannel.configureBlocking(false); //設(shè)置為非阻塞式調(diào)用
serverSocketChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open(); //打開選擇器
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("啟動(dòng)服務(wù)器,監(jiān)聽端口:" + port + "...");
while (true) {
selector.select();
//selectionKeys包含了select()接收到的所有事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for(SelectionKey key : selectionKeys){
//處理被觸發(fā)的事件
handles(key);
}
selectionKeys.clear(); //把集合清空
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close(selector);//啟到既關(guān)閉selector又關(guān)閉通道的作用
}
}
/**
* 處理被觸發(fā)的事件
* @param key 每當(dāng)通道被選擇器注冊(cè)時(shí),都會(huì)創(chuàng)建一個(gè)選擇鍵
* @throws IOException
*/
private void handles(SelectionKey key) throws IOException {
// 觸發(fā) ACCEPT事件 --- 和客戶端建立了連接
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println(getClientName(client) + "已連接");
}
// 觸發(fā) READ事件 --- 客戶端發(fā)送了消息給服務(wù)器端
else if(key.isReadable()){
SocketChannel client = (SocketChannel) key.channel();
String fwdMsg = receive(client); //讀取客戶端消息
if(fwdMsg.isEmpty()){ //客戶端異常
key.cancel(); //不再監(jiān)視這個(gè)通道上的read事件
selector.wakeup();
}else {
forwardMessage(client, fwdMsg); //轉(zhuǎn)發(fā)客戶端消息
// 檢查用戶是否退出
if(readyToQuit(fwdMsg)){
key.cancel();//解除監(jiān)聽
selector.wakeup();
System.out.println(getClientName(client) + "已斷開");
}
}
}
}
/**
* 用于轉(zhuǎn)發(fā)消息
* @param client
* @param fwdMsg
* @throws IOException
*/
private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException {
for(SelectionKey key : selector.keys()){
Channel connectedClient = key.channel();
if(connectedClient instanceof ServerSocketChannel) continue;
if(key.isValid() && !client.equals(connectedClient)) {
byteBufferWriter.clear();
byteBufferWriter.put(charset.encode((getClientName(client)) + ":" + fwdMsg));
byteBufferWriter.flip(); //寫轉(zhuǎn)讀
while(byteBufferWriter.hasRemaining()){
((SocketChannel)connectedClient).write(byteBufferWriter);
}
}
}
}
private String receive(SocketChannel client) throws IOException {
byteBufferReader.clear();
while(client.read(byteBufferReader) > 0);
byteBufferReader.flip();
return String.valueOf(charset.decode(byteBufferReader));
}
private String getClientName(SocketChannel client){
return "客戶端[" + client.socket().getPort() + "]";
}
private boolean readyToQuit(String msg){
return QUIT.equals(msg);
}
private void close(Closeable closeable){
if(closeable != null){
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ChatServer chatServer = new ChatServer(6666);
chatServer.start();
}
}
2. 客戶端代碼
ChatClient類:
package nio.test.client;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Set;
public class ChatClient {
private static final String DEFAULT_SERVER_HOST = "127.0.0.1";
private static final int DEFAULT_SERVER_PORT = 6666;
private static final String QUIT = "quit";
private static final int BUFFER = 1024;
private String host;
private int port;
private SocketChannel client;
private ByteBuffer byteBufferReader = ByteBuffer.allocate(BUFFER);
private ByteBuffer byteBufferWriter = ByteBuffer.allocate(BUFFER);
private Selector selector;
private Charset charset = Charset.forName("UTF-8");
public ChatClient(){
this(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
}
public ChatClient(String host, int port){
this.host = host;
this.port = port;
}
public boolean readyToQuit(String msg){
return QUIT.equals(msg);
}
private void close(Closeable closeable){
if(closeable != null){
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void start(){
try {
client = SocketChannel.open();
client.configureBlocking(false);
selector = Selector.open();
client.register(selector, SelectionKey.OP_CONNECT);
client.connect(new InetSocketAddress(host, port));
while(true){
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for(SelectionKey key : selectionKeys){
handles(key);
}
selectionKeys.clear();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClosedSelectorException e){
//用戶正常退出
}finally {
close(selector);
}
}
private void handles(SelectionKey key) throws IOException {
// CONNECT事件 連接就緒事件
if(key.isConnectable()){
SocketChannel client = (SocketChannel)key.channel();
if(client.isConnectionPending()){//連接處于就緒狀態(tài)
client.finishConnect();
// 處理用戶的輸入信息
new Thread(new UserInputHandler(this)).start();
}
client.register(selector, SelectionKey.OP_READ);
}
// READ事件 服務(wù)器轉(zhuǎn)發(fā)消息
else if(key.isReadable()){
SocketChannel client = (SocketChannel)key.channel();
String msg = receive(client);
if(msg.isEmpty()){
// 服務(wù)器出現(xiàn)異常
close(selector);
}else{
System.out.println(msg);
}
}
}
public void send(String msg) throws IOException {
if(msg.isEmpty()){
return ;
}else{
byteBufferWriter.clear();
byteBufferWriter.put(charset.encode(msg));
byteBufferWriter.flip();
while(byteBufferWriter.hasRemaining()){
client.write(byteBufferWriter);
}
//檢查用戶是否準(zhǔn)備退出
if(readyToQuit(msg)){
close(selector);
}
}
}
private String receive(SocketChannel client) throws IOException {
byteBufferReader.clear();
while(client.read(byteBufferReader) > 0);
byteBufferReader.flip();
return String.valueOf(charset.decode(byteBufferReader));
}
public static void main(String[] args) {
ChatClient chatClient = new ChatClient();
chatClient.start();
}
}
UserInputHandler類:
package nio.test.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class UserInputHandler implements Runnable{
private ChatClient chatclient;
public UserInputHandler(ChatClient chatClient){
this.chatclient = chatClient;
}
/**r
*
*/
@Override
public void run() {
try {
//等待用戶輸入的消息
BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(System.in)
);
while(true){
String input = consoleReader.readLine();
//向服務(wù)器發(fā)送消息
chatclient.send(input);
//檢查用戶是否準(zhǔn)備退出
if(chatclient.readyToQuit(input)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 執(zhí)行效果截圖




以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(3)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07
詳解Java多線程編程中LockSupport類的線程阻塞用法
LockSupport類提供了park()和unpark()兩個(gè)方法來(lái)實(shí)現(xiàn)線程的阻塞和喚醒,下面我們就來(lái)詳解Java多線程編程中LockSupport類的線程阻塞用法:2016-07-07
java8中的lambda表達(dá)式,看這篇絕對(duì)夠
這篇文章主要介紹了java8中的lambda表達(dá)式,看這篇絕對(duì)夠!具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
jtds1.1連接sqlserver2000測(cè)試示例
這篇文章主要介紹了jtds1.1連接sqlserver2000測(cè)試示例,需要的朋友可以參考下2014-02-02
Spring Boot 會(huì)員管理系統(tǒng)之處理文件上傳功能
Spring Boot會(huì)員管理系統(tǒng)的中,需要涉及到Spring框架,SpringMVC框架,Hibernate框架,thymeleaf模板引擎。這篇文章主要介紹了Spring Boot會(huì)員管理系統(tǒng)之處理文件上傳功能,需要的朋友可以參考下2018-03-03
Java時(shí)間類Date類和Calendar類的使用詳解
這篇文章主要介紹了Java時(shí)間類Date類和Calendar類的使用詳解,需要的朋友可以參考下2017-08-08

