# IO 流

主要由 4 个抽象类基类中派送出来。

  • InputStream/Reader:所有输入流的基类。
  • OutputStream/writer:所有输出流的基类。

两种区别就是前者是字节,后者是字符。

# 常见 IO 流问答

  1. 字节流和字符流哪个好?
    答:字节流,IO 操作一般是直接操作磁盘文件,这些流在传输时都是以字节方式传递。
    但是在内存中的 IO 操作就更佳适合字符流,字符流拥有缓冲区,性能更佳。
  2. 缓冲区的作用?
    缓冲区用于需要频繁操作的场景。就是一块划分出来的特殊内存区。对于提高 IO 操作的性能有不错的作用。
  3. 字符流和字节流有什么区别?
    还是上面的第一个问题变版,一个有缓冲区,一个没有缓冲区。

# IO 设计模式

# 装饰器模式

可用于在不改变原有对象的情况下拓展功能,有点像插件。

装饰器的核心就是 FilterInputStream(对应输入流)和 FilterOutputStream(对应输出流)。相对地对应着 InputStream 和 OutputStream 子类对象的功能。

# 适配器模式

应用在接口互不兼容的类的协调工作,联想电源适配器?

适配器和装饰器两者有何区别?

装饰器侧重动态增强原始类的功能,且支持嵌套。

适配器侧重兼容,将不兼容的接口类整合。

# 工厂模式

NIO 中主要使用了工厂模式,工厂模式是用来创建对象的。(NIO 接下来会提到)

# 观察者模式

NIO 中的文件目录监听服务用到了观察者模式。主要注意两个接口:WatchService 和 Watchable。这两个中前者是观察者,后者是被观察者。

主要的三种监听事件:

  1. 文件创建
  2. 文件删除
  3. 文件修改

有哪些常见的 IO 模型?


UNIX 系统下,总共有五种:

  • 同步阻塞 I/O
  • 同步非阻塞 I/O
  • I/O 多路复用
  • 信号驱动 I/O
  • 异步 I/O

Java 中三种常见的 IO 模型

  1. 同步阻塞 IO 模型(Blocking I/O)


    特点:应用程序 read 时,一直阻塞,直到拷贝完成。


    缺点:无法应对高并发,当在十万或百万级别连接时,传统 BIO 就 g 了。

  2. 同步非阻塞 I/O(Non-blocking/New I/O)


    特点:在 read 时不阻塞,只在拷贝时阻塞。


    缺点:占用 CPU 资源较多

优化:I/O 多路复用模型,使用 Selector 选择器进行 read 前质询,待内核把数据准备好后再 read。此优化可以通过减少无效系统调用,来减少对 CPU 的消耗。

有同步当然也有异步啦。


AIO(Asynchronous I/O)
异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。


一张图总结三种IO

总结

# NIO

# Channel 简述

File 读操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.jun.channel;

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/*
* @Author: jun
* @Date:2023/4/4 11:18
* @概述:
*/
public class FileChannelDemo1 {
//FileChannel读取数据到buffer中

public static void main(String[] args) throws Exception {
//创建FileChannel
RandomAccessFile file = new RandomAccessFile("d:\\JAVAIdeaProjects\\Java_nio\\nio_test\\FileData\\01.txt","rw");
FileChannel channel = file.getChannel();
//创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);

//读取数据到buffer中
int bytesRead = channel.read(buffer);
while (bytesRead != -1) {
System.out.println("读取了:"+bytesRead);
buffer.flip();
while (buffer.hasRemaining()) {
System.out.println((char)buffer.get());
}
buffer.clear();
bytesRead = channel.read(buffer);
}
file.close();
System.out.println("读取数据结束");
}
}

file 写操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.jun.channel;

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/*
* @Author: jun
* @Date:2023/4/4 11:38
* @概述:
*/
public class FileChannelDemo2 {
//FileChannel写操作

public static void main(String[] args) throws Exception {
//打开FileChannel
RandomAccessFile file = new RandomAccessFile("d:\\JAVAIdeaProjects\\Java_nio\\nio_test\\FileData\\01.txt","rw");
FileChannel channel = file.getChannel();
//创建buffer对象
ByteBuffer buffer = ByteBuffer.allocate(1024);

String newData = "data jun";

//清空buffer,其实就是覆盖掉原先的
buffer.clear();

//写入内容
buffer.put(newData.getBytes());

buffer.flip();

//FileChannel完成最终的实现
while (buffer.hasRemaining()) {
channel.write(buffer);
}

//关闭channel
channel.close();
}
}

通道间数据传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.jun.channel;

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;

