本文实例为大家分享了java基于NIO实现群聊模式的具体代码,供大家参考,具体内容如下
Client
package com.qst.chat;
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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
public class GroupChatClient {
private final int PORT = 9999;
private final String HOST = "localhost";
private SocketChannel channel;
private static Selector selector;
private String name;
public GroupChatClient() throws IOException {
selector = Selector.open();
// 连接服务器
channel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
// 设置非阻塞
channel.configureBlocking(false);
// 将channel 注册到selector
channel.register(selector, SelectionKey.OP_READ);
name = channel.getLocalAddress().toString().substring(1);
System.out.println(name + "is ok ....");
}
// 向服务器发送消息
public void sendTO(String msg) {
ByteBuffer buffer = ByteBuffer.wrap((name+":"+msg).getBytes());
try {
channel.write(buffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 读取从服务器端回复的消息
public static void getInfo() {
try {
if(selector.select() >0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()) {
SelectionKey key = iterator.next();
if(key.isReadable()) {
// 得到通道
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len;
// 把读到的缓冲区的数据转成字符串
while((len = sc.read(buffer)) > 0) {
System.out.println(new String(buffer.array()));
}
}
}
// 删除当前的selectionKey, 防止重复操作
iterator.remove();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
GroupChatClient client = new GroupChatClient();
new Thread() {
public void run() {
while(true)
{
try {
Thread.sleep(3000);
GroupChatClient.getInfo();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}.start();
Scanner sc = new Scanner(System.in);
// while(true) {
// String name = sc.nextLine();
// client.sendTO(name);
// }
while(sc.hasNextLine()) {
String s = sc.nextLine();
client.sendTO(s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Server端
package com.qst.chat;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.time.chrono.IsoChronology;
import java.util.Iterator;
import com.sun.accessibility.internal.resources.accessibility;
import sun.print.resources.serviceui;
public class GroupChatServer {
private static ServerSocketChannel socketChannel;
private static Socket socket;
private static Selector selector;
private static SocketChannel accept;
public GroupChatServer() throws IOException {
socketChannel = ServerSocketChannel.open();
selector = Selector.open();
// 绑定端口
socketChannel.socket().bind(new InetSocketAddress(9999));
// 设置非阻塞模式
socketChannel.configureBlocking(false);
// 将该通道 注册到selector
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
// 监听
public static void listen() {
System.out.println("监听线程: " + Thread.currentThread().getName());
try {
while (selector.select() > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
if (iterator.hasNext()) {
// 遍历得到selectionKey 集合
SelectionKey next = iterator.next();
if (next.isAcceptable()) {
next.channel();
// socketChannel = (ServerSocketChannel) next.channel();
SocketChannel accept = socketChannel.accept();
accept.configureBlocking(false);
accept.register(selector, SelectionKey.OP_READ);
System.out.println(accept.getRemoteAddress()+" 上线 了。。。");
}
if (next.isReadable()) {
readDate(next);
}
// 移除当前的next,防止重复处理
iterator.remove();
// System.out.println("未发现");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 读取客户端消息
public static void readDate(SelectionKey key) {
try {
accept = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = accept.read(buffer);
if (len > 0) {
buffer.flip();
String msg = new String(buffer.array());
System.out.println("user = " + msg);
// 向其它的客户端转发消息(去掉自己)
sendToAll(msg, accept);
buffer.clear();
}
} catch (IOException e) {
// TODO Auto-generated catch block
try {
String msg = accept.getRemoteAddress().toString();
// 取消注册
key.cancel();
// 关闭通道
accept.close();
System.out.println(msg + "离线了");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// e.printStackTrace();
} finally {
// TODO: handle finally clause
}
}
public static void sendToAll(String msg, SocketChannel ssc) {
for (SelectionKey ss : selector.keys()) {
// 通过 key 取出对应的 SocketChannel
SelectableChannel channel = ss.channel();
// 排除自己
if (channel instanceof SocketChannel && channel != ssc) {
// 转型
SocketChannel sh = (SocketChannel) channel;
// 转存到buffer
ByteBuffer wrap = ByteBuffer.wrap(msg.getBytes());
try {
// 写入通道
sh.write(wrap);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
GroupChatServer server = new GroupChatServer();
GroupChatServer.listen();
}
}
key.isAcceptable()进行接入 操作的时候, 获取通道有两种方式
1、 通过selector获取 (Selector key) socketChannel = (ServerSocketChannel) key.channel();
建立连接 socketChannel .accept();
2、定义一个全局变量
在进行初始化的时候,存储(socketChannel = ServerSocketChannel.open();)
建立连接 socketChannel .accept();
key.isReadable() 当进行到读入操作的时候( ) SelectionKey key accept = (SocketChannel) key.channel();
演示
服务器启动,客户端启动
客户端发送消息
启动第二个客户端
两个客户端相互通信
离线信息显示
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。