实现的功能:

  运行一个服务端,运行多个客户端。在客户端1,发送消息,其余客户端都能收到客户端1发送的消息。

重点:

  1、ByteBuffer在使用时,注意flip()方法的调用,否则读取不到消息。

服务端

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
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.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set; public class NioServer {
public static void main(String[] args) throws Exception{
//创建服务端
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//绑定端口
serverSocketChannel.bind(new InetSocketAddress("localhost",12345));
//创建selector
Selector selector = Selector.open();
//在selector中注册服务端的链接事件(注1)
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// //用于存放客户端的链接,用远端的端口,作为唯一标识(由于是本机开启多个客户端进行测试,所以不存在端口冲突问题)
// Map<Integer,SocketChannel> clients = new HashMap<>();
List<SocketChannel> clients = new ArrayList<>(); while (true){
//阻塞等待事件的到来
selector.select();
//获取被触发的事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
//遍历触发的事件
while (iterator.hasNext()){
try {
//获取事件
SelectionKey event = iterator.next();
//是否可以链接
if(event.isAcceptable()){
//为什么需要强转? 因为在(注1)中,我们注册的是 ServerSocketChannel ,所有需要强转回来。(注2)
ServerSocketChannel ssc = (ServerSocketChannel) event.channel();
//获取到链接的socketchannel
SocketChannel socketChannel = ssc.accept();
socketChannel.configureBlocking(false);
//将获取到的链接,注册读事件到selector中,
socketChannel.register(selector,SelectionKey.OP_READ);
// //将获取到的客户端,保存起来,用于跟其它客户端进行通信,由于不涉及线程问题,所以使用map足已
// clients.put(((InetSocketAddress)socketChannel.getRemoteAddress()).getPort(),socketChannel);
clients.add(socketChannel);
}else if(event.isReadable()){ //是否可以读取
//同理(注2)
SocketChannel socketChannel = (SocketChannel) event.channel();
//创建socketChannel需要的buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
String receiveMessage = "";
while (true){
try{
//重置buffer
byteBuffer.clear();
int read = socketChannel.read(byteBuffer);
if(read <= 0 ){
//当读取到末尾时,跳出循环
break;
}
receiveMessage += new String(byteBuffer.array(), Charset.forName("UTF-8"));
}catch (Exception e){
e.printStackTrace();
break;
}
}
System.out.println("收到的消息为:"+((InetSocketAddress)socketChannel.getRemoteAddress()).getPort()+"---"+receiveMessage);
//拼装需要发送的消息
final ByteBuffer otherbf = ByteBuffer.allocate(receiveMessage.length()+10);
otherbf.put((((InetSocketAddress)socketChannel.getRemoteAddress()).getPort()+":"+receiveMessage).getBytes());
System.out.println(new String(otherbf.array()));
//遍历客户端,发送消息
clients.stream().forEach(sc -> {
try {
if(((InetSocketAddress)socketChannel.getRemoteAddress()).getPort() ==
((InetSocketAddress)sc.getRemoteAddress()).getPort()){
//消息不发给自己
}else{
otherbf.flip();
sc.write(otherbf);
}
}catch (Exception e){
e.printStackTrace();
}
});
}
}catch (Exception e){
//添加try是为了程序的健壮
e.printStackTrace();
}finally {
//删除已经处理了的事件
iterator.remove();
}
}
}
}
}

  

