22
33import java .io .IOException ;
44import java .net .InetSocketAddress ;
5+ import java .nio .ByteBuffer ;
6+ import java .nio .channels .Channel ;
57import java .nio .channels .SelectionKey ;
68import java .nio .channels .Selector ;
79import java .nio .channels .ServerSocketChannel ;
10+ import java .nio .channels .SocketChannel ;
11+ import java .nio .charset .Charset ;
812import java .util .Iterator ;
913import java .util .Set ;
1014
1115/**
1216 * NIO服务器
1317 * NIO Non Blocking io非阻塞IO JDK1.4有的
18+ * NIO网络编程缺陷
19+ * 麻烦:NIO类库和API繁杂
20+ * 心累:可靠性能力补齐,工作量和难度都非常大
21+ * 有坑:Selector空轮询,导致CPU100%
1422 * @author dell
1523 */
1624public class NioServer {
@@ -36,6 +44,12 @@ public void start() throws IOException {
3644 */
3745 int readyChannels = selector .select ();
3846 if (readyChannels == 0 ) {
47+ /**
48+ * 这里有坑:Selector空轮询,导致CPU100%
49+ * 主要出现在类Unix上面,这个问题java官方声称在jdk1.6的版本中已经修复,
50+ * 但实际证明该问题在jdk1.8上面依然存在,只不过发生的概率低了一些.
51+ * 如果要在Linux上面使用,要非常小心,最好不用,老师没说替代办法
52+ */
3953 continue ;
4054 }
4155
@@ -47,9 +61,67 @@ public void start() throws IOException {
4761 //移除Set中的当前SelectionKey
4862 iterator .remove ();
4963 //接入事件,根据就绪状态,调用对应的方法处理业务逻辑
50-
64+ if (selectionKey .isAcceptable ()) {
65+ acceptHandler (serverSocketChannel , selector );
66+ }
5167 //可读事件,根据就绪状态,调用对应的方法处理业务逻辑
68+ if (selectionKey .isReadable ()) {
69+ readHandler (selectionKey , selector );
70+ }
5271 }
5372 }
5473 }
74+
75+ //接入事件处理
76+ private void acceptHandler (ServerSocketChannel serverSocketChannel , Selector selector ) throws IOException {
77+ //如果是接入事件,创建SocketChannel
78+ SocketChannel socketChannel = serverSocketChannel .accept ();
79+ socketChannel .configureBlocking (false );//将socketChannel设置为非阻塞工作模式
80+ //将channel注册到selector上,监听可读事件
81+ socketChannel .register (selector , SelectionKey .OP_READ );
82+ //回复客户端提示信息
83+ socketChannel .write (Charset .forName ("UTF-8" ).encode ("你与聊天室里其他人不是朋友关系,请注意隐私安全" ));
84+ }
85+
86+ //可读事件处理
87+ private void readHandler (SelectionKey selectionKey , Selector selector ) throws IOException {
88+ //要从selectionKey中获取到已经就绪的channel
89+ SocketChannel socketChannel = (SocketChannel )selectionKey .channel ();
90+ //创建Buffer
91+ ByteBuffer byteBuffer = ByteBuffer .allocate (1024 );
92+ //循环读取客户端的请求信息
93+ String request = "" ;
94+ while (socketChannel .read (byteBuffer ) > 0 ) {
95+ byteBuffer .flip ();//切换到读模式
96+ request += Charset .forName ("UTF-8" ).decode (byteBuffer );
97+ }
98+ //将channel再次注册到Selector上,监听他的可读事件
99+ socketChannel .register (selector , SelectionKey .OP_READ );
100+ if (request .length () > 0 ) {//将客户端发送的请求消息光博给其他客户端
101+ System .out .println (":::" + request );
102+ broadCast (selector , socketChannel , request );
103+ }
104+ }
105+ //广播给其他所有客户端
106+ private void broadCast (Selector selector , SocketChannel sourceChannel , String request ) {
107+ //获取到所有已接入的客户端channel
108+ Set <SelectionKey > selectionKeySet = selector .keys ();
109+ //
110+ selectionKeySet .forEach (selectionKey ->{//循环向所有channel广播信息
111+ Channel targetChannel = selectionKey .channel ();
112+ //删除发送消息的客户端
113+ if (targetChannel instanceof SocketChannel && targetChannel != sourceChannel ) {
114+ try {//将消息发送到targetChannel客户端
115+ ((SocketChannel )targetChannel ).write (Charset .forName ("UTF-8" ).encode (request ));
116+ } catch (IOException e ) {
117+ e .printStackTrace ();
118+ }
119+ }
120+ });
121+ }
122+
123+ public static void main (String [] args ) throws IOException {
124+ NioServer nioServer = new NioServer ();
125+ nioServer .start ();
126+ }
55127}
0 commit comments