Java NIO实现多人聊天室
作者:RivenDong 发布时间:2022-04-05 09:03:17
标签:Java,NIO,聊天室
本文实例为大家分享了Java NIO实现多人聊天室的具体代码,供大家参考,具体内容如下
1. 服务器端代码
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; //服务器端用于处理IO的通道
private Selector selector;
private ByteBuffer byteBufferReader = ByteBuffer.allocate(BUFFER); //用来读取消息
private ByteBuffer byteBufferWriter = ByteBuffer.allocate(BUFFER); //用来转发消息时写入其他通道的缓冲区
private Charset charset = Charset.forName("UTF-8"); //标准化编码解码
private int port;
public ChatServer(){
this(DEFAULT_PORT);
}
public ChatServer(int port){
this.port = port;
}
private void start(){
try {
serverSocketChannel = ServerSocketChannel.open(); //创建服务器套接字通道
serverSocketChannel.configureBlocking(false); //设置为非阻塞式调用
serverSocketChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open(); //打开选择器
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("启动服务器,监听端口:" + port + "...");
while (true) {
selector.select();
//selectionKeys包含了select()接收到的所有事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for(SelectionKey key : selectionKeys){
//处理被触发的事件
handles(key);
}
selectionKeys.clear(); //把集合清空
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close(selector);//启到既关闭selector又关闭通道的作用
}
}
/**
* 处理被触发的事件
* @param key 每当通道被选择器注册时,都会创建一个选择键
* @throws IOException
*/
private void handles(SelectionKey key) throws IOException {
// 触发 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) + "已连接");
}
// 触发 READ事件 --- 客户端发送了消息给服务器端
else if(key.isReadable()){
SocketChannel client = (SocketChannel) key.channel();
String fwdMsg = receive(client); //读取客户端消息
if(fwdMsg.isEmpty()){ //客户端异常
key.cancel(); //不再监视这个通道上的read事件
selector.wakeup();
}else {
forwardMessage(client, fwdMsg); //转发客户端消息
// 检查用户是否退出
if(readyToQuit(fwdMsg)){
key.cancel();//解除监听
selector.wakeup();
System.out.println(getClientName(client) + "已断开");
}
}
}
}
/**
* 用于转发消息
* @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(); //写转读
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()){//连接处于就绪状态
client.finishConnect();
// 处理用户的输入信息
new Thread(new UserInputHandler(this)).start();
}
client.register(selector, SelectionKey.OP_READ);
}
// READ事件 服务器转发消息
else if(key.isReadable()){
SocketChannel client = (SocketChannel)key.channel();
String msg = receive(client);
if(msg.isEmpty()){
// 服务器出现异常
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);
}
//检查用户是否准备退出
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();
//向服务器发送消息
chatclient.send(input);
//检查用户是否准备退出
if(chatclient.readyToQuit(input)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 执行效果截图
来源:https://blog.csdn.net/RivenDong/article/details/103327130


猜你喜欢
- 先给大家这是下效果图:谷歌提供的v4包,ViewPager在布局文件中,先添加<android.support.v4.view.Vie
- 微信公众号开发一般是针对企业和组织的,个人一般只能申请订阅号,并且调用的接口有限,下面我们就来简单的描述下接入公众号的步骤:1、首先你需要一
- 1、特效按钮的进展 之前的思路:css设置div的样式,在js中实现div对事件的响应,并改变div的样式,以实现动画效果。 1:以动画的形
- 今天给大家介绍一下Java实现钢琴的小程序,程序虽小,功能挺多,支持循环播放,录音等功能,首先简单介绍下源码结构:先看看钢琴界面实现,添加相
- 文件切割和文件合并这个问题困扰了我有一段时间了(超过一天没做粗来)。找了好多博客,本来想转载一个来的 结果找不到了。很无奈。只好自己贴代码上
- Android PopWindow 设置背景亮度的实例设置背景 /** * 设置添加屏幕的背景透明度 * @param bgAl
- 本文实例讲述了C#使用iTextSharp封装的PDF文件操作类。分享给大家供大家参考。具体分析如下:这个C#代码主要讲iTextSharp
- 本文实例为大家分享了java常用工具类的具体代码,供大家参考,具体内容如下Random随机数工具类package com.jarvis.ba
- 本文实例为大家分享了C#实现网页画图的具体代码,供大家参考,具体内容如下代码贴着保存下using System;using System.C
- //十进制转二进制 Console.WriteLine(Convert.ToString(69, 2)); //十进制转八进制 Consol
- 引言最近的项目需求中有使用到后端发送http请求,在网上寻找资料后发现可以使用spring自带的RestTemplate类实现,故作此记录项
- Spring发布了一个新工具Spring Native Beta,用于将现有的Spring Boot应用程序(用Java或Kotlin编写)
- 首先假设你的应用程序配置文件如下:<?xml version="1.0" encoding="utf-8
- using System;using System.Collections.Generic;using System.Data;using
- 1、注解是什么Java 注解用于为 Java 代码提供元数据,看完这句话也许你还是一脸懵逼,用人话说就是注解不直接影响你的代码执行,仅提供信
- Android 设置颜色的方法总结Android中有几种设置界面背景及文字颜色的方法,下面由浅入深分别介绍Android中设置颜色的几种方法
- Spring Boot CLI是Spring Boot项目提供的一个用于快速运行Spring Boot应用的命令行工具,通过结合Groovy
- 1. 测试用例我们以sentinel-demo中的sentinel-annotation-spring-aop为例,分析sentinel的源
- 一、前言做新应用就是这样,会遇到各种问题,昨天刚解决了加载某一个类时候抛出了 class is not visible from class
- Java中的set是无序的,但是是不可重复的HashSet底层是哈希表,通过调用hashcode和equals方法实现去重当我们HashSe