Java NIO网络编程制作简易聊天室
一、NIO简介二、编程模型三、BIO网络模型四、NIO网络模型五、具体代码实现六、演示效果图
一、NIO简介
NIO全称:Non-blocking IO 或 New IO,是非阻塞式的IOJDK版本:JDK1.4+应用场景:高并发网络服务编程
二、编程模型
模型:对事物共性的抽象编程模型:对编程共性的抽象
三、BIO网络模型
BIO网络模型介绍 从图中可以看出,一个线程到第5步的时候,会阻塞在那等待客户端的下次请求,每新增一个客户端,就会启动一个新的线程来处理这个客户端的请求,如果客户端量很大的话,就会造成服务器压力过大而崩溃。
BIO网络模型缺点
阻塞式IO模型弹性伸缩能力差多线程非常消耗资源
四、NIO网络模型
NIO网络模型介绍 相对于BIO,NIO的网络模型就要复杂的多,服务端提供一个单线程的Selector组件,它是一个事件注册监听器,负责监听管理注册到它上面的连接和事件,但是本身不负责处理客户端的业务逻辑。当它监听到指定的事件,就会启动相应的事件处理器去处理请求,事件处理器可以依次处理多个请求,处理完成之后,由事件处理器去响应客户端,Selector本身继续监听其他事件。
NIO模型的优点
非阻塞式IO模型弹性伸缩能力强单线程节约资源
原生NIO的缺点
NIO类库和API比较复杂可靠性能力补齐,工作量和难度都非常大Selector空轮询,导致CPU占用100%的bug还没有修复
五、具体代码实现
服务端代码NioServer.java
package com
.feonix
;
import java
.io
.IOException
;
import java
.net
.InetSocketAddress
;
import java
.nio
.ByteBuffer
;
import java
.nio
.channels
.*
;
import java
.nio
.charset
.Charset
;
import java
.util
.HashMap
;
import java
.util
.Iterator
;
import java
.util
.Map
;
import java
.util
.Map
.Entry
;
import java
.util
.Set
;
public class NioServer {
private Map
<String, SocketChannel> clientsMap
= new HashMap<String, SocketChannel>();
public static void main(String
[] args
) {
NioServer nioServer
= new NioServer();
try {
nioServer
.start();
} catch (IOException e
) {
e
.printStackTrace();
}
}
public void start() throws IOException
{
Selector selector
= Selector
.open();
ServerSocketChannel serverSocketChannel
= ServerSocketChannel
.open();
serverSocketChannel
.bind(new InetSocketAddress(8000));
serverSocketChannel
.configureBlocking(false);
serverSocketChannel
.register(selector
, SelectionKey
.OP_ACCEPT
);
System
.out
.println("服务器端启动成功!");
for (; ; ) {
int readyChannels
= selector
.select();
if (readyChannels
== 0) {
continue;
}
Set
<SelectionKey> selectionKeys
= selector
.selectedKeys();
Iterator
<SelectionKey> iterator
= selectionKeys
.iterator();
while (iterator
.hasNext()) {
SelectionKey selectionKey
= iterator
.next();
iterator
.remove();
if (selectionKey
.isAcceptable()) {
acceptHandler(serverSocketChannel
, selector
);
}
if (selectionKey
.isReadable()) {
readHandler(selectionKey
, selector
);
}
}
}
}
private void acceptHandler(ServerSocketChannel serverSocketChannel
, Selector selector
) throws IOException
{
SocketChannel socketChannel
= serverSocketChannel
.accept();
socketChannel
.configureBlocking(false);
socketChannel
.register(selector
, SelectionKey
.OP_READ
);
socketChannel
.write(Charset
.forName("UTF-8").encode("系统提示:你与聊天室内的其他人都不是好友关系,请注意隐私安全!"));
}
private void readHandler(SelectionKey selectionKey
, Selector selector
) throws IOException
{
SocketChannel socketChannel
= (SocketChannel
) selectionKey
.channel();
ByteBuffer byteBuffer
= ByteBuffer
.allocate(1024);
String request
= "";
while (socketChannel
.read(byteBuffer
) > 0) {
byteBuffer
.flip();
request
+= Charset
.forName("UTF-8").decode(byteBuffer
);
}
socketChannel
.register(selector
, SelectionKey
.OP_READ
);
if (request
.length() > 0) {
if (request
.contains("name:::")) {
String
[] res
= request
.split(":::");
if (res
.length
== 2) {
clientsMap
.put(res
[1], socketChannel
);
System
.out
.printf("客户端%s加入聊天室\n", res
[1]);
broadCast(socketChannel
, res
[1] + "加入聊天室,和Ta打个招呼吧~");
}
} else {
if (!clientsMap
.isEmpty()) {
for (Entry
<String, SocketChannel> clientSet
: clientsMap
.entrySet()) {
if (socketChannel
.equals(clientSet
.getValue())) {
String name
= clientSet
.getKey();
System
.out
.printf("客户端%s消息:%s\n", name
, request
);
broadCast(socketChannel
, name
+ "说:" + request
);
}
}
}
}
}
}
private void broadCast(SocketChannel sourceChannel
, String request
) {
if (!clientsMap
.isEmpty()) {
clientsMap
.entrySet().forEach(clientSet
-> {
SocketChannel targetChannel
= clientSet
.getValue();
if (!sourceChannel
.equals(targetChannel
)) {
try {
targetChannel
.write(Charset
.forName("UTF-8").encode(request
));
} catch (IOException e
) {
e
.printStackTrace();
}
}
});
}
}
}
客户端代码NioClient.java
package com
.feonix
;
import java
.io
.IOException
;
import java
.net
.InetSocketAddress
;
import java
.nio
.channels
.SelectionKey
;
import java
.nio
.channels
.Selector
;
import java
.nio
.channels
.SocketChannel
;
import java
.nio
.charset
.Charset
;
import java
.util
.Scanner
;
public class NioClient {
public static void main(String
[] args
) {
NioClient nioClient
= new NioClient();
try {
nioClient
.start();
} catch (IOException e
) {
e
.printStackTrace();
}
}
public void start() throws IOException
{
SocketChannel socketChannel
= SocketChannel
.open(new InetSocketAddress("127.0.0.1", 8000));
System
.out
.println("客户端启动成功!");
Selector selector
= Selector
.open();
socketChannel
.configureBlocking(false);
socketChannel
.register(selector
, SelectionKey
.OP_READ
);
new Thread(new NioClientHandler(selector
)).start();
Scanner key
= new Scanner(System
.in
);
System
.out
.print("请输入你的昵称:");
String name
= key
.nextLine();
if (name
!= null
&& name
.length() > 0) {
socketChannel
.write(Charset
.forName("UTF-8").encode("name:::" + name
));
}
while (key
.hasNextLine()) {
String request
= key
.nextLine();
if (request
!= null
&& request
.length() > 0) {
socketChannel
.write(Charset
.forName("UTF-8").encode(request
));
}
}
}
}
客户端消息读取处理器NioClientHandler.java
package com
.feonix
;
import java
.io
.IOException
;
import java
.nio
.ByteBuffer
;
import java
.nio
.channels
.SelectionKey
;
import java
.nio
.channels
.Selector
;
import java
.nio
.channels
.SocketChannel
;
import java
.nio
.charset
.Charset
;
import java
.util
.Iterator
;
import java
.util
.Set
;
public class NioClientHandler implements Runnable {
private Selector selector
;
public NioClientHandler(Selector selector
) {
this.selector
= selector
;
}
@Override
public void run() {
try {
for (; ; ) {
int readyChannels
= selector
.select();
if (readyChannels
== 0) {
continue;
}
Set
<SelectionKey> selectionKeys
= selector
.selectedKeys();
Iterator
<SelectionKey> iterator
= selectionKeys
.iterator();
while (iterator
.hasNext()) {
SelectionKey selectionKey
= iterator
.next();
iterator
.remove();
if (selectionKey
.isReadable()) {
readHandler(selectionKey
, selector
);
}
}
}
} catch (IOException e
) {
e
.printStackTrace();
}
}
private void readHandler(SelectionKey selectionKey
, Selector selector
) throws IOException
{
SocketChannel socketChannel
= (SocketChannel
) selectionKey
.channel();
ByteBuffer byteBuffer
= ByteBuffer
.allocate(1024);
String response
= "";
while (socketChannel
.read(byteBuffer
) > 0) {
byteBuffer
.flip();
response
+= Charset
.forName("UTF-8").decode(byteBuffer
);
}
socketChannel
.register(selector
, SelectionKey
.OP_READ
);
if (response
.length() > 0) {
System
.out
.println(response
);
}
}
}
六、演示效果图
以上就是本次分享的全部内容,欢迎留言讨论,记得点赞哦(๑¯∀¯๑)~