简单即时通讯、聊天室--java NIO版本
实现的功能:
运行一个服务端,运行多个客户端。在客户端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版本的更多相关文章
- 黑科技!仅需 3 行代码,就能将 Gitter 集成到个人网站中,实现一个 IM 即时通讯聊天室功能?
欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...
- C# SignalR 即时通讯 聊天室
一.SignalR简介 SignalR:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据.实现实时服务器与客户端通信.是一个开源.NET 库生成需要实时用 ...
- 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。
基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...
- Openfire XMPP Smack RTC IM 即时通讯 聊天 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 使用Servlet和JSP实现一个简单的Web聊天室系统
1 问题描述 利用Java EE相关技术实现一个简单的Web聊天室系统,具体要求如下. (1)编写一个登录 ...
- 分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室
实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSoc ...
- 用Java构建一个简单的WebSocket聊天室
前言 首先对于一个简单的聊天室,大家应该都有一定的概念了,这里我们省略用户模块的讲解,而是单纯的先说说聊天室的几个功能:自我对话.好友交流.群聊.离线消息等. 今天我们要做的demo就能帮我们做到这一 ...
- 简单聊天室(java版)
这是本人从其他地方学习到的关于聊天室的一个模本,我从中截取了一部分关于客户端和服务端通信的Socket的内容.希望对大家对socket有个了解,我写的这些代码可以实现两人或多人在多台电脑上实现简单的对 ...
- java Activiti6 工作流引擎 websocket 即时聊天 SSM源码 支持手机即时通讯聊天
即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...
随机推荐
- vs2017 编译linux项目
官方文档1: https://blogs.msdn.microsoft.com/vcblog/2017/04/11/linux-development-with-c-in-visual-studio/ ...
- useReducer介绍和简单使用(六)
上节课学习了useContext函数,那这节课开始学习一下useReducer,因为他们两个很像,并且合作可以完成类似的Redux库的操作.在开发中使用useReducer可以让代码具有更好的可读性和 ...
- .gitignore忽略多层文件夹用**
一.写法 **/bin/Debug/ 前面的两个*号代表任意多层上级文件夹 需要 git 1.8.2 及其以上的版本才支持 如何查看当前版本并且升级(windows) 二.如何升级 git是2.14. ...
- web常用服务架构
架构风格就是一种项目的设计模式.常见的架构风格有基于客户端与服务端的.基于组件模型的(EJB).分层架构(MVC).面向服务架构(SOA)等. 一.单体架构 单体架构也称为单体系统或单体应用,就是一种 ...
- Java Web J2EE下的两大框架SSH和SSM对比
当下流行的两种企业开发MVC开源框架,是我们Java程序猿必备知识能力.MVC,即模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界 ...
- C# MVC Ajax上传多个图片,可预览,可重复上传等
//上传文件 function UploadFile(el) { var dataValue = $(el).attr("data-id"); var ele = dataValu ...
- Apache log4j 1.2 - Short introduction to log4j
Apache log4j 1.2 - Short introduction to log4jhttps://logging.apache.org/log4j/1.2/manual.html log4j ...
- Manning Java Persistence with Hibernate & hibernate_in_action
Manning | Java Persistence with Hibernatehttps://www.manning.com/books/java-persistence-with-hiberna ...
- 搭建 Kafka 集群 (v2.12-2.3.0)
服务器:10.20.32.121,10.20.32.122,10.20.32.123 三台服务器都需要安装jdk.配置zookeeper.配置kafka 1.安装配置jdk1.8 [root@loca ...
- 深入学习c++--容器
1. 简介 1. 序列式容器: array, vector, deque, list, forward_list --- 数组 或者 指针实现 2. 关联容器: set, map, multiset, ...