客户端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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.Set; public class NioClient { public static void main(String[] args) throws Exception{ //打开socketChannel
SocketChannel socketChannel = SocketChannel.open();
//设置为非阻塞
socketChannel.configureBlocking(false);
//链接到服务器
socketChannel.connect(new InetSocketAddress("localhost",12345));
//创建Selector
Selector selector = Selector.open();
//向Selector注册连接事件
socketChannel.register(selector, SelectionKey.OP_CONNECT);
//阻塞等待事件触发
selector.select();
//获取连接事件key
Set<SelectionKey> connectEventKey = selector.selectedKeys();
//获取触发的连接事件
SelectionKey connectEvent = connectEventKey.iterator().next();
//删除已经处理了的事件
selector.selectedKeys().clear();
//转换为注册时的channel
SocketChannel eventSocketChannel = (SocketChannel) connectEvent.channel();
//向selector注册读事件
eventSocketChannel.register(selector,SelectionKey.OP_READ);
new Thread(){
@Override
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
ByteBuffer inputBuffer = ByteBuffer.allocate(1024); //长度需要重新考量
try{
if(socketChannel.finishConnect()){
System.out.println("完成连接。");
}
while (true){
String s = reader.readLine();
inputBuffer.clear();
inputBuffer.put(s.getBytes());
inputBuffer.flip();
socketChannel.write(inputBuffer);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
//没有和连接事件合并到一个while里面,是因为压根就不会有两次连接,所以我将连接事件单独出来
while (true){
//阻塞等待事件触发,这次是触发读事件
selector.select();
Set<SelectionKey> readEventKey = selector.selectedKeys();
Iterator<SelectionKey> readIterator = readEventKey.iterator();
SocketChannel readSocketChannel = (SocketChannel) readIterator.next().channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(256);
String content = "";
while (true){
byteBuffer.clear();
int read = readSocketChannel.read(byteBuffer);
if(read <= 0){
break;
}
content += new String(byteBuffer.array());
}
System.out.println("收到的消息为:"+content);
readIterator.remove();
}
}
}

  

简单即时通讯、聊天室--java NIO版本的更多相关文章

  1. 黑科技!仅需 3 行代码,就能将 Gitter 集成到个人网站中,实现一个 IM 即时通讯聊天室功能?

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  2. C# SignalR 即时通讯 聊天室

    一.SignalR简介 SignalR:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据.实现实时服务器与客户端通信.是一个开源.NET 库生成需要实时用 ...

  3. 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

    基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...

  4. Openfire XMPP Smack RTC IM 即时通讯 聊天 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  5. 使用Servlet和JSP实现一个简单的Web聊天室系统

    1 问题描述                                                利用Java EE相关技术实现一个简单的Web聊天室系统,具体要求如下. (1)编写一个登录 ...

  6. 分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室

    实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSoc ...

  7. 用Java构建一个简单的WebSocket聊天室

    前言 首先对于一个简单的聊天室,大家应该都有一定的概念了,这里我们省略用户模块的讲解,而是单纯的先说说聊天室的几个功能:自我对话.好友交流.群聊.离线消息等. 今天我们要做的demo就能帮我们做到这一 ...

  8. 简单聊天室(java版)

    这是本人从其他地方学习到的关于聊天室的一个模本,我从中截取了一部分关于客户端和服务端通信的Socket的内容.希望对大家对socket有个了解,我写的这些代码可以实现两人或多人在多台电脑上实现简单的对 ...

  9. java Activiti6 工作流引擎 websocket 即时聊天 SSM源码 支持手机即时通讯聊天

    即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...

随机推荐

  1. 编程基础-c语言中指针、sizeof用法总结

    1.指针 学习 C 语言的指针既简单又有趣.通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的.所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的. ...

  2. linux中截取字段与#、$区别

    1.Linux shell 截取字符变量的前8位 实现方法有如下几种: expr substr “$a” 1 8 echo $a|awk ‘{print substr(,1,8)}’ echo $a| ...

  3. 高性能计算 —— 中国金融服务业创新发展的助推剂 & 微软

    “高性能计算 —— 中国金融服务业创新发展的助推剂“六大盘点 - 微软 - 博客园https://www.cnblogs.com/stbchina/archive/2011/12/02/HPC-in- ...

  4. Java基础 do-while 简单示例

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...

  5. 【转载】 clusterdata-2011-2 谷歌集群数据分析(二)--task_usage

    原文地址: https://blog.csdn.net/yangss123/article/details/78298749 由于原文声明其原创文章不得允许不可转载,故这里没有转载其正文内容. --- ...

  6. visual studio code 调试ROS的插件

    ctrl+p搜索: ext install ros https://marketplace.visualstudio.com/items?itemName=ajshort.ros 进行安装 其他可以调 ...

  7. QML注意color小写

    1.用的时候大小写一样的 如: color="#3D3D3D" 和 color="#3d3d3d"都是同一个颜色 2.判断的时候,QML默认是小写 如:if(c ...

  8. Qt编写气体安全管理系统11-数据打印

    一.前言 在各种软件系统中,数据打印也是常用的功能之一,一般来说会对查询的数据结果导出到excel,还会对查询的数据结果直接打印,在Qt中提供了打印机类QPrinter,在printsupport组件 ...

  9. (一)IDEA修改HTML不生效(未热部署)

    一.问题 IDEA 版本:2018.1.2 项目类型:SpringBoot 描述 : 修改JSP文件内容时,不会热部署,需要每次都重启项目才生效. 二.解决方案 加入Springboot开发者工具,即 ...

  10. JS验证正数字,正则的一种正数规则1

    JS中有一个验证数字的方法,就是!isNAN.NAN是非数字,!在JS里表示不是的意思,所以这个!isNAN就是判断不是非数字,也就是是数字.验证某个字符串是否是数字格式是:!isNaN(字符串)经过 ...