直接用 NIO 实现一个多人聊天案例,话不多说,直接上代码。
1package com.mobaijun.TCP;
2
3import java.io.IOException;
4import java.net.InetSocketAddress;
5import java.nio.ByteBuffer;
6import java.nio.channels.*;
7import java.text.SimpleDateFormat;
8import java.util.Date;
9import java.util.Iterator;
10
11/**
12 * @Author:Auser·杰
13 * @DATE:2019/10/7 22:59
14 */
15public class ChatServer {
16 private Selector selector;
17 private ServerSocketChannel listenerChannel;
18 //服务器端口
19 private static final int PORT = 9999;
20
21 public ChatServer() {
22 try {
23 // 得到选择器
24 selector = Selector.open();
25 // 打开监听通道
26 listenerChannel = ServerSocketChannel.open();
27 // 绑定端口
28 listenerChannel.bind(new InetSocketAddress(PORT));
29 // 设置为非阻塞模式
30 listenerChannel.configureBlocking(false);
31 // 将选择器绑定到监听通道并监听 accept 事件
32 listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
33 printInfo("Chat Server is ready.......");
34 } catch (IOException e) {
35 e.printStackTrace();
36 }
37 }
38
39 public void start() {
40 try {
41 //不停轮询
42 while (true) {
43 //获取就绪 channel
44 int count = selector.select();
45 if (count > 0) {
46 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
47 while (iterator.hasNext()) {
48 SelectionKey key = iterator.next();
49 // 监听到 accept
50 if (key.isAcceptable()) {
51 SocketChannel sc = listenerChannel.accept();
52 //非阻塞模式
53 sc.configureBlocking(false);
54 //注册到选择器上并监听 read
55 sc.register(selector, SelectionKey.OP_READ);
56 System.out.println(sc.getRemoteAddress().toString().substring(1) + "上线了...");
57 //将此对应的 channel 设置为 accept,接着准备接受其他客户端请求
58 key.interestOps(SelectionKey.OP_ACCEPT);
59 }
60 //监听到 read
61 if (key.isReadable()) {
62 readMsg(key); //读取客户端发来的数据
63 }
64 //一定要把当前 key 删掉,防止重复处理
65 iterator.remove();
66 }
67 } else {
68 System.out.println("独自在寒风中等候...");
69 }
70 }
71 } catch (IOException e) {
72 e.printStackTrace();
73 }
74 }
75
76 private void readMsg(SelectionKey key) {
77 SocketChannel channel = null;
78 try {
79 // 得到关联的通道
80 channel = (SocketChannel) key.channel();
81 //设置 buffer 缓冲区
82 ByteBuffer buffer = ByteBuffer.allocate(1024);
83 //从通道中读取数据并存储到缓冲区中
84 int count = channel.read(buffer);
85 //如果读取到了数据
86 if (count > 0) {
87 //把缓冲区数据转换为字符串
88 String msg = new String(buffer.array());
89 printInfo(msg);
90 //将关联的 channel 设置为 read,继续准备接受数据
91 key.interestOps(SelectionKey.OP_READ);
92 BroadCast(channel, msg); //向所有客户端广播数据
93 }
94 buffer.clear();
95 } catch (IOException e) {
96 try {
97 //当客户端关闭 channel 时,进行异常如理
98 printInfo(channel.getRemoteAddress().toString().substring(1) + "下线了...");
99 key.cancel(); //取消注册
100 channel.close(); //关闭通道
101 } catch (IOException e1) {
102 e1.printStackTrace();
103 }
104 }
105 }
106
107 public void BroadCast(SocketChannel except, String msg) throws IOException {
108 System.out.println("发送广播...");
109 //广播数据到所有的 SocketChannel 中
110 for (SelectionKey key : selector.keys()) {
111 Channel targetchannel = key.channel();
112 //排除自身
113 if (targetchannel instanceof SocketChannel && targetchannel != except) {
114 SocketChannel dest = (SocketChannel) targetchannel;
115 //把数据存储到缓冲区中
116 ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
117 //往通道中写数据
118 dest.write(buffer);
119 }
120 }
121 }
122
123 private void printInfo(String str) {
124 //往控制台打印消息
125 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
126 System.out.println("[" + sdf.format(new Date()) + "] -> " + str);
127 }
128
129 public static void main(String[] args) {
130 ChatServer server = new ChatServer();
131 server.start();
132 }
133}
1package com.mobaijun.TCP;
2
3import java.io.IOException;
4import java.net.InetSocketAddress;
5import java.nio.ByteBuffer;
6import java.nio.channels.SelectionKey;
7import java.nio.channels.Selector;
8import java.nio.channels.SocketChannel;
9import java.util.Iterator;
10import java.util.Set;
11
12/**
13 * @Author:Auser·杰
14 * @DATE:2019/10/7 23:05
15 */
16public class ChatClient {
17 private final String HOST = "127.0.0.1"; //服务器地址
18 private int PORT = 9999; //服务器端口
19 private Selector selector;
20 private SocketChannel socketChannel;
21 private String userName;
22
23 public ChatClient() throws IOException {
24 //得到选择器
25 selector = Selector.open();
26 //连接远程服务器
27 socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", PORT));
28 //设置非阻塞
29 socketChannel.configureBlocking(false);
30 //注册选择器并设置为 read
31 socketChannel.register(selector, SelectionKey.OP_READ);
32 //得到客户端 IP 地址和端口信息,作为聊天用户名使用
33 userName = socketChannel.getLocalAddress().toString().substring(1);
34 System.out.println("---------------Client(" + userName + ") is ready---------------");
35 }
36
37 //向服务器端发送数据
38 public void sendMsg(String msg) throws Exception {
39 //如果控制台输入 bye 就关闭通道,结束聊天
40 if (msg.equalsIgnoreCase("bye")) {
41 socketChannel.close();
42 socketChannel = null;
43 return;
44 }
45 msg = userName + "说: " + msg;
46 try {
47 //往通道中写数据
48 socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
49 } catch (IOException e) {
50 e.printStackTrace();
51 }
52 }
53
54 //从服务器端接收数据
55 public void receiveMsg() {
56 try {
57 int readyChannels = selector.select();
58 if (readyChannels > 0) { //有可用通道
59 Set selectedKeys = selector.selectedKeys();
60 Iterator keyIterator = selectedKeys.iterator();
61 while (keyIterator.hasNext()) {
62 SelectionKey sk = (SelectionKey) keyIterator.next();
63 if (sk.isReadable()) {
64 //得到关联的通道
65 SocketChannel sc = (SocketChannel) sk.channel();
66 //得到一个缓冲区
67 ByteBuffer buff = ByteBuffer.allocate(1024);
68 //读取数据并存储到缓冲区
69 sc.read(buff);
70 //把缓冲区数据转换成字符串
71 String msg = new String(buff.array());
72 System.out.println(msg.trim());
73 }
74 keyIterator.remove(); //删除当前 SelectionKey,防止重复处理
75 }
76 } else {
77 System.out.println("人呢?都去哪儿了?没人聊天啊...");
78 }
79 } catch (IOException e) {
80 e.printStackTrace();
81 }
82 }
83}
1package com.mobaijun.TCP;
2
3import java.util.Scanner;
4
5/**
6 * @Author:Auser·杰
7 * @DATE:2019/10/7 23:07
8 */
9public class TestMoBai {
10 public static void main(String[] args) throws Exception {
11 //创建一个聊天客户端对象
12 ChatClient chatClient = new ChatClient();
13 new Thread() { //单独开一个线程不断的接收服务器端广播的数据
14 @Override
15 public void run() {
16 while (true) {
17 chatClient.receiveMsg();
18 try { //间隔 3 秒
19 sleep(3000);
20 } catch (InterruptedException e) {
21 e.printStackTrace();
22 }
23 }
24 }
25 }.start();
26 Scanner scanner = new Scanner(System.in);
27 //在控制台输入数据并发送到服务器端
28 while (scanner.hasNextLine()) {
29 String msg = scanner.nextLine();
30 chatClient.sendMsg(msg);
31 }
32 }
33}