简单即时通讯、聊天室--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源码 支持手机即时通讯聊天
即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...
随机推荐
- useReducer代替Redux小案例-1(七)
使用useContext和useReducer是可以实现类似Redux的效果,并且一些简单的个人项目,完全可以用下面的方案代替Redux,这种做法要比Redux简单一些.因为useContext和us ...
- Oracle数据库安装以及使用脚本创建数据库授权
安装数据库 事实上Oracle安装 1.安装准备 Oracle的安装包下载以后是两个压缩包,同时选中两个压缩包右击进行解压 2.解压完成如下图所示 3.双击 setup.exe 文件进行安装,会弹出以 ...
- 判断x的m次方和y的m次方末尾三位数是否相等
/*==============================================对于任意给定的两个正整数x和y,是否存在一个不超过100的正整数m使得x^m与y^m的末尾三位数相等呢? ...
- linux: ubuntu 14.04 和16.04 快速下载
由于官网服务器在国外,下载速度奇慢,所以我们可以利用阿里云镜像下载ubuntuubuntu 14.04:http://mirrors.aliyun.com/ubuntu-releases/14.04/ ...
- linux下apache安装ssl步骤
制作证书: 参考:linux下运用opensll制作ssl证书 生成三个证书 server.crt .server-ca.crt.server.key 安装openssl tar -xzvf open ...
- [原][C++]拒绝智能指针与指针混用,常见智能指针问题
公司一个非专科的程序在开发过程中有些毛躁,但是又想使用些新学的技术 这天他正调试呢,发现有一个BUG怎么也找不到原因. 用的好好的内存怎么就突然被删除了呢,好好的指针,怎么就访问越界了呢 没办法,他只 ...
- 004-行为型-05-职责链模式(Chain of Responsibility)
一.概述 为请求创建一个接收此次请求对象的链 该模式构造一系列分别担当不同的职责的类的对象来共同完成一个任务,这些类的对象之间像链条一样紧密相连,所以被称作职责链模式. 在这种模式中,通常每个接收者都 ...
- java.io.IOException: Connection reset by peer at sun.nio.ch.FileDispatcherImpl.read0(Native Method) at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)
报错: java.io.IOException: Connection reset by peer at sun.nio.ch.FileDispatcherImpl.read0(Native Meth ...
- Python3实战——爬虫入门
一.安装库 使用conda安装: conda install requests 如果出现解析环境问题,需要激活conda环境: https://www.cnblogs.com/jdemarryme/p ...
- source insight 4.0常见问题及相关配置
摘自:https://blog.csdn.net/liitdar/article/details/79891795 本文介绍source insight 4.0常见的问题以及相关的配置. 1. ...