/*
* @Author: jun
* @Date:2023/4/4 12:35
* @概述:
*/
public class FileChannelDemo3 {
//通道之间数据传输

public static void main(String[] args) throws Exception {
//创建两个fileChannel
RandomAccessFile aFile = new RandomAccessFile("d:\\JAVAIdeaProjects\\Java_nio\\nio_test\\FileData\\01.txt", "rw");
FileChannel fromChannel = aFile.getChannel();

RandomAccessFile bFile = new RandomAccessFile("d:\\JAVAIdeaProjects\\Java_nio\\nio_test\\FileData\\02.txt", "rw");
FileChannel toChannel = bFile.getChannel();

//fromChannel 传输到 toChannel
long position = 0;
long size = fromChannel.size();
toChannel.transferFrom(fromChannel,position,size);

aFile.close();
bFile.close();
System.out.println("传输结束!");


}
}

# Socket 通道

ServerSocketChannel 一个 socket 监听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.jun.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/*
* @Author: jun
* @Date:2023/4/4 13:09
* @概述:
*/
public class ServerSocketChannelDemo {
public static void main(String[] args) throws Exception {
//端口号设置

int port = 8888;

//buffer
ByteBuffer buffer = ByteBuffer.wrap("hello abc".getBytes());
//serverSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//绑定
serverSocketChannel.socket().bind(new InetSocketAddress(port));
//设置非阻塞模式
serverSocketChannel.configureBlocking(false);

//监听有新链接传入
while (true) {
System.out.println("等待传输数据中。。");
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel == null) { //没有链接传入
System.out.println("null");
Thread.sleep(2000);
} else {
System.out.println("Incoming connection from:"+ socketChannel.socket().getRemoteSocketAddress());
buffer.rewind(); //指针为0
socketChannel.write(buffer);
socketChannel.close();
}
}
}
}

# Buffer

buffer 的几个方法
put ():将一个元素添加到缓冲区的末尾。

get ():从缓冲区的开头移除并返回一个元素。

full ():如果缓冲区已满,则返回 True,否则返回 False。

empty ():如果缓冲区为空,则返回 True,否则返回 False。

clear ():从缓冲区中移除所有元素。

size ():返回缓冲区中当前元素的数量。

peek ():返回缓冲区的下一个元素,但不删除它。

缓冲区操作:

写入缓冲区:将数据写入缓冲区,可以通过系统调用或者库函数实现。

读取缓冲区:从缓冲区中读取数据,可以通过系统调用或者库函数实现。

清空缓冲区:将缓冲区中的数据清空,可以使用库函数或者操作系统提供的清空缓冲区方法。

刷新缓冲区:将缓冲区的内容刷新到文件或者设备中,可以使用库函数或者操作系统提供的刷新缓冲区方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.jun.buffer;

import org.junit.Test;

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/*
* @Author: jun
* @Date:2023/4/9 15:20
* @概述:
*/

/**
* 这是一个使用JUnit测试框架的Java方法,方法名为b04(),没有输入参数,可能会抛出异常。
*
* 该方法打开一个文件("D:\JAVAIdeaProjects\Java_nio\nio_test\FileData\01.txt"),创建一个随机访问文件对象并获取其通道。然后,它使用文件通道将文件映射到内存中,并返回一个MappedByteBuffer对象。
*
* 接下来,该方法在内存映射文件的偏移量为0的位置和1023的位置分别写入字节97和122。
*
* 最后,该方法关闭文件。
*/

public class BufferDemo3 {
static private final int start = 0;
static private final int size = 1024;

@Test
public void b04() throws Exception {
RandomAccessFile file = new RandomAccessFile("D:\\JAVAIdeaProjects\\Java_nio\\nio_test\\FileData\\01.txt", "rw");
FileChannel fileChannel = file.getChannel();
MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, size);

map.put(0,(byte) 97);
map.put(1023,(byte) 122);
file.close();
}
}

# 客户端模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.jun.selector;

import org.junit.Test;

import java.io.IOException;
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.util.Date;
import java.util.Iterator;
import java.util.Set;

/*
* @Author: jun
* @Date:2023/4/9 16:28
* @概述:
*/
public class SelectorDemo2 {

//服务端代码
@Test
public void serverDemo() throws Exception {
//1 获取服务端通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2 切换非阻塞模式
serverSocketChannel.configureBlocking(false);
//3 创建buffer
ByteBuffer serverByteBuffer = ByteBuffer.allocate(1024);
//4 绑定端口号
serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",9090));
//5 获取selector选择器
Selector selector = Selector.open();
//6 通道注册到选择器,进行监听
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//7 选择器进行轮询,进行后续操作
while (selector.select() > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历
Iterator<SelectionKey> selectionKeysIterator = selectionKeys.iterator();
while (selectionKeysIterator.hasNext()) {
//获取就绪操作
SelectionKey next = selectionKeysIterator.next();
//判断什么操作
if (next.isAcceptable()) {
//获取连接
SocketChannel nextOperate = serverSocketChannel.accept();
//切换非阻塞模式
nextOperate.configureBlocking(false);
//注册
nextOperate.register(selector,SelectionKey.OP_READ);

} else if (next.isReadable()) {
SocketChannel channel = (SocketChannel) next.channel();


ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//读取数据
int length = 0;
while ((length = channel.read(byteBuffer)) > 0) {
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,length));
byteBuffer.clear();
}
}
System.out.println(next);
selectionKeysIterator.remove();
}

}
}

