使用NIO技术编写一个简单的群聊技术来了解ServerSocketChannel,SocketChannel,Selector,SelectionKey等技术的使用技巧与方法。
服务器端的代码:
服务器端代码
package com.yu.nio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/**
* @author yucheng
* @create 2020-07-03 13:15
* 使用nio编写一个群聊系统,实现服务器端和客户端的数据通信,
* 服务器端的功能:接收客户端发送的信息,并且转发给其余的客户端
*/
public class GroupChatServerDemo {
//定义属性信息
private ServerSocketChannel serverSocketChannel; //在服务器端监听新的sockerChannnel连接
private Selector selector; //选择器
private static final Integer PORT = 8080; //监听的端口信息
/**
* 定义构造方法
*/
public GroupChatServerDemo(){
try {
serverSocketChannel = ServerSocketChannel.open();
selector = Selector.open();
serverSocketChannel.configureBlocking(false); //设置管道为非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(PORT));//设置端口信息
//把通道注册到selector上,事件为监听事件
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
System.out.println("服务器端准备好了");
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 定义监听客户端有连接请求的方法
*/
public void seleorSocketChannel(){
try {
//循环进行处理
while(true){
//如果连接大于0,有新的连接请求
if(selector.select() > 0){
Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator();
while (selectionKeyIterator.hasNext()) {
SelectionKey selectionKey = selectionKeyIterator.next();
//如果事件是监听事件,则把事件注册到选择器,事件为读
if(selectionKey.isAcceptable()){
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
//有新的用户上线
System.out.println("有新用户上线了!!!");
}
//如果事件是读的事件,把读取获取到的数据信息
if(selectionKey.isReadable()){
readData(selectionKey);
}
}
//移除使用过的selectionKeyIterator
selectionKeyIterator.remove();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 读取传输过来的数据
* @param selectionKey 注册关系
*/
private void readData(SelectionKey selectionKey) {
SocketChannel channel = null;
try {
channel = (SocketChannel)selectionKey.channel();//获取通道信息
//ByteBuffer buffer = (ByteBuffer)selectionKey.attachment();//获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//开始读取数据信息
if(channel.read(buffer) > 0){
String msg = new String(buffer.array());
System.out.println(channel.getLocalAddress().toString().substring(1)+":说" + msg);
//把消息转发给其他客户端
sendOtherClients(channel,msg);
}
}catch (Exception e){
try {
System.out.println(channel.getLocalAddress().toString().substring(1)+"离线了!!!");
selectionKey.cancel();//关闭selectionKey
channel.close();//关闭channel
} catch (Exception e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
/**
* 转发信息给塔器客户端,但是要排除自己
* @param channel 通道
* @param msg 发送的信息
*/
private void sendOtherClients(SocketChannel channel, String msg) {
//selector.keys()代表注册到选择器上的所有通道
//selector.selectedKeys()代表监听的时候注册到选择器上的所有通道
try {
Set<SelectionKey> keys = selector.keys();
for (SelectionKey key : keys) {
SelectableChannel selectableChannel = key.channel();
if(selectableChannel instanceof SocketChannel && selectableChannel != channel) {
SocketChannel socketChannel = (SocketChannel)selectableChannel;
ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
socketChannel.write(byteBuffer);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
GroupChatServerDemo groupChatServerDemo = new GroupChatServerDemo();
groupChatServerDemo.seleorSocketChannel();
}
}
|
客户端的代码:
package com.yu.nio;
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;
/**
* @author yucheng
* @create 2020-07-03 13:15
* 使用nio编写一个群聊系统,实现服务器端和客户端的数据通信,
*服务器端的功能:接收客户端发送的信息,并且转发给其余的客户端
*/
public class GroupChatClientDemo {
//定义属性信息
private SocketChannel socketChannel;//定义网络IO通道
private Selector selector;//定义选择器
private static final Integer PORT = 8080; //监听的端口信息
private final String HOST = "127.0.0.1";//定义ip地址
private String userName;//用户名
/**
* 定义构造方法
*/
public GroupChatClientDemo(){
try {
socketChannel = SocketChannel.open(new InetSocketAddress(HOST,PORT));
selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
userName = socketChannel.getLocalAddress().toString().substring(1);
System.out.println(userName + ":准备好了");
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 定义发送消息的方法
* @param msg 消息
*/
public void sendMsg(String msg){
try {
msg = userName + "说:" + msg;
ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
socketChannel.write(byteBuffer);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取服务器转发的消息
*/
public void readMsg(){
try {
//大于0代表有通道信息
if(selector.select() > 0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//获取读的操作
if(selectionKey.isReadable()){
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
socketChannel.read(byteBuffer);
System.out.println("服务器转发的信息为:" + new String(byteBuffer.array()));
}
}
iterator.remove();
}
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
final GroupChatClientDemo groupChatClientDemo = new GroupChatClientDemo();
//启动一个线程循环去读取服务器转发的信息
new Thread(new Runnable() {
public void run() {
while(true){
groupChatClientDemo.readMsg();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//调用发送信息的方法
Scanner scanner = new Scanner(System.in);
if(scanner.hasNextLine()){
groupChatClientDemo.sendMsg(scanner.nextLine());
}
}
}
|
运行效果:
