基于 java nio 长连接实现的聊天室,如果并发量大的话,可能会有线程问题。
服务端代码
package com.lp.io.socket;
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.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class LpNioServerSocket {
//线程安全
private static List channels = Collections.synchronizedList( new ArrayList() );
public static void main(String[] args) {
HandlerSelectionKey handler = new HandlerHandlerSelectionKeyImpl();
try {
//创建 ServerSocketChannel
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.bind(new InetSocketAddress("localhost", 12345));
//创建 Selector
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
//死循环,持续接收 客户端连接
while(true) {
//selector.select(); 是阻塞方法
int keys = selector.select();
if(keys > 0) {
Iterator it = selector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
it.remove();
//处理 SelectionKey
handler.handler(key, selector);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* SelectionKey 处理接口
*
*/
public static interface HandlerSelectionKey {
public void handler(SelectionKey key, Selector selector) throws IOException;
}
/**
* SelectionKey 接口 实现类
*
*/
public static class HandlerHandlerSelectionKeyImpl implements HandlerSelectionKey {
@Override
public void handler(SelectionKey key, Selector selector) throws IOException {
int keyState = selectionKeyState(key);
switch (keyState) {
case SelectionKey.OP_ACCEPT:
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
accept(serverSocketChannel, selector);
break;
case SelectionKey.OP_READ:
SocketChannel readSocketChannel = (SocketChannel) key.channel();
read(readSocketChannel, selector);
break;
}
}
/**
* 获取 SelectionKey 是什么事件
* @param key
* @return
*/
private int selectionKeyState(SelectionKey key) {
if(key.isAcceptable()) {
return SelectionKey.OP_ACCEPT;
} else if(key.isReadable()) {
return SelectionKey.OP_READ;
}
return -1;
}
/**
* 接口客户端请求
* @param serverSocketChannel
* @param selector
* @throws IOException
*/
private void accept(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
channels.add(socketChannel);
//将 channel 注册到 Selector
socketChannel.register(selector, SelectionKey.OP_READ);
}
/**
* 读取客户端发送过来的信息
* @param socketChannel
* @param selector
* @throws IOException
*/
private void read(SocketChannel socketChannel, Selector selector) throws IOException {
ByteBuffer readBuffer = ByteBuffer.allocate(8192);
int readBytes = socketChannel.read(readBuffer);
String msg = "";//客户端发送来的消息
if(readBytes > 0) {
msg = new String(readBuffer.array(), 0, readBytes);
System.out.println("客户端发送来的消息");
System.out.println(msg);
}
write(socketChannel, msg);
}
/**
* 响应客户端请求
* @param socketChannel
* @param selector
* @throws IOException
*/
private void write(SocketChannel socketChannel, String msg) throws IOException {
msg = "游客" + socketChannel.hashCode()+ "\r\n " + msg;
//响应消息
byte[] responseByte = msg.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(responseByte.length);
writeBuffer.put(responseByte);
writeBuffer.flip();
//响应客户端
for(int i=0; i
if(!socketChannel.equals(channels.get(i))) {
channels.get(i).write(writeBuffer);
}
}
}
}
}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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
客户端代码
package com.lp.io.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class LpSocketClient1 {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
* @throws InterruptedException
*/
public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
final Socket socket = new Socket("localhost", 12345);
Thread inT = new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[8192];
int readSize = inputStream.read(b);
System.out.println(new String(b,0,readSize));
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
inT.start();
while(true) {
InputStreamReader stdin = new InputStreamReader(System.in);//键盘输入
BufferedReader bufin = new BufferedReader(stdin);
String str = bufin.readLine();
System.out.println(str);
OutputStream outStream = socket.getOutputStream();
outStream.write(str.getBytes());
outStream.flush();
}
}
}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
启动服务端代码,然后启动多个客户端。在某个客户端代码控制台输入英文(输入中文乱码),点击回车,可以看到其他客户端控制台有信息输出。