//客户端代码
@Test
public void clientDemo() throws Exception {
//1.获取通道,绑定主机和端口号
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9090));
//2.切换到非阻塞模式
socketChannel.configureBlocking(false);
//3.创建buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//4.写入buffer数据
byteBuffer.put(new Date().toString().getBytes());
//5.模式切换
byteBuffer.flip();
//6.写入通道
socketChannel.write(byteBuffer);
//7.关闭
byteBuffer.clear();

}
}

# JAVA NIO(Selector)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.jun.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;

/*
* @Author: jun
* @Date:2023/4/9 16:10
* @概述:
*/

/**
* 使用Java NIO库实现了基于事件驱动的非阻塞网络通信,
* 通过Selector监听ServerSocketChannel上的事件,
* 当接收到新的连接请求时就可以做出相应处理。
*/
public class SelectorDemo1 {

public static void main(String[] args) throws IOException {
//创建selector
Selector selector = Selector.open();

//通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

//非阻塞
serverSocketChannel.configureBlocking(false);

//绑定连接
serverSocketChannel.bind(new InetSocketAddress(9999));

//将通道注册到选择器上,并指定监听事件为:"接收"事件,(即当有客户端连接请求时触发)。
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

//查询已经就绪通道操作
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历集合
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//判断key就绪状态操作
if (key.isAcceptable()) {

} else if (key.isConnectable()) {

} else if (key.isReadable()) {

} else if (key.isWritable()) {

}

iterator.remove();
}
}
}

# FileLock

文件锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.jun.filelock;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/*
* @Author: jun
* @Date:2023/4/10 11:35
* @概述:
*/
public class FileLockDemo1 {
public static void main(String[] args) throws Exception {
String input = "jun";
System.out.println("input:"+input);

ByteBuffer buffer = ByteBuffer.wrap(input.getBytes());

String filePath = "D:\\JAVAIdeaProjects\\Java_nio\\nio_test\\FileData\\01.txt";
Path path = Paths.get(filePath);
FileChannel channel = FileChannel.open(
path,
StandardOpenOption.WRITE,
StandardOpenOption.APPEND);

channel.position(channel.size()-1);
//加锁
FileLock lock = channel.lock();

System.out.println("是否为共享锁:"+lock.isShared());

channel.write(buffer);

channel.close();

//读文件
readFile(filePath);
}

private static void readFile(String filePath) throws Exception {
FileReader fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String readLine = bufferedReader.readLine();
System.out.println("读取出内容:");
while (readLine != null) {
System.out.println(" "+readLine);
readLine = bufferedReader.readLine();
}

fileReader.close();
bufferedReader.close();

}
}

# 异步 FileChannel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.jun.asyncfilechannel;


import org.junit.Test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;

/*
* @Author: jun
* @Date:2023/4/10 14:16
* @概述:
*/
@SuppressWarnings({"all"})
public class AsyncFileChannelDemo {
@Test
public void readAsyncFileChannelFuture() throws Exception {
//1 创建AsynchronousFileChannel
Path path = Paths.get("D:\\JAVAIdeaProjects\\Java_nio\\nio_test\\FileData\\03.txt");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);

//2 创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3 调用channel的read方法得到Future
Future<Integer> future = fileChannel.read(buffer, 0);
//4 判断是否完成 isDone,返回true
while (!future.isDone()) ;
//5 读取数据到buffer里面
buffer.flip();
// while (buffer.remaining() > 0) {
// System.out.println(buffer.get());
// }
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String string = new String(data, StandardCharsets.UTF_8);
System.out.println(string);
buffer.compact();

}
}

# Charset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.jun.Charset;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/*
* @Author: jun
* @Date:2023/4/11 13:22
* @概述:
*/
public class CharsetDemo {
public static void main(String[] args) throws CharacterCodingException {
//1.获取charset对象
Charset charset = Charset.forName("UTF-8");
//2.获取编码器对象
CharsetEncoder charsetEncoder = charset.newEncoder();
//3.创建缓冲区
CharBuffer charBuffer = CharBuffer.allocate(1024);
charBuffer.put("jun会java");
charBuffer.flip();
//4.编码
ByteBuffer byteBuffer = charsetEncoder.encode(charBuffer);
for (int i = 0; i < byteBuffer.limit() ;i++) {
System.out.println(byteBuffer.get());

}
//5.解码:获取解码器对象
byteBuffer.flip();
CharsetDecoder charsetDecoder = charset.newDecoder();

//6.解码过程
CharBuffer charBuffer1 = charsetDecoder.decode(byteBuffer);
System.out.println("解码之后的结果:");
System.out.println(charBuffer1.toString());

//7.使用GBK解码
Charset charset1 = Charset.forName("GBK");
byteBuffer.flip();
CharBuffer charBuffer2 = charset1.decode(byteBuffer);
System.out.println(charBuffer2.toString());

//8.获取Charset所支持的字符编码
SortedMap<String, Charset> map = Charset.availableCharsets();
Set<Map.Entry<String,Charset>> set = map.entrySet();
for (Map.Entry<String, Charset> entry:
set){
System.out.println(entry.getKey()+"="+entry.getValue().toString());
}
}
}

# 聊天室综合案例

# 服务端编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package com.jun.server;

/*
* @Author: jun
* @Date:2023/4/11 14:29
* @概述:
*/
//服务器端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class ChatSever {

//服务器端启动的方法
public void startServer() throws IOException {
//1.创建Selector选择器
Selector selector = Selector.open();
//2.创建ServerSocketChannel通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//3.为channel通道绑定监听端口
serverSocketChannel.bind(new InetSocketAddress(8000));
//设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//4.把channel通道注册到selector选择器上
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器已经启动成功。");
//5.循环,等待是否有新的链接接入
for(;;){
//获取channel数量
int readChannels = selector.select();
if (readChannels == 0) {
continue;
}
//获取可用的channel
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历集合
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//移除set集合当前selectionKey
iterator.remove();

//6.根据就绪状态,调用对应的方法实现具体业务操作
//6.1 如果accept状态
if (selectionKey.isAcceptable()) {
//do something
acceptOperator(serverSocketChannel,selector);
}
//6.2 如果可读状态
if (selectionKey.isReadable()) {
//do something
readOperator(selector,selectionKey);
}
}
}

}

//处理可读状态操作
private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
//1 从SelectionKey获取到已经就虚的通道,强转为SocketChannel
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
//2 创建buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//3 循环读取客户端消息
int readLength = socketChannel.read(byteBuffer);
String message = "";
if (readLength > 0) {
//切换模式
byteBuffer.flip();

//读取内容
message += Charset.forName("UTF-8").decode(byteBuffer);
}
//4 将channel再次注册到选择器中,监听可读状态
socketChannel.register(selector,SelectionKey.OP_READ);
//5 不止一个人,把客户端的消息给所有的客户端全部发送一遍,就是广播到其它客户端
if (message.length() > 0) {
//广播给其它的客户端
System.out.println(message);
castOtherClient(message,selector,socketChannel);
}
}

//广播个其它客户端
private void castOtherClient(String message, Selector selector, SocketChannel socketChannel) throws IOException {
//1 获取所有已经接入channel
Set<SelectionKey> selectionKeySet = selector.keys();
//2 循环向所有channel广播消息
for (SelectionKey selectionKey: selectionKeySet){
//获取每个channel
Channel tarChannel = selectionKey.channel();
//不需要给自己发送
if (tarChannel instanceof SocketChannel && tarChannel != socketChannel) {
((SocketChannel)tarChannel).write(Charset.forName("UTF-8").encode(message));
}
}
}

//处理接入状态操作
private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
//1 接入状态,创建socketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
//2 把socketChannel设置非阻塞模式
socketChannel.configureBlocking(false);
//3 把channel注册到selector选择器上,监听可读状态
socketChannel.register(selector,SelectionKey.OP_READ);
//4 客户端回复消息
socketChannel.write(Charset.forName("UTF-8")
.encode("欢迎你进入聊天室,你与其它人还不是朋友,请注意隐私安全"));
}

//启动主方法
public static void main(String[] args) {
try {
new ChatSever().startServer();
} catch (IOException e) {
e.printStackTrace();
}
}
}

# 客户端编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.jun.client;

/*
* @Author: jun
* @Date:2023/4/11 14:30
* @概述:
*/

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 ChatClient {
//启动客户都方法
public void startClient(String name) throws IOException {
//连接服务器
SocketChannel socketChannel =
SocketChannel.open(new InetSocketAddress("127.0.0.1",8000));
//接收服务端响应数据
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
//创建线程
new Thread(new ClientThread(selector)).start();

//向服务器发送消息
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String msg = scanner.nextLine();
if (msg.length() > 0) {
socketChannel.write(Charset.forName("UTF-8").encode(name + ":" + msg));

}
}


}

public static void main(String[] args) {
// try{
// new ChatClient().startClient();
// } catch (IOException e){
// e.printStackTrace();
// }

}
}