本篇博客实现:简易聊天室
package com.zcw.clink.box;
import com.zcw.clink.core.ReceivePacket;
import java.io.ByteArrayOutputStream;
/**
* @ClassName : AbsByteArrayReceivePacket
* @Description : 定义最基础的基于{@link ByteArrayOutputStream}的输出接收包
* @param 对应的实体范性,
* 需定义{@link ByteArrayOutputStream}流最终转化为什么数据实体
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:55
*/
public abstract class AbsByteArrayReceivePacket <Entity> extends ReceivePacket<ByteArrayOutputStream, Entity> {
public AbsByteArrayReceivePacket(long len) {
super(len);
}
/**
* 创建流操作直接返回一个{@link ByteArrayOutputStream}流
*
* @return {@link ByteArrayOutputStream}
*/
@Override
protected final ByteArrayOutputStream createStream() {
return new ByteArrayOutputStream((int) length);
}
}
package com.zcw.clink.box;
import java.io.ByteArrayOutputStream;
/**
* @ClassName : BytesReceivePacket
* @Description :纯Byte数组接收包
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:56
*/
public class BytesReceivePacket extends AbsByteArrayReceivePacket<byte[]>{
public BytesReceivePacket(long len) {
super(len);
}
@Override
public byte type() {
return TYPE_MEMORY_BYTES;
}
@Override
protected byte[] buildEntity(ByteArrayOutputStream stream) {
return stream.toByteArray();
}
}
package com.zcw.clink.box;
import com.zcw.clink.core.SendPacket;
import java.io.ByteArrayInputStream;
/**
* @ClassName : BytesSendPacket
* @Description : 纯Byte数组发送包
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:56
*/
public class BytesSendPacket extends SendPacket<ByteArrayInputStream> {
private final byte[] bytes;
public BytesSendPacket(byte[] bytes) {
this.bytes = bytes;
this.length = bytes.length;
}
@Override
public byte type() {
return TYPE_MEMORY_BYTES;
}
@Override
protected ByteArrayInputStream createStream() {
return new ByteArrayInputStream(bytes);
}
}
package com.zcw.clink.box;
import com.zcw.clink.core.ReceivePacket;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* @ClassName : FileReceivePacket
* @Description :文件接收包
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:57
*/
public class FileReceivePacket extends ReceivePacket<FileOutputStream, File> {
private File file;
public FileReceivePacket(long len, File file) {
super(len);
this.file = file;
}
@Override
public byte type() {
return TYPE_STREAM_FILE;
}
/**
* 从流转变为对应实体时直接返回创建时传入的File文件
*
* @param stream 文件传输流
* @return File
*/
@Override
protected File buildEntity(FileOutputStream stream) {
return file;
}
@Override
protected FileOutputStream createStream() {
try {
return new FileOutputStream(file);
} catch (FileNotFoundException e) {
return null;
}
}
}
package com.zcw.clink.box;
import com.zcw.clink.core.SendPacket;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* @ClassName : FileSendPacket
* @Description : 文件发送包
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:57
*/
public class FileSendPacket extends SendPacket<FileInputStream> {
private final File file;
public FileSendPacket(File file) {
this.file = file;
this.length = file.length();
}
@Override
public byte type() {
return TYPE_STREAM_FILE;
}
/**
* 使用File构建文件读取流,用以读取本地的文件数据进行发送
*
* @return 文件读取流
*/
@Override
protected FileInputStream createStream() {
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
return null;
}
}
}
package com.zcw.clink.box;
import java.io.ByteArrayOutputStream;
/**
* @ClassName : StringReceivePacket
* @Description : 字符串接收包
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:58
*/
public class StringReceivePacket extends AbsByteArrayReceivePacket<String> {
public StringReceivePacket(long len) {
super(len);
}
@Override
protected String buildEntity(ByteArrayOutputStream stream) {
return new String(stream.toByteArray());
}
@Override
public byte type() {
return TYPE_MEMORY_STRING;
}
}
package com.zcw.clink.box;
/**
* @ClassName : StringSendPacket
* @Description : 字符串发送包
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:58
*/
public class StringSendPacket extends BytesSendPacket{
/**
* 字符串发送时就是Byte数组,所以直接得到Byte数组,并按照Byte的发送方式发送即可
*
* @param msg 字符串
*/
public StringSendPacket(String msg) {
super(msg.getBytes());
}
@Override
public byte type() {
return TYPE_MEMORY_STRING;
}
}
package com.zcw.clink.core.ds;
/**
* @ClassName : BytePriorityNode
* @Description :带优先级的节点, 可用于构成链表
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:00
*/
public class BytePriorityNode<Object> {
public byte priority;
public Object item;
public BytePriorityNode<Object> next;
public BytePriorityNode(Object item) {
this.item = item;
}
/**
* 按优先级追加到当前链表中
*
* @param node Node
*/
public void appendWithPriority(BytePriorityNode<Object> node) {
if (next == null) {
next = node;
} else {
BytePriorityNode<Object> after = this.next;
if (after.priority < node.priority) {
// 中间位置插入
this.next = node;
node.next = after;
} else {
after.appendWithPriority(node);
}
}
}
}
package com.zcw.clink.core;
import com.zcw.clink.box.BytesReceivePacket;
import com.zcw.clink.box.FileReceivePacket;
import com.zcw.clink.box.StringReceivePacket;
import com.zcw.clink.box.StringSendPacket;
import com.zcw.clink.impl.SocketChannelAdapter;
import com.zcw.clink.impl.async.AsyncReceiveDispatcher;
import com.zcw.clink.impl.async.AsyncSendDispatcher;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.UUID;
/**
* @ClassName : Connector
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:59
*/
public abstract class Connector implements Closeable,
SocketChannelAdapter.OnChannelStatusChangedListener {
protected UUID key = UUID.randomUUID();
private SocketChannel channel;
private Sender sender;
private Receiver receiver;
private SendDispatcher sendDispatcher;
private ReceiveDispatcher receiveDispatcher;
public void setup(SocketChannel socketChannel) throws IOException {
this.channel = socketChannel;
IoContext context = IoContext.get();
SocketChannelAdapter adapter = new SocketChannelAdapter(channel, context.getIoProvider(), this);
this.sender = adapter;
this.receiver = adapter;
sendDispatcher = new AsyncSendDispatcher(sender);
receiveDispatcher = new AsyncReceiveDispatcher(receiver, receivePacketCallback);
// 启动接收
receiveDispatcher.start();
}
public void send(String msg) {
SendPacket packet = new StringSendPacket(msg);
sendDispatcher.send(packet);
}
public void send(SendPacket packet) {
sendDispatcher.send(packet);
}
@Override
public void close() throws IOException {
receiveDispatcher.close();
sendDispatcher.close();
sender.close();
receiver.close();
channel.close();
}
@Override
public void onChannelClosed(SocketChannel channel) {
}
protected void onReceivedPacket(ReceivePacket packet) {
System.out.println(key.toString() + ":[New Packet]-Type:" + packet.type() + ", Length:" + packet.length);
}
protected abstract File createNewReceiveFile();
private ReceiveDispatcher.ReceivePacketCallback receivePacketCallback = new ReceiveDispatcher.ReceivePacketCallback() {
@Override
public ReceivePacket<?, ?> onArrivedNewPacket(byte type, long length) {
switch (type) {
case Packet.TYPE_MEMORY_BYTES:
return new BytesReceivePacket(length);
case Packet.TYPE_MEMORY_STRING:
return new StringReceivePacket(length);
case Packet.TYPE_STREAM_FILE:
return new FileReceivePacket(length, createNewReceiveFile());
case Packet.TYPE_STREAM_DIRECT:
return new BytesReceivePacket(length);
default:
throw new UnsupportedOperationException("Unsupported packet type:" + type);
}
}
@Override
public void onReceivePacketCompleted(ReceivePacket packet) {
onReceivedPacket(packet);
}
};
}
package com.zcw.clink.core;
import java.io.IOException;
/**
* @ClassName : Frame
* @Description : 帧-分片使用
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:00
*/
public abstract class Frame {
// 帧头长度
public static final int FRAME_HEADER_LENGTH = 6;
// 单帧最大容量 64KB
public static final int MAX_CAPACITY = 64 * 1024 - 1;
// Packet头信息帧
public static final byte TYPE_PACKET_HEADER = 11;
// Packet数据分片信息帧
public static final byte TYPE_PACKET_ENTITY = 12;
// 指令-发送取消
public static final byte TYPE_COMMAND_SEND_CANCEL = 41;
// 指令-接受拒绝
public static final byte TYPE_COMMAND_RECEIVE_REJECT = 42;
// Flag标记
public static final byte FLAG_NONE = 0;
// 头部6字节固定
protected final byte[] header = new byte[FRAME_HEADER_LENGTH];
public Frame(int length, byte type, byte flag, short identifier) {
if (length < 0 || length > MAX_CAPACITY) {
throw new RuntimeException("The Body length of a single frame should be between 0 and " + MAX_CAPACITY);
}
if (identifier < 1 || identifier > 255) {
throw new RuntimeException("The Body identifier of a single frame should be between 1 and 255");
}
// 00000000 00000000 00000000 01000000
header[0] = (byte) (length >> 8);
header[1] = (byte) (length);
header[2] = type;
header[3] = flag;
header[4] = (byte) identifier;
header[5] = 0;
}
public Frame(byte[] header) {
System.arraycopy(header, 0, this.header, 0, FRAME_HEADER_LENGTH);
}
/**
* 获取Body的长度
*
* @return 当前帧Body总长度[0~MAX_CAPACITY]
*/
public int getBodyLength() {
// 00000000
// 01000000
// 00000000 00000000 00000000 01000000
// 01000000
// 11111111 11111111 11111111 01000000
// 00000000 00000000 00000000 11111111 0xFF
// 00000000 00000000 00000000 01000000
return ((((int) header[0]) & 0xFF) << 8) | (((int) header[1]) & 0xFF);
}
/**
* 获取Body的类型
*
* @return 类型[0~255]
*/
public byte getBodyType() {
return header[2];
}
/**
* 获取Body的Flag
*
* @return Flag
*/
public byte getBodyFlag() {
return header[3];
}
/**
* 获取Body的唯一标志
*
* @return 标志[0~255]
*/
public short getBodyIdentifier() {
return (short) (((short) header[4]) & 0xFF);
}
/**
* 进行数据读或写操作
*
* @param args 数据
* @return 是否已消费完全, True:则无需再传递数据到Frame或从当前Frame读取数据
*/
public abstract boolean handle(IoArgs args) throws IOException;
/**
* 基于当前帧尝试构建下一份待消费的帧
*
* @return NULL:没有待消费的帧
*/
public abstract Frame nextFrame();
// 64MB 64KB 1024+1 6
public abstract int getConsumableLength();
}
package com.zcw.clink.core;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
/**
* @ClassName : IoArgs
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:01
*/
@SuppressWarnings("Duplicates")
public class IoArgs {
private int limit = 256;
private ByteBuffer buffer = ByteBuffer.allocate(256);
/**
* 从bytes数组进行消费
*/
public int readFrom(byte[] bytes, int offset, int count) {
int size = Math.min(count, buffer.remaining());
if (size <= 0) {
return 0;
}
buffer.put(bytes, offset, size);
return size;
}
/**
* 写入数据到bytes中
*/
public int writeTo(byte[] bytes, int offset) {
int size = Math.min(bytes.length - offset, buffer.remaining());
buffer.get(bytes, offset, size);
return size;
}
/**
* 从bytes中读取数据
*/
public int readFrom(ReadableByteChannel channel) throws IOException {
int bytesProduced = 0;
while (buffer.hasRemaining()) {
int len = channel.read(buffer);
if (len < 0) {
throw new EOFException();
}
bytesProduced += len;
}
return bytesProduced;
}
/**
* 写入数据到bytes中
*/
public int writeTo(WritableByteChannel channel) throws IOException {
int bytesProduced = 0;
while (buffer.hasRemaining()) {
int len = channel.write(buffer);
if (len < 0) {
throw new EOFException();
}
bytesProduced += len;
}
return bytesProduced;
}
/**
* 从SocketChannel读取数据
*/
public int readFrom(SocketChannel channel) throws IOException {
startWriting();
int bytesProduced = 0;
while (buffer.hasRemaining()) {
int len = channel.read(buffer);
if (len < 0) {
throw new EOFException();
}
bytesProduced += len;
}
finishWriting();
return bytesProduced;
}
/**
* 写数据到SocketChannel
*/
public int writeTo(SocketChannel channel) throws IOException {
int bytesProduced = 0;
while (buffer.hasRemaining()) {
int len = channel.write(buffer);
if (len < 0) {
throw new EOFException();
}
bytesProduced += len;
}
return bytesProduced;
}
/**
* 开始写入数据到IoArgs
*/
public void startWriting() {
buffer.clear();
// 定义容纳区间
buffer.limit(limit);
}
/**
* 写完数据后调用
*/
public void finishWriting() {
buffer.flip();
}
/**
* 设置单次写操作的容纳区间
*
* @param limit 区间大小
*/
public void limit(int limit) {
this.limit = Math.min(limit, buffer.capacity());
}
public int readLength() {
return buffer.getInt();
}
public int capacity() {
return buffer.capacity();
}
public boolean remained() {
return buffer.remaining() > 0;
}
/**
* 填充数据
*
* @param size 想要填充数据的长度
* @return 真实填充数据的长度
*/
public int fillEmpty(int size) {
int fillSize = Math.min(size, buffer.remaining());
buffer.position(buffer.position() + fillSize);
return fillSize;
}
/**
* 清空部分数据
*
* @param size 想要清空的数据长度
* @return 真实清空的数据长度
*/
public int setEmpty(int size) {
int emptySize = Math.min(size, buffer.remaining());
buffer.position(buffer.position() + emptySize);
return emptySize;
}
/**
* IoArgs 提供者、处理者;数据的生产或消费者
*/
public interface IoArgsEventProcessor {
/**
* 提供一份可消费的IoArgs
*
* @return IoArgs
*/
IoArgs provideIoArgs();
/**
* 消费失败时回调
*
* @param args IoArgs
* @param e 异常信息
*/
void onConsumeFailed(IoArgs args, Exception e);
/**
* 消费成功
*
* @param args IoArgs
*/
void onConsumeCompleted(IoArgs args);
}
}
package com.zcw.clink.core;
import java.io.IOException;
/**
* @ClassName : IoContext
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:01
*/
public class IoContext {
private static IoContext INSTANCE;
private final IoProvider ioProvider;
private IoContext(IoProvider ioProvider) {
this.ioProvider = ioProvider;
}
public IoProvider getIoProvider() {
return ioProvider;
}
public static IoContext get() {
return INSTANCE;
}
public static StartedBoot setup() {
return new StartedBoot();
}
public static void close() throws IOException {
if (INSTANCE != null) {
INSTANCE.callClose();
}
}
private void callClose() throws IOException {
ioProvider.close();
}
public static class StartedBoot {
private IoProvider ioProvider;
private StartedBoot() {
}
public StartedBoot ioProvider(IoProvider ioProvider) {
this.ioProvider = ioProvider;
return this;
}
public IoContext start() {
INSTANCE = new IoContext(ioProvider);
return INSTANCE;
}
}
}
package com.zcw.clink.core;
import java.io.Closeable;
import java.nio.channels.SocketChannel;
public interface IoProvider extends Closeable {
boolean registerInput(SocketChannel channel, HandleInputCallback callback);
boolean registerOutput(SocketChannel channel, HandleOutputCallback callback);
void unRegisterInput(SocketChannel channel);
void unRegisterOutput(SocketChannel channel);
abstract class HandleInputCallback implements Runnable {
@Override
public final void run() {
canProviderInput();
}
protected abstract void canProviderInput();
}
abstract class HandleOutputCallback implements Runnable {
@Override
public final void run() {
canProviderOutput();
}
protected abstract void canProviderOutput();
}
}
package com.zcw.clink.core;
import java.io.Closeable;
import java.io.IOException;
/**
* @ClassName : Packet
* @Description : 公共的数据封装, 提供了类型以及基本的长度的定义
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:02
*/
public abstract class Packet<Stream extends Closeable> implements Closeable {
// BYTES 类型
public static final byte TYPE_MEMORY_BYTES = 1;
// String 类型
public static final byte TYPE_MEMORY_STRING = 2;
// 文件 类型
public static final byte TYPE_STREAM_FILE = 3;
// 长链接流 类型
public static final byte TYPE_STREAM_DIRECT = 4;
protected long length;
private Stream stream;
public long length() {
return length;
}
/**
* 对外的获取当前实例的流操作
*
* @return {@link java.io.InputStream} or {@link java.io.OutputStream}
*/
public final Stream open() {
if (stream == null) {
stream = createStream();
}
return stream;
}
/**
* 对外的关闭资源操作,如果流处于打开状态应当进行关闭
*
* @throws IOException IO异常
*/
@Override
public final void close() throws IOException {
if (stream != null) {
closeStream(stream);
stream = null;
}
}
/**
* 类型,直接通过方法得到:
*
* {@link #TYPE_MEMORY_BYTES}
* {@link #TYPE_MEMORY_STRING}
* {@link #TYPE_STREAM_FILE}
* {@link #TYPE_STREAM_DIRECT}
*
* @return 类型
*/
public abstract byte type();
/**
* 创建流操作,应当将当前需要传输的数据转化为流
*
* @return {@link java.io.InputStream} or {@link java.io.OutputStream}
*/
protected abstract Stream createStream();
/**
* 关闭流,当前方法会调用流的关闭操作
*
* @param stream 待关闭的流
* @throws IOException IO异常
*/
protected void closeStream(Stream stream) throws IOException {
stream.close();
}
/**
* 头部额外信息,用于携带额外的校验信息等
*
* @return byte 数组,最大255长度
*/
public byte[] headerInfo() {
return null;
}
}
package com.zcw.clink.core;
import java.io.Closeable;
/**
* 接收的数据调度封装
* 把一份或者多分IoArgs组合成一份Packet
*/
public interface ReceiveDispatcher extends Closeable {
void start();
void stop();
interface ReceivePacketCallback {
ReceivePacket<?, ?> onArrivedNewPacket(byte type, long length);
void onReceivePacketCompleted(ReceivePacket packet);
}
}
package com.zcw.clink.core;
import java.io.IOException;
import java.io.OutputStream;
/**
* @ClassName : ReceivePacket
* @Description : 接收包的定义
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:10
*/
public abstract class ReceivePacket<Stream extends OutputStream, Entity> extends Packet<Stream> {
// 定义当前接收包最终的实体
private Entity entity;
public ReceivePacket(long len) {
this.length = len;
}
/**
* 得到最终接收到的数据实体
*
* @return 数据实体
*/
public Entity entity() {
return entity;
}
/**
* 根据接收到的流转化为对应的实体
*
* @param stream {@link OutputStream}
* @return 实体
*/
protected abstract Entity buildEntity(Stream stream);
/**
* 先关闭流,随后将流的内容转化为对应的实体
*
* @param stream 待关闭的流
* @throws IOException IO异常
*/
@Override
protected final void closeStream(Stream stream) throws IOException {
super.closeStream(stream);
entity = buildEntity(stream);
}
}
package com.zcw.clink.core;
import java.io.Closeable;
import java.io.IOException;
public interface Receiver extends Closeable {
void setReceiveListener(IoArgs.IoArgsEventProcessor processor);
boolean postReceiveAsync() throws IOException;
}
package com.zcw.clink.core;
import java.io.Closeable;
/**
* 发送数据的调度者
* 缓存所有需要发送的数据,通过队列对数据进行发送
* 并且在发送数据时,实现对数据的基本包装
*/
public interface SendDispatcher extends Closeable {
/**
* 发送一份数据
*
* @param packet 数据
*/
void send(SendPacket packet);
/**
* 取消发送数据
*
* @param packet 数据
*/
void cancel(SendPacket packet);
}
package com.zcw.clink.core;
import java.io.Closeable;
import java.io.IOException;
public interface Sender extends Closeable {
void setSendListener(IoArgs.IoArgsEventProcessor processor);
boolean postSendAsync() throws IOException;
}
package com.zcw.clink.core;
import java.io.InputStream;
/**
* @ClassName : SendPacket
* @Description : 发送的包定义
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:12
*/
public abstract class SendPacket<T extends InputStream> extends Packet<T> {
private boolean isCanceled;
public boolean isCanceled() {
return isCanceled;
}
/**
* 设置取消发送标记
*/
public void cancel() {
isCanceled = true;
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
import java.io.IOException;
/**
* @ClassName : AbsReceiveFrame
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:13
*/
public abstract class AbsReceiveFrame extends Frame {
// 帧体可读写区域大小
volatile int bodyRemaining;
AbsReceiveFrame(byte[] header) {
super(header);
bodyRemaining = getBodyLength();
}
@Override
public synchronized boolean handle(IoArgs args) throws IOException {
if (bodyRemaining == 0) {
// 已读取所有数据
return true;
}
bodyRemaining -= consumeBody(args);
return bodyRemaining == 0;
}
@Override
public final Frame nextFrame() {
return null;
}
@Override
public int getConsumableLength() {
return bodyRemaining;
}
protected abstract int consumeBody(IoArgs args) throws IOException;
}
package com.zcw.clink.frames;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
import java.io.IOException;
/**
* @ClassName : AbsSendFrame
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:14
*/
public abstract class AbsSendFrame extends Frame {
// 帧头可读写区域大小
volatile byte headerRemaining = Frame.FRAME_HEADER_LENGTH;
// 帧体可读写区域大小
volatile int bodyRemaining;
public AbsSendFrame(int length, byte type, byte flag, short identifier) {
super(length, type, flag, identifier);
bodyRemaining = length;
}
@Override
public synchronized boolean handle(IoArgs args) throws IOException {
try {
args.limit(headerRemaining + bodyRemaining);
args.startWriting();
if (headerRemaining > 0 && args.remained()) {
headerRemaining -= consumeHeader(args);
}
if (headerRemaining == 0 && args.remained() && bodyRemaining > 0) {
bodyRemaining -= consumeBody(args);
}
return headerRemaining == 0 && bodyRemaining == 0;
} finally {
args.finishWriting();
}
}
@Override
public int getConsumableLength() {
return headerRemaining + bodyRemaining;
}
private byte consumeHeader(IoArgs args) {
int count = headerRemaining;
int offset = header.length - count;
return (byte) args.readFrom(header, offset, count);
}
protected abstract int consumeBody(IoArgs args) throws IOException;
/**
* 是否已经处于发送数据中,如果已经发送了部分数据则返回True
* 只要头部数据已经开始消费,则肯定已经处于发送数据中
*
* @return True,已发送部分数据
*/
protected synchronized boolean isSending() {
return headerRemaining < Frame.FRAME_HEADER_LENGTH;
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.SendPacket;
import java.io.IOException;
/**
* @ClassName : AbsSendPacketFrame
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:14
*/
public abstract class AbsSendPacketFrame extends AbsSendFrame {
protected volatile SendPacket<?> packet;
public AbsSendPacketFrame(int length, byte type, byte flag, short identifier, SendPacket packet) {
super(length, type, flag, identifier);
this.packet = packet;
}
/**
* 获取当前对应的发送Packet
*
* @return SendPacket
*/
public synchronized SendPacket getPacket() {
return packet;
}
@Override
public synchronized boolean handle(IoArgs args) throws IOException {
if (packet == null && !isSending()) {
// 已取消,并且未发送任何数据,直接返回结束,发送下一帧
return true;
}
return super.handle(args);
}
/**
* 构建下一帧时做一次判断,如果已经终止,则没有下一帧;
* 如果没有则尝试进行构建操作
*
* @return 下一帧
*/
@Override
public final synchronized Frame nextFrame() {
return packet == null ? null : buildNextFrame();
}
/**
* 终止当前帧
* 需要在当前方法中做一些操作,以及状态的维护
* 后续可以扩展{@link #fillDirtyDataOnAbort()}方法对数据进行填充操作
*
* @return True:完美终止,可以顺利的移除当前帧;False:已发送部分数据
*/
public final synchronized boolean abort() {
// True, 当前帧没有发送任何数据
// 1234, 12,34
boolean isSending = isSending();
if (isSending) {
fillDirtyDataOnAbort();
}
packet = null;
return !isSending;
}
protected void fillDirtyDataOnAbort() {
}
/**
* 构建下一帧
*
* @return NULL:没有下一帧
*/
protected abstract Frame buildNextFrame();
}
package com.zcw.clink.frames;
import com.zcw.clink.core.IoArgs;
import java.io.IOException;
/**
* @ClassName : CancelReceiveFrame
* @Description : 取消传输帧,接收实现
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:15
*/
public class CancelReceiveFrame extends AbsReceiveFrame {
CancelReceiveFrame(byte[] header) {
super(header);
}
@Override
protected int consumeBody(IoArgs args) throws IOException {
return 0;
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
/**
* @ClassName : CancelSendFrame
* @Description : 取消发送帧,用于标志某Packet取消进行发送数据
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:15
*/
public class CancelSendFrame extends AbsSendFrame {
public CancelSendFrame(short identifier) {
super(0, Frame.TYPE_COMMAND_SEND_CANCEL, Frame.FLAG_NONE, identifier);
}
@Override
protected int consumeBody(IoArgs args) {
return 0;
}
@Override
public Frame nextFrame() {
return null;
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.IoArgs;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
/**
* @ClassName : ReceiveEntityFrame
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:17
*/
public class ReceiveEntityFrame extends AbsReceiveFrame {
private WritableByteChannel channel;
ReceiveEntityFrame(byte[] header) {
super(header);
}
public void bindPacketChannel(WritableByteChannel channel) {
this.channel = channel;
}
@Override
protected int consumeBody(IoArgs args) throws IOException {
return channel == null ? args.setEmpty(bodyRemaining) : args.writeTo(channel);
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
/**
* @ClassName : ReceiveFrameFactory
* @Description : 接收帧构建工厂
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:21
*/
public class ReceiveFrameFactory {
/**
* 使用传入的帧头数据构建接收帧
*
* @param args IoArgs至少需要有6字节数据可读
* @return 构建的帧头数据
*/
public static AbsReceiveFrame createInstance(IoArgs args) {
byte[] buffer = new byte[Frame.FRAME_HEADER_LENGTH];
args.writeTo(buffer, 0);
byte type = buffer[2];
switch (type) {
case Frame.TYPE_COMMAND_SEND_CANCEL:
return new CancelReceiveFrame(buffer);
case Frame.TYPE_PACKET_HEADER:
return new ReceiveHeaderFrame(buffer);
case Frame.TYPE_PACKET_ENTITY:
return new ReceiveEntityFrame(buffer);
default:
throw new UnsupportedOperationException("Unsupported frame type:" + type);
}
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.IoArgs;
/**
* @ClassName : ReceiveHeaderFrame
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:22
*/
public class ReceiveHeaderFrame extends AbsReceiveFrame {
private final byte[] body;
ReceiveHeaderFrame(byte[] header) {
super(header);
body = new byte[bodyRemaining];
}
@Override
protected int consumeBody(IoArgs args) {
int offset = body.length - bodyRemaining;
return args.writeTo(body, offset);
}
public long getPacketLength() {
return ((((long) body[0]) & 0xFFL) << 32)
| ((((long) body[1]) & 0xFFL) << 24)
| ((((long) body[2]) & 0xFFL) << 16)
| ((((long) body[3]) & 0xFFL) << 8)
| (((long) body[4]) & 0xFFL);
}
public byte getPacketType() {
return body[5];
}
public byte[] getPacketHeaderInfo() {
if (body.length > SendHeaderFrame.PACKET_HEADER_FRAME_MIN_LENGTH) {
byte[] headerInfo = new byte[body.length - SendHeaderFrame.PACKET_HEADER_FRAME_MIN_LENGTH];
System.arraycopy(body, SendHeaderFrame.PACKET_HEADER_FRAME_MIN_LENGTH,
headerInfo, 0, headerInfo.length);
return headerInfo;
}
return null;
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.SendPacket;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
/**
* @ClassName : SendEntityFrame
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:22
*/
public class SendEntityFrame extends AbsSendPacketFrame {
private final ReadableByteChannel channel;
private final long unConsumeEntityLength;
SendEntityFrame(short identifier,
long entityLength,
ReadableByteChannel channel,
SendPacket packet) {
super((int) Math.min(entityLength, Frame.MAX_CAPACITY),
Frame.TYPE_PACKET_ENTITY,
Frame.FLAG_NONE,
identifier,
packet);
// 1234567890
// 1234 5678 90
// 10 4,6 4,2 2
this.unConsumeEntityLength = entityLength - bodyRemaining;
this.channel = channel;
}
@Override
protected int consumeBody(IoArgs args) throws IOException {
if (packet == null) {
// 已终止当前帧,则填充假数据
return args.fillEmpty(bodyRemaining);
}
return args.readFrom(channel);
}
@Override
public Frame buildNextFrame() {
if (unConsumeEntityLength == 0) {
return null;
}
// 将未消费的长度用于构建下一帧
return new SendEntityFrame(getBodyIdentifier(),
unConsumeEntityLength, channel, packet);
}
}
package com.zcw.clink.frames;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.SendPacket;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
/**
* @ClassName : SendHeaderFrame
* @Description :Packet头帧
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:23
*/
public class SendHeaderFrame extends AbsSendPacketFrame {
static final int PACKET_HEADER_FRAME_MIN_LENGTH = 6;
private final byte[] body;
public SendHeaderFrame(short identifier, SendPacket packet) {
super(PACKET_HEADER_FRAME_MIN_LENGTH,
Frame.TYPE_PACKET_HEADER,
Frame.FLAG_NONE,
identifier,
packet);
final long packetLength = packet.length();
final byte packetType = packet.type();
final byte[] packetHeaderInfo = packet.headerInfo();
// 头部对应的数据信息长度
body = new byte[bodyRemaining];
// 头5字节存储长度信息低5字节(40位)数据
// 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
body[0] = (byte) (packetLength >> 32);
body[1] = (byte) (packetLength >> 24);
body[2] = (byte) (packetLength >> 16);
body[3] = (byte) (packetLength >> 8);
body[4] = (byte) (packetLength);
body[5] = packetType;
if (packetHeaderInfo != null) {
System.arraycopy(packetHeaderInfo, 0,
body, PACKET_HEADER_FRAME_MIN_LENGTH, packetHeaderInfo.length);
}
}
@Override
protected int consumeBody(IoArgs args) throws IOException {
int count = bodyRemaining;
int offset = body.length - count;
return args.readFrom(body, offset, count);
}
@Override
public Frame buildNextFrame() {
InputStream stream = packet.open();
ReadableByteChannel channel = Channels.newChannel(stream);
return new SendEntityFrame(getBodyIdentifier(),
packet.length(), channel, packet);
}
}
package com.zcw.clink.impl.async;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.SendPacket;
import com.zcw.clink.core.ds.BytePriorityNode;
import com.zcw.clink.frames.AbsSendPacketFrame;
import com.zcw.clink.frames.CancelSendFrame;
import com.zcw.clink.frames.SendEntityFrame;
import com.zcw.clink.frames.SendHeaderFrame;
import java.io.Closeable;
import java.io.IOException;
/**
* @ClassName : AsyncPacketReader
* @Description : Packet转换为帧序列,并进行读取发送的封装管理类
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:26
*/
public class AsyncPacketReader implements Closeable {
private final PacketProvider provider;
private volatile IoArgs args = new IoArgs();
// Frame队列
private volatile BytePriorityNode<Frame> node;
private volatile int nodeSize = 0;
// 1,2,3.....255
private short lastIdentifier = 0;
AsyncPacketReader(PacketProvider provider) {
this.provider = provider;
}
/**
* 请求从 {@link #provider}队列中拿一份Packet进行发送
*
* @return 如果当前Reader中有可以用于网络发送的数据,则返回True
*/
boolean requestTakePacket() {
synchronized (this) {
if (nodeSize >= 1) {
return true;
}
}
SendPacket packet = provider.takePacket();
if (packet != null) {
short identifier = generateIdentifier();
SendHeaderFrame frame = new SendHeaderFrame(identifier, packet);
appendNewFrame(frame);
}
synchronized (this) {
return nodeSize != 0;
}
}
/**
* 填充数据到IoArgs中
*
* @return 如果当前有可用于发送的帧,则填充数据并返回,如果填充失败可返回null
*/
IoArgs fillData() {
Frame currentFrame = gerCurrentFrame();
if (currentFrame == null) {
return null;
}
try {
if (currentFrame.handle(args)) {
// 消费完本帧
// 尝试基于本帧构建后续帧
Frame nextFrame = currentFrame.nextFrame();
if (nextFrame != null) {
appendNewFrame(nextFrame);
} else if (currentFrame instanceof SendEntityFrame) {
// 末尾实体帧
// 通知完成
provider.completedPacket(((SendEntityFrame) currentFrame).getPacket(),
true);
}
// 从链头弹出
popCurrentFrame();
}
return args;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 取消Packet对应的帧发送,如果当前Packet已发送部分数据(就算只是头数据)
* 也应该在当前帧队列中发送一份取消发送的标志{@link CancelSendFrame}
*
* @param packet 待取消的packet
*/
synchronized void cancel(SendPacket packet) {
if (nodeSize == 0) {
return;
}
for (BytePriorityNode<Frame> x = node, before = null; x != null; before = x, x = x.next) {
Frame frame = x.item;
if (frame instanceof AbsSendPacketFrame) {
AbsSendPacketFrame packetFrame = (AbsSendPacketFrame) frame;
if (packetFrame.getPacket() == packet) {
boolean removable = packetFrame.abort();
if (removable) {
// A B C
removeFrame(x, before);
if (packetFrame instanceof SendHeaderFrame) {
// 头帧,并且未被发送任何数据,直接取消后不需要添加取消发送帧
break;
}
}
// 添加终止帧,通知到接收方
CancelSendFrame cancelSendFrame = new CancelSendFrame(packetFrame.getBodyIdentifier());
appendNewFrame(cancelSendFrame);
// 意外终止,返回失败
provider.completedPacket(packet, false);
break;
}
}
}
}
/**
* 关闭当前Reader,关闭时应关闭所有的Frame对应的Packet
*/
@Override
public synchronized void close() {
while (node != null) {
Frame frame = node.item;
if (frame instanceof AbsSendPacketFrame) {
SendPacket packet = ((AbsSendPacketFrame) frame).getPacket();
provider.completedPacket(packet, false);
}
}
nodeSize = 0;
node = null;
}
/**
* 添加新的帧
*
* @param frame 新帧
*/
private synchronized void appendNewFrame(Frame frame) {
BytePriorityNode<Frame> newNode = new BytePriorityNode<>(frame);
if (node != null) {
// 使用优先级别添加到链表
node.appendWithPriority(newNode);
} else {
node = newNode;
}
nodeSize++;
}
/**
* 获取当前链表头的帧
*
* @return Frame
*/
private synchronized Frame gerCurrentFrame() {
if (node == null) {
return null;
}
return node.item;
}
/**
* 弹出链表头的帧
*/
private synchronized void popCurrentFrame() {
node = node.next;
nodeSize--;
if (node == null) {
requestTakePacket();
}
}
/**
* 删除某帧对应的链表节点
*
* @param removeNode 待删除的节点
* @param before 当前删除节点的前一个节点,用于构建新的链表结构
*/
private synchronized void removeFrame(BytePriorityNode<Frame> removeNode, BytePriorityNode<Frame> before) {
if (before == null) {
// A B C
// B C
node = removeNode.next;
} else {
// A B C
// A C
before.next = removeNode.next;
}
nodeSize--;
if (node == null) {
requestTakePacket();
}
}
/**
* 构建一份Packet惟一标志
*
* @return 标志为:1~255
*/
private short generateIdentifier() {
short identifier = ++lastIdentifier;
if (identifier == 255) {
lastIdentifier = 0;
}
return identifier;
}
/**
* Packet提供者
*/
interface PacketProvider {
/**
* 拿Packet操作
*
* @return 如果队列有可以发送的Packet则返回不为null
*/
SendPacket takePacket();
/**
* 结束一份Packet
*
* @param packet 发送包
* @param isSucceed 是否成功发送完成
*/
void completedPacket(SendPacket packet, boolean isSucceed);
}
}
package com.zcw.clink.impl.async;
import com.zcw.clink.core.Frame;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.ReceivePacket;
import com.zcw.clink.frames.*;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.Collection;
import java.util.HashMap;
/**
* @ClassName : AsyncPacketWriter
* @Description : 写数据到Packet中
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:27
*/
class AsyncPacketWriter implements Closeable {
private final PacketProvider provider;
private final HashMap<Short, PacketModel> packetMap = new HashMap<>();
private final IoArgs args = new IoArgs();
private volatile Frame frameTemp;
AsyncPacketWriter(PacketProvider provider) {
this.provider = provider;
}
/**
* 构建一份数据容纳封装
* 当前帧如果没有则返回至少6字节长度的IoArgs,
* 如果当前帧有,则返回当前帧未消费完成的区间
*
* @return IoArgs
*/
synchronized IoArgs takeIoArgs() {
args.limit(frameTemp == null
? Frame.FRAME_HEADER_LENGTH
: frameTemp.getConsumableLength());
return args;
}
/**
* 消费IoArgs中的数据
*
* @param args IoArgs
*/
synchronized void consumeIoArgs(IoArgs args) {
if (frameTemp == null) {
Frame temp;
do {
// 还有未消费数据,则重复构建帧
temp = buildNewFrame(args);
} while (temp == null && args.remained());
if (temp == null) {
// 最终消费数据完成,但没有可消费区间,则直接返回
return;
}
frameTemp = temp;
if (!args.remained()) {
// 没有数据,则直接返回
return;
}
}
// 确保此时currentFrame一定不为null
Frame currentFrame = frameTemp;
do {
try {
if (currentFrame.handle(args)) {
// 某帧已接收完成
if (currentFrame instanceof ReceiveHeaderFrame) {
// Packet 头帧消费完成,则根据头帧信息构建接收的Packet
ReceiveHeaderFrame headerFrame = (ReceiveHeaderFrame) currentFrame;
ReceivePacket packet = provider.takePacket(headerFrame.getPacketType(),
headerFrame.getPacketLength(),
headerFrame.getPacketHeaderInfo());
appendNewPacket(headerFrame.getBodyIdentifier(), packet);
} else if (currentFrame instanceof ReceiveEntityFrame) {
// Packet 实体帧消费完成,则将当前帧消费到Packet
completeEntityFrame((ReceiveEntityFrame) currentFrame);
}
// 接收完成后,直接推出循环,如果还有未消费数据则交给外层调度
frameTemp = null;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
} while (args.remained());
}
/**
* 根据args创建新的帧
* 若当前解析的帧是取消帧,则直接进行取消操作,并返回null
*
* @param args IoArgs
* @return 返回新的帧
*/
private Frame buildNewFrame(IoArgs args) {
AbsReceiveFrame frame = ReceiveFrameFactory.createInstance(args);
if (frame instanceof CancelReceiveFrame) {
cancelReceivePacket(frame.getBodyIdentifier());
return null;
} else if (frame instanceof ReceiveEntityFrame) {
WritableByteChannel channel = getPacketChannel(frame.getBodyIdentifier());
((ReceiveEntityFrame) frame).bindPacketChannel(channel);
}
return frame;
}
/**
* 当某Packet实体帧消费完成时调用
*
* @param frame 帧信息
*/
private void completeEntityFrame(ReceiveEntityFrame frame) {
synchronized (packetMap) {
short identifier = frame.getBodyIdentifier();
int length = frame.getBodyLength();
PacketModel model = packetMap.get(identifier);
model.unreceivedLength -= length;
if (model.unreceivedLength <= 0) {
provider.completedPacket(model.packet, true);
packetMap.remove(identifier);
}
}
}
/**
* 添加一个新的Packet到当前缓冲区
*
* @param identifier Packet标志
* @param packet Packet
*/
private void appendNewPacket(short identifier, ReceivePacket packet) {
synchronized (packetMap) {
PacketModel model = new PacketModel(packet);
packetMap.put(identifier, model);
}
}
/**
* 获取Packet对应的输出通道,用以设置给帧进行数据传输
* 因为关闭当前map的原因,可能存在返回NULL
*
* @param identifier Packet对应的标志
* @return 通道
*/
private WritableByteChannel getPacketChannel(short identifier) {
synchronized (packetMap) {
PacketModel model = packetMap.get(identifier);
return model == null ? null : model.channel;
}
}
/**
* 取消某Packet继续接收数据
*
* @param identifier Packet标志
*/
private void cancelReceivePacket(short identifier) {
synchronized (packetMap) {
PacketModel model = packetMap.get(identifier);
if (model != null) {
ReceivePacket packet = model.packet;
provider.completedPacket(packet, false);
}
}
}
/**
* 关闭操作,关闭时若当前还有正在接收的Packet,则尝试停止对应的Packet接收
*/
@Override
public void close() {
synchronized (packetMap) {
Collection<PacketModel> values = packetMap.values();
for (PacketModel value : values) {
provider.completedPacket(value.packet, false);
}
packetMap.clear();
}
}
/**
* Packet提供者
*/
interface PacketProvider {
/**
* 拿Packet操作
*
* @param type Packet类型
* @param length Packet长度
* @param headerInfo Packet headerInfo
* @return 通过类型,长度,描述等信息得到一份接收Packet
*/
ReceivePacket takePacket(byte type, long length, byte[] headerInfo);
/**
* 结束一份Packet
*
* @param packet 接收包
* @param isSucceed 是否成功接收完成
*/
void completedPacket(ReceivePacket packet, boolean isSucceed);
}
/**
* 对于接收包的简单封装
* 用以提供Packet、通道、未接收数据长度信息存储
*/
static class PacketModel {
final ReceivePacket packet;
final WritableByteChannel channel;
volatile long unreceivedLength;
PacketModel(ReceivePacket<?, ?> packet) {
this.packet = packet;
this.channel = Channels.newChannel(packet.open());
this.unreceivedLength = packet.length();
}
}
}
package com.zcw.clink.impl.async;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.ReceiveDispatcher;
import com.zcw.clink.core.ReceivePacket;
import com.zcw.clink.core.Receiver;
import com.zcw.clink.utils.CloseUtils;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @ClassName : AsyncReceiveDispatcher
* @Description : 接收调度
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:29
*/
public class AsyncReceiveDispatcher implements ReceiveDispatcher,
IoArgs.IoArgsEventProcessor, AsyncPacketWriter.PacketProvider {
private final AtomicBoolean isClosed = new AtomicBoolean(false);
private final Receiver receiver;
private final ReceivePacketCallback callback;
private AsyncPacketWriter writer = new AsyncPacketWriter(this);
public AsyncReceiveDispatcher(Receiver receiver, ReceivePacketCallback callback) {
this.receiver = receiver;
this.receiver.setReceiveListener(this);
this.callback = callback;
}
/**
* 开始进入接收方法
*/
@Override
public void start() {
registerReceive();
}
/**
* 停止接收数据
*/
@Override
public void stop() {
}
/**
* 关闭操作,关闭相关流
*/
@Override
public void close() {
if (isClosed.compareAndSet(false, true)) {
writer.close();
}
}
/**
* 自主发起的关闭操作,并且需要进行通知
*/
private void closeAndNotify() {
CloseUtils.close(this);
}
/**
* 注册接收数据
*/
private void registerReceive() {
try {
receiver.postReceiveAsync();
} catch (IOException e) {
closeAndNotify();
}
}
/**
* 网络接收就绪,此时可以读取数据,需要返回一个容器用于容纳数据
*
* @return 用以容纳数据的IoArgs
*/
@Override
public IoArgs provideIoArgs() {
return writer.takeIoArgs();
}
/**
* 接收数据失败
*
* @param args IoArgs
* @param e 异常信息
*/
@Override
public void onConsumeFailed(IoArgs args, Exception e) {
e.printStackTrace();
}
/**
* 接收数据成功
*
* @param args IoArgs
*/
@Override
public void onConsumeCompleted(IoArgs args) {
// 有数据则重复消费
do {
writer.consumeIoArgs(args);
} while (args.remained());
registerReceive();
}
/**
* 构建Packet操作,根据类型、长度构建一份用于接收数据的Packet
*/
@Override
public ReceivePacket takePacket(byte type, long length, byte[] headerInfo) {
return callback.onArrivedNewPacket(type, length);
}
/**
* 当Packet接收数据完成或终止时回调
*
* @param packet 接收包
* @param isSucceed 是否成功接收完成
*/
@Override
public void completedPacket(ReceivePacket packet, boolean isSucceed) {
CloseUtils.close(packet);
callback.onReceivePacketCompleted(packet);
}
}
package com.zcw.clink.impl.async;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.SendDispatcher;
import com.zcw.clink.core.SendPacket;
import com.zcw.clink.core.Sender;
import com.zcw.clink.utils.CloseUtils;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @ClassName : AsyncSendDispatcher
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:29
*/
public class AsyncSendDispatcher implements SendDispatcher,
IoArgs.IoArgsEventProcessor, AsyncPacketReader.PacketProvider {
private final Sender sender;
private final Queue<SendPacket> queue = new ConcurrentLinkedQueue<>();
private final AtomicBoolean isSending = new AtomicBoolean();
private final AtomicBoolean isClosed = new AtomicBoolean(false);
private final AsyncPacketReader reader = new AsyncPacketReader(this);
private final Object queueLock = new Object();
public AsyncSendDispatcher(Sender sender) {
this.sender = sender;
sender.setSendListener(this);
}
/**
* 发送Packet
* 首先添加到队列,如果当前状态为未启动发送状态
* 则,尝试让reader提取一份packet进行数据发送
*
* 如果提取数据后reader有数据,则进行异步输出注册
*
* @param packet 数据
*/
@Override
public void send(SendPacket packet) {
synchronized (queueLock) {
queue.offer(packet);
if (isSending.compareAndSet(false, true)) {
if (reader.requestTakePacket()) {
requestSend();
}
}
}
}
/**
* 取消Packet操作
* 如果还在队列中,代表Packet未进行发送,则直接标志取消,并返回即可
* 如果未在队列中,则让reader尝试扫描当前发送序列,查询是否当前Packet正在发送
* 如果是则进行取消相关操作
*
* @param packet 数据
*/
@Override
public void cancel(SendPacket packet) {
boolean ret;
synchronized (queueLock) {
ret = queue.remove(packet);
}
if (ret) {
packet.cancel();
return;
}
reader.cancel(packet);
}
/**
* reader从当前队列中提取一份Packet
*
* @return 如果队列有可用于发送的数据则返回该Packet
*/
@Override
public SendPacket takePacket() {
SendPacket packet;
synchronized (queueLock) {
packet = queue.poll();
if (packet == null) {
// 队列为空,取消发送状态
isSending.set(false);
return null;
}
}
if (packet.isCanceled()) {
// 已取消,不用发送
return takePacket();
}
return packet;
}
/**
* 完成Packet发送
*
* @param isSucceed 是否成功
*/
@Override
public void completedPacket(SendPacket packet, boolean isSucceed) {
CloseUtils.close(packet);
}
/**
* 请求网络进行数据发送
*/
private void requestSend() {
try {
sender.postSendAsync();
} catch (IOException e) {
closeAndNotify();
}
}
/**
* 请求网络发送异常时触发,进行关闭操作
*/
private void closeAndNotify() {
CloseUtils.close(this);
}
/**
* 关闭操作,关闭自己同时需要关闭reader
*
* @throws IOException 异常
*/
@Override
public void close() throws IOException {
if (isClosed.compareAndSet(false, true)) {
isSending.set(false);
// reader关闭
reader.close();
}
}
/**
* 网络发送就绪回调,当前已进入发送就绪状态,等待填充数据进行发送
* 此时从reader中填充数据,并进行后续网络发送
*
* @return NULL,可能填充异常,或者想要取消本次发送
*/
@Override
public IoArgs provideIoArgs() {
return reader.fillData();
}
/**
* 网络发送IoArgs出现异常
*
* @param args IoArgs
* @param e 异常信息
*/
@Override
public void onConsumeFailed(IoArgs args, Exception e) {
if (args != null) {
e.printStackTrace();
} else {
// TODO
}
}
/**
* 网络发送IoArgs完成回调
* 在该方法进行reader对当前队列的Packet提取,并进行后续的数据发送注册
*
* @param args IoArgs
*/
@Override
public void onConsumeCompleted(IoArgs args) {
// 继续发送当前包
if (reader.requestTakePacket()) {
requestSend();
}
}
}
package com.zcw.clink.impl;
import com.zcw.clink.core.IoProvider;
import com.zcw.clink.utils.CloseUtils;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Map;
import java.util.Set;
/**
* @ClassName : IoSelectorProvider
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:24
*/
public class IoSelectorProvider implements IoProvider {
private final AtomicBoolean isClosed = new AtomicBoolean(false);
// 是否处于某个过程
private final AtomicBoolean inRegInput = new AtomicBoolean(false);
private final AtomicBoolean inRegOutput = new AtomicBoolean(false);
private final Selector readSelector;
private final Selector writeSelector;
private final HashMap<SelectionKey, Runnable> inputCallbackMap = new HashMap<>();
private final HashMap<SelectionKey, Runnable> outputCallbackMap = new HashMap<>();
private final ExecutorService inputHandlePool;
private final ExecutorService outputHandlePool;
public IoSelectorProvider() throws IOException {
readSelector = Selector.open();
writeSelector = Selector.open();
inputHandlePool = Executors.newFixedThreadPool(4,
new IoProviderThreadFactory("IoProvider-Input-Thread-"));
outputHandlePool = Executors.newFixedThreadPool(4,
new IoProviderThreadFactory("IoProvider-Output-Thread-"));
// 开始输出输入的监听
startRead();
startWrite();
}
private void startRead() {
Thread thread = new Thread("Clink IoSelectorProvider ReadSelector Thread") {
@Override
public void run() {
while (!isClosed.get()) {
try {
if (readSelector.select() == 0) {
waitSelection(inRegInput);
continue;
}
Set<SelectionKey> selectionKeys = readSelector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys) {
if (selectionKey.isValid()) {
handleSelection(selectionKey, SelectionKey.OP_READ, inputCallbackMap, inputHandlePool);
}
}
selectionKeys.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
private void startWrite() {
Thread thread = new Thread("Clink IoSelectorProvider WriteSelector Thread") {
@Override
public void run() {
while (!isClosed.get()) {
try {
if (writeSelector.select() == 0) {
waitSelection(inRegOutput);
continue;
}
Set<SelectionKey> selectionKeys = writeSelector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys) {
if (selectionKey.isValid()) {
handleSelection(selectionKey, SelectionKey.OP_WRITE, outputCallbackMap, outputHandlePool);
}
}
selectionKeys.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
@Override
public boolean registerInput(SocketChannel channel, HandleInputCallback callback) {
return registerSelection(channel, readSelector, SelectionKey.OP_READ, inRegInput,
inputCallbackMap, callback) != null;
}
@Override
public boolean registerOutput(SocketChannel channel, HandleOutputCallback callback) {
return registerSelection(channel, writeSelector, SelectionKey.OP_WRITE, inRegOutput,
outputCallbackMap, callback) != null;
}
@Override
public void unRegisterInput(SocketChannel channel) {
unRegisterSelection(channel, readSelector, inputCallbackMap);
}
@Override
public void unRegisterOutput(SocketChannel channel) {
unRegisterSelection(channel, writeSelector, outputCallbackMap);
}
@Override
public void close() {
if (isClosed.compareAndSet(false, true)) {
inputHandlePool.shutdown();
outputHandlePool.shutdown();
inputCallbackMap.clear();
outputCallbackMap.clear();
readSelector.wakeup();
writeSelector.wakeup();
CloseUtils.close(readSelector, writeSelector);
}
}
private static void waitSelection(final AtomicBoolean locker) {
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (locker) {
if (locker.get()) {
try {
locker.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private static SelectionKey registerSelection(SocketChannel channel, Selector selector,
int registerOps, AtomicBoolean locker,
HashMap<SelectionKey, Runnable> map,
Runnable runnable) {
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (locker) {
// 设置锁定状态
locker.set(true);
try {
// 唤醒当前的selector,让selector不处于select()状态
selector.wakeup();
SelectionKey key = null;
if (channel.isRegistered()) {
// 查询是否已经注册过
key = channel.keyFor(selector);
if (key != null) {
key.interestOps(key.readyOps() | registerOps);
}
}
if (key == null) {
// 注册selector得到Key
key = channel.register(selector, registerOps);
// 注册回调
map.put(key, runnable);
}
return key;
} catch (ClosedChannelException e) {
return null;
} finally {
// 解除锁定状态
locker.set(false);
try {
// 通知
locker.notify();
} catch (Exception ignored) {
}
}
}
}
private static void unRegisterSelection(SocketChannel channel, Selector selector,
Map<SelectionKey, Runnable> map) {
if (channel.isRegistered()) {
SelectionKey key = channel.keyFor(selector);
if (key != null) {
// 取消监听的方法
key.cancel();
map.remove(key);
selector.wakeup();
}
}
}
private static void handleSelection(SelectionKey key, int keyOps,
HashMap<SelectionKey, Runnable> map,
ExecutorService pool) {
// 重点
// 取消继续对keyOps的监听
key.interestOps(key.readyOps() & ~keyOps);
Runnable runnable = null;
try {
runnable = map.get(key);
} catch (Exception ignored) {
}
if (runnable != null && !pool.isShutdown()) {
// 异步调度
pool.execute(runnable);
}
}
static class IoProviderThreadFactory implements ThreadFactory {
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
IoProviderThreadFactory(String namePrefix) {
SecurityManager s = System.getSecurityManager();
this.group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
this.namePrefix = namePrefix;
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
}
package com.zcw.clink.impl;
import com.zcw.clink.core.IoArgs;
import com.zcw.clink.core.IoProvider;
import com.zcw.clink.core.Receiver;
import com.zcw.clink.core.Sender;
import com.zcw.clink.utils.CloseUtils;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @ClassName : SocketChannelAdapter
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 14:26
*/
public class SocketChannelAdapter implements Sender, Receiver, Cloneable {
private final AtomicBoolean isClosed = new AtomicBoolean(false);
private final SocketChannel channel;
private final IoProvider ioProvider;
private final OnChannelStatusChangedListener listener;
private IoArgs.IoArgsEventProcessor receiveIoEventProcessor;
private IoArgs.IoArgsEventProcessor sendIoEventProcessor;
public SocketChannelAdapter(SocketChannel channel, IoProvider ioProvider,
OnChannelStatusChangedListener listener) throws IOException {
this.channel = channel;
this.ioProvider = ioProvider;
this.listener = listener;
channel.configureBlocking(false);
}
@Override
public void setReceiveListener(IoArgs.IoArgsEventProcessor processor) {
receiveIoEventProcessor = processor;
}
@Override
public boolean postReceiveAsync() throws IOException {
if (isClosed.get()) {
throw new IOException("Current channel is closed!");
}
return ioProvider.registerInput(channel, inputCallback);
}
@Override
public void setSendListener(IoArgs.IoArgsEventProcessor processor) {
sendIoEventProcessor = processor;
}
@Override
public boolean postSendAsync() throws IOException {
if (isClosed.get()) {
throw new IOException("Current channel is closed!");
}
// 当前发送的数据附加到回调中
return ioProvider.registerOutput(channel, outputCallback);
}
@Override
public void close() throws IOException {
if (isClosed.compareAndSet(false, true)) {
// 解除注册回调
ioProvider.unRegisterInput(channel);
ioProvider.unRegisterOutput(channel);
// 关闭
CloseUtils.close(channel);
// 回调当前Channel已关闭
listener.onChannelClosed(channel);
}
}
private final IoProvider.HandleInputCallback inputCallback = new IoProvider.HandleInputCallback() {
@Override
protected void canProviderInput() {
if (isClosed.get()) {
return;
}
IoArgs.IoArgsEventProcessor processor = receiveIoEventProcessor;
IoArgs args = processor.provideIoArgs();
try {
if (args == null) {
processor.onConsumeFailed(null, new IOException("ProvideIoArgs is null."));
} else if (args.readFrom(channel) > 0) {
// 读取完成回调
processor.onConsumeCompleted(args);
} else {
processor.onConsumeFailed(args, new IOException("Cannot read any data!"));
}
} catch (IOException ignored) {
CloseUtils.close(SocketChannelAdapter.this);
}
}
};
private final IoProvider.HandleOutputCallback outputCallback = new IoProvider.HandleOutputCallback() {
@Override
protected void canProviderOutput() {
if (isClosed.get()) {
return;
}
IoArgs.IoArgsEventProcessor processor = sendIoEventProcessor;
IoArgs args = processor.provideIoArgs();
try {
if (args == null) {
processor.onConsumeFailed(null, new IOException("ProvideIoArgs is null."));
} else if (args.writeTo(channel) > 0) {
// 输出完成回调
processor.onConsumeCompleted(args);
} else {
processor.onConsumeFailed(args, new IOException("Cannot write any data!"));
}
} catch (IOException ignored) {
CloseUtils.close(SocketChannelAdapter.this);
}
}
};
public interface OnChannelStatusChangedListener {
void onChannelClosed(SocketChannel channel);
}
}
package com.zcw.clink.utils;
/**
* @ClassName : ByteUtils
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:42
*/
public class ByteUtils {
/**
* Does this byte array begin with match array content?
*
* @param source Byte array to examine
* @param match Byte array to locate in source
* @return true If the starting bytes are equal
*/
public static boolean startsWith(byte[] source, byte[] match) {
return startsWith(source, 0, match);
}
/**
* Does this byte array begin with match array content?
*
* @param source Byte array to examine
* @param offset An offset into the source
array
* @param match Byte array to locate in source
* @return true If the starting bytes are equal
*/
public static boolean startsWith(byte[] source, int offset, byte[] match) {
if (match.length > (source.length - offset)) {
return false;
}
for (int i = 0; i < match.length; i++) {
if (source[offset + i] != match[i]) {
return false;
}
}
return true;
}
/**
* Does the source array equal the match array?
*
* @param source Byte array to examine
* @param match Byte array to locate in source
* @return true If the two arrays are equal
*/
public static boolean equals(byte[] source, byte[] match) {
if (match.length != source.length) {
return false;
}
return startsWith(source, 0, match);
}
/**
* Copies bytes from the source byte array to the destination array
*
* @param source The source array
* @param srcBegin Index of the first source byte to copy
* @param srcEnd Index after the last source byte to copy
* @param destination The destination array
* @param dstBegin The starting offset in the destination array
*/
public static void getBytes(byte[] source, int srcBegin, int srcEnd, byte[] destination,
int dstBegin) {
System.arraycopy(source, srcBegin, destination, dstBegin, srcEnd - srcBegin);
}
/**
* Return a new byte array containing a sub-portion of the source array
*
* @param srcBegin The beginning index (inclusive)
* @param srcEnd The ending index (exclusive)
* @return The new, populated byte array
*/
public static byte[] subbytes(byte[] source, int srcBegin, int srcEnd) {
byte destination[];
destination = new byte[srcEnd - srcBegin];
getBytes(source, srcBegin, srcEnd, destination, 0);
return destination;
}
/**
* Return a new byte array containing a sub-portion of the source array
*
* @param srcBegin The beginning index (inclusive)
* @return The new, populated byte array
*/
public static byte[] subbytes(byte[] source, int srcBegin) {
return subbytes(source, srcBegin, source.length);
}
}
package com.zcw.clink.utils;
import java.io.Closeable;
import java.io.IOException;
/**
* @ClassName : CloseUtils
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:42
*/
public class CloseUtils {
public static void close(Closeable... closeables) {
if (closeables == null) {
return;
}
for (Closeable closeable : closeables) {
if (closeable == null) {
continue;
}
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>socket-chat-room</artifactId>
<groupId>com.zcw</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sample-client</artifactId>
<dependencies>
<dependency>
<groupId>com.zcw</groupId>
<artifactId>lib-clink</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.zcw</groupId>
<artifactId>sample-foo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
package com.zcw.client.bean;
/**
* @ClassName : ServerInfo
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:53
*/
public class ServerInfo {
private String sn;
private int port;
private String address;
public ServerInfo(int port, String ip, String sn) {
this.port = port;
this.address = ip;
this.sn = sn;
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "ServerInfo{" +
"sn='" + sn + '\'' +
", port=" + port +
", address='" + address + '\'' +
'}';
}
}
package com.zcw.client;
import com.zcw.client.bean.ServerInfo;
import com.zcw.clink.box.FileSendPacket;
import com.zcw.clink.core.IoContext;
import com.zcw.clink.impl.IoSelectorProvider;
import com.zcw.foo.Foo;
import java.io.*;
/**
* @ClassName : Client
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:45
*/
public class Client {
public static void main(String[] args) throws IOException {
File cachePath = Foo.getCacheDir("client");
IoContext.setup()
.ioProvider(new IoSelectorProvider())
.start();
ServerInfo info = UDPSearcher.searchServer(10000);
System.out.println("Server:" + info);
if (info != null) {
TCPClient tcpClient = null;
try {
tcpClient = TCPClient.startWith(info, cachePath);
if (tcpClient == null) {
return;
}
write(tcpClient);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (tcpClient != null) {
tcpClient.exit();
}
}
}
IoContext.close();
}
private static void write(TCPClient tcpClient) throws IOException {
// 构建键盘输入流
InputStream in = System.in;
BufferedReader input = new BufferedReader(new InputStreamReader(in));
do {
// 键盘读取一行
String str = input.readLine();
if ("00bye00".equalsIgnoreCase(str)) {
break;
}
// --f url
if (str.startsWith("--f")) {
String[] array = str.split(" ");
if (array.length >= 2) {
String filePath = array[1];
File file = new File(filePath);
if (file.exists() && file.isFile()) {
FileSendPacket packet = new FileSendPacket(file);
tcpClient.send(packet);
continue;
}
}
}
// 发送字符串
tcpClient.send(str);
} while (true);
}
}
package com.zcw.client;
import com.zcw.client.bean.ServerInfo;
import com.zcw.foo.Foo;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName : ClientTest
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:53
*/
public class ClientTest {
private static boolean done;
public static void main(String[] args) throws IOException {
File cachePath = Foo.getCacheDir("client/test");
ServerInfo info = UDPSearcher.searchServer(10000);
System.out.println("Server:" + info);
if (info == null) {
return;
}
// 当前连接数量
int size = 0;
final List<TCPClient> tcpClients = new ArrayList<>();
for (int i = 0; i < 10; i++) {
try {
TCPClient tcpClient = TCPClient.startWith(info, cachePath);
if (tcpClient == null) {
System.out.println("连接异常");
continue;
}
tcpClients.add(tcpClient);
System.out.println("连接成功:" + (++size));
} catch (IOException e) {
System.out.println("连接异常");
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.in.read();
Runnable runnable = () -> {
while (!done) {
for (TCPClient tcpClient : tcpClients) {
tcpClient.send("Hello~~");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
System.in.read();
// 等待线程完成
done = true;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 客户端结束操作
for (TCPClient tcpClient : tcpClients) {
tcpClient.exit();
}
}
}
package com.zcw.client;
import com.zcw.client.bean.ServerInfo;
import com.zcw.clink.core.Connector;
import com.zcw.clink.core.Packet;
import com.zcw.clink.core.ReceivePacket;
import com.zcw.clink.utils.CloseUtils;
import com.zcw.foo.Foo;
import java.io.File;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
/**
* @ClassName : TCPClient
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:46
*/
public class TCPClient extends Connector {
private final File cachePath;
public TCPClient(SocketChannel socketChannel, File cachePath) throws IOException {
this.cachePath = cachePath;
setup(socketChannel);
}
public void exit() {
CloseUtils.close(this);
}
@Override
public void onChannelClosed(SocketChannel channel) {
super.onChannelClosed(channel);
System.out.println("连接已关闭,无法读取数据!");
}
@Override
protected File createNewReceiveFile() {
return Foo.createRandomTemp(cachePath);
}
@Override
protected void onReceivedPacket(ReceivePacket packet) {
super.onReceivedPacket(packet);
if (packet.type() == Packet.TYPE_MEMORY_STRING) {
String string = (String) packet.entity();
System.out.println(key.toString() + ":" + string);
}
}
public static TCPClient startWith(ServerInfo info, File cachePath) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
// 连接本地,端口2000;超时时间3000ms
socketChannel.connect(new InetSocketAddress(Inet4Address.getByName(info.getAddress()), info.getPort()));
System.out.println("已发起服务器连接,并进入后续流程~");
System.out.println("客户端信息:" + socketChannel.getLocalAddress().toString());
System.out.println("服务器信息:" + socketChannel.getRemoteAddress().toString());
try {
return new TCPClient(socketChannel, cachePath);
} catch (Exception e) {
System.out.println("连接异常");
CloseUtils.close(socketChannel);
}
return null;
}
}
package com.zcw.client;
import com.zcw.client.bean.ServerInfo;
import com.zcw.clink.utils.ByteUtils;
import com.zcw.foo.constants.UDPConstants;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @ClassName : UDPSearcher
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:47
*/
public class UDPSearcher {
private static final int LISTEN_PORT = UDPConstants.PORT_CLIENT_RESPONSE;
public static ServerInfo searchServer(int timeout) {
System.out.println("UDPSearcher Started.");
// 成功收到回送的栅栏
CountDownLatch receiveLatch = new CountDownLatch(1);
Listener listener = null;
try {
listener = listen(receiveLatch);
sendBroadcast();
receiveLatch.await(timeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
}
// 完成
System.out.println("UDPSearcher Finished.");
if (listener == null) {
return null;
}
List<ServerInfo> devices = listener.getServerAndClose();
if (devices.size() > 0) {
return devices.get(0);
}
return null;
}
private static Listener listen(CountDownLatch receiveLatch) throws InterruptedException {
System.out.println("UDPSearcher start listen.");
CountDownLatch startDownLatch = new CountDownLatch(1);
Listener listener = new Listener(LISTEN_PORT, startDownLatch, receiveLatch);
listener.start();
startDownLatch.await();
return listener;
}
private static void sendBroadcast() throws IOException {
System.out.println("UDPSearcher sendBroadcast started.");
// 作为搜索方,让系统自动分配端口
DatagramSocket ds = new DatagramSocket();
// 构建一份请求数据
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
// 头部
byteBuffer.put(UDPConstants.HEADER);
// CMD命名
byteBuffer.putShort((short) 1);
// 回送端口信息
byteBuffer.putInt(LISTEN_PORT);
// 直接构建packet
DatagramPacket requestPacket = new DatagramPacket(byteBuffer.array(),
byteBuffer.position() + 1);
// 广播地址
requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
// 设置服务器端口
requestPacket.setPort(UDPConstants.PORT_SERVER);
// 发送
ds.send(requestPacket);
ds.close();
// 完成
System.out.println("UDPSearcher sendBroadcast finished.");
}
private static class Listener extends Thread {
private final int listenPort;
private final CountDownLatch startDownLatch;
private final CountDownLatch receiveDownLatch;
private final List<ServerInfo> serverInfoList = new ArrayList<ServerInfo>();
private final byte[] buffer = new byte[128];
private final int minLen = UDPConstants.HEADER.length + 2 + 4;
private boolean done = false;
private DatagramSocket ds = null;
private Listener(int listenPort, CountDownLatch startDownLatch, CountDownLatch receiveDownLatch) {
super();
this.listenPort = listenPort;
this.startDownLatch = startDownLatch;
this.receiveDownLatch = receiveDownLatch;
}
@Override
public void run() {
super.run();
// 通知已启动
startDownLatch.countDown();
try {
// 监听回送端口
ds = new DatagramSocket(listenPort);
// 构建接收实体
DatagramPacket receivePack = new DatagramPacket(buffer, buffer.length);
while (!done) {
// 接收
ds.receive(receivePack);
// 打印接收到的信息与发送者的信息
// 发送者的IP地址
String ip = receivePack.getAddress().getHostAddress();
int port = receivePack.getPort();
int dataLen = receivePack.getLength();
byte[] data = receivePack.getData();
boolean isValid = dataLen >= minLen
&& ByteUtils.startsWith(data, UDPConstants.HEADER);
System.out.println("UDPSearcher receive form ip:" + ip
+ "\tport:" + port + "\tdataValid:" + isValid);
if (!isValid) {
// 无效继续
continue;
}
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, UDPConstants.HEADER.length, dataLen);
final short cmd = byteBuffer.getShort();
final int serverPort = byteBuffer.getInt();
if (cmd != 2 || serverPort <= 0) {
System.out.println("UDPSearcher receive cmd:" + cmd + "\tserverPort:" + serverPort);
continue;
}
String sn = new String(buffer, minLen, dataLen - minLen);
ServerInfo info = new ServerInfo(serverPort, ip, sn);
serverInfoList.add(info);
// 成功接收到一份
receiveDownLatch.countDown();
}
} catch (Exception ignored) {
} finally {
close();
}
System.out.println("UDPSearcher listener finished.");
}
private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
List<ServerInfo> getServerAndClose() {
done = true;
close();
return serverInfoList;
}
}
}
package com.zcw.foo.constants;
/**
* @ClassName : TCPConstants
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:40
*/
public class TCPConstants {
// 服务器固化UDP接收端口
public static int PORT_SERVER = 30401;
}
package com.zcw.foo.constants;
/**
* @ClassName : UDPConstants
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:40
*/
public class UDPConstants {
// 公用头部
public static byte[] HEADER = new byte[]{7, 7, 7, 7, 7, 7, 7, 7};
// 服务器固化UDP接收端口
public static int PORT_SERVER = 30201;
// 客户端回送端口
public static int PORT_CLIENT_RESPONSE = 30202;
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>socket-chat-room</artifactId>
<groupId>com.zcw</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sample-server</artifactId>
<dependencies>
<dependency>
<groupId>com.zcw</groupId>
<artifactId>lib-clink</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.zcw</groupId>
<artifactId>sample-foo</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
package com.zcw.server.handle;
import com.zcw.clink.core.Connector;
import com.zcw.clink.core.Packet;
import com.zcw.clink.core.ReceivePacket;
import com.zcw.clink.utils.CloseUtils;
import com.zcw.foo.Foo;
import java.io.File;
import java.io.IOException;
import java.nio.channels.SocketChannel;
/**
* @ClassName : ClientHandler
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:50
*/
public class ClientHandler extends Connector {
private final File cachePath;
private final ClientHandlerCallback clientHandlerCallback;
private final String clientInfo;
public ClientHandler(SocketChannel socketChannel,
ClientHandlerCallback clientHandlerCallback,
File cachePath) throws IOException {
this.clientHandlerCallback = clientHandlerCallback;
this.clientInfo = socketChannel.getRemoteAddress().toString();
this.cachePath = cachePath;
System.out.println("新客户端连接:" + clientInfo);
setup(socketChannel);
}
public void exit() {
CloseUtils.close(this);
System.out.println("客户端已退出:" + clientInfo);
}
@Override
public void onChannelClosed(SocketChannel channel) {
super.onChannelClosed(channel);
exitBySelf();
}
@Override
protected File createNewReceiveFile() {
return Foo.createRandomTemp(cachePath);
}
@Override
protected void onReceivedPacket(ReceivePacket packet) {
super.onReceivedPacket(packet);
if (packet.type() == Packet.TYPE_MEMORY_STRING) {
String string = (String) packet.entity();
System.out.println(key.toString() + ":" + string);
clientHandlerCallback.onNewMessageArrived(this, string);
}
}
private void exitBySelf() {
exit();
clientHandlerCallback.onSelfClosed(this);
}
public interface ClientHandlerCallback {
// 自身关闭通知
void onSelfClosed(ClientHandler handler);
// 收到消息时通知
void onNewMessageArrived(ClientHandler handler, String msg);
}
}
package com.zcw.server;
import com.zcw.clink.core.IoContext;
import com.zcw.clink.impl.IoSelectorProvider;
import com.zcw.foo.Foo;
import com.zcw.foo.constants.TCPConstants;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @ClassName : Server
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:48
*/
public class Server {
public static void main(String[] args) throws IOException {
File cachePath = Foo.getCacheDir("server");
IoContext.setup()
.ioProvider(new IoSelectorProvider())
.start();
TCPServer tcpServer = new TCPServer(TCPConstants.PORT_SERVER, cachePath);
boolean isSucceed = tcpServer.start();
if (!isSucceed) {
System.out.println("Start TCP server failed!");
return;
}
UDPProvider.start(TCPConstants.PORT_SERVER);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String str;
do {
str = bufferedReader.readLine();
if ("00bye00".equalsIgnoreCase(str)) {
break;
}
// 发送字符串
tcpServer.broadcast(str);
} while (true);
UDPProvider.stop();
tcpServer.stop();
IoContext.close();
}
}
package com.zcw.server;
import com.zcw.clink.utils.CloseUtils;
import com.zcw.server.handle.ClientHandler;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
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.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName : TCPServer
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:50
*/
public class TCPServer implements ClientHandler.ClientHandlerCallback{
private final int port;
private final File cachePath;
private final ExecutorService forwardingThreadPoolExecutor;
private ClientListener listener;
private List<ClientHandler> clientHandlerList = new ArrayList<>();
private Selector selector;
private ServerSocketChannel server;
public TCPServer(int port, File cachePath) {
this.port = port;
this.cachePath = cachePath;
// 转发线程池
this.forwardingThreadPoolExecutor = Executors.newSingleThreadExecutor();
}
public boolean start() {
try {
selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
// 设置为非阻塞
server.configureBlocking(false);
// 绑定本地端口
server.socket().bind(new InetSocketAddress(port));
// 注册客户端连接到达监听
server.register(selector, SelectionKey.OP_ACCEPT);
this.server = server;
System.out.println("服务器信息:" + server.getLocalAddress().toString());
// 启动客户端监听
ClientListener listener = this.listener = new ClientListener();
listener.start();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
public void stop() {
if (listener != null) {
listener.exit();
}
CloseUtils.close(server);
CloseUtils.close(selector);
synchronized (TCPServer.this) {
for (ClientHandler clientHandler : clientHandlerList) {
clientHandler.exit();
}
clientHandlerList.clear();
}
// 停止线程池
forwardingThreadPoolExecutor.shutdownNow();
}
public synchronized void broadcast(String str) {
for (ClientHandler clientHandler : clientHandlerList) {
clientHandler.send(str);
}
}
@Override
public synchronized void onSelfClosed(ClientHandler handler) {
clientHandlerList.remove(handler);
}
@Override
public void onNewMessageArrived(final ClientHandler handler, final String msg) {
// 异步提交转发任务
forwardingThreadPoolExecutor.execute(() -> {
synchronized (TCPServer.this) {
for (ClientHandler clientHandler : clientHandlerList) {
if (clientHandler.equals(handler)) {
// 跳过自己
continue;
}
// 对其他客户端发送消息
clientHandler.send(msg);
}
}
});
}
private class ClientListener extends Thread {
private boolean done = false;
@Override
public void run() {
super.run();
Selector selector = TCPServer.this.selector;
System.out.println("服务器准备就绪~");
// 等待客户端连接
do {
// 得到客户端
try {
if (selector.select() == 0) {
if (done) {
break;
}
continue;
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
if (done) {
break;
}
SelectionKey key = iterator.next();
iterator.remove();
// 检查当前Key的状态是否是我们关注的
// 客户端到达状态
if (key.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
// 非阻塞状态拿到客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
try {
// 客户端构建异步线程
ClientHandler clientHandler = new ClientHandler(socketChannel,
TCPServer.this, cachePath);
// 添加同步处理
synchronized (TCPServer.this) {
clientHandlerList.add(clientHandler);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端连接异常:" + e.getMessage());
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
} while (!done);
System.out.println("服务器已关闭!");
}
void exit() {
done = true;
// 唤醒当前的阻塞
selector.wakeup();
}
}
}
package com.zcw.server;
import com.zcw.clink.utils.ByteUtils;
import com.zcw.foo.constants.UDPConstants;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.ByteBuffer;
import java.util.UUID;
/**
* @ClassName : UDPProvider
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-08 13:50
*/
public class UDPProvider {
private static Provider PROVIDER_INSTANCE;
static void start(int port) {
stop();
String sn = UUID.randomUUID().toString();
Provider provider = new Provider(sn, port);
provider.start();
PROVIDER_INSTANCE = provider;
}
static void stop() {
if (PROVIDER_INSTANCE != null) {
PROVIDER_INSTANCE.exit();
PROVIDER_INSTANCE = null;
}
}
private static class Provider extends Thread {
private final byte[] sn;
private final int port;
private boolean done = false;
private DatagramSocket ds = null;
// 存储消息的Buffer
final byte[] buffer = new byte[128];
Provider(String sn, int port) {
super();
this.sn = sn.getBytes();
this.port = port;
}
@Override
public void run() {
super.run();
System.out.println("UDPProvider Started.");
try {
// 监听20000 端口
ds = new DatagramSocket(UDPConstants.PORT_SERVER);
// 接收消息的Packet
DatagramPacket receivePack = new DatagramPacket(buffer, buffer.length);
while (!done) {
// 接收
ds.receive(receivePack);
// 打印接收到的信息与发送者的信息
// 发送者的IP地址
String clientIp = receivePack.getAddress().getHostAddress();
int clientPort = receivePack.getPort();
int clientDataLen = receivePack.getLength();
byte[] clientData = receivePack.getData();
boolean isValid = clientDataLen >= (UDPConstants.HEADER.length + 2 + 4)
&& ByteUtils.startsWith(clientData, UDPConstants.HEADER);
System.out.println("UDPProvider receive form ip:" + clientIp
+ "\tport:" + clientPort + "\tdataValid:" + isValid);
if (!isValid) {
// 无效继续
continue;
}
// 解析命令与回送端口
int index = UDPConstants.HEADER.length;
short cmd = (short) ((clientData[index++] << 8) | (clientData[index++] & 0xff));
int responsePort = (((clientData[index++]) << 24) |
((clientData[index++] & 0xff) << 16) |
((clientData[index++] & 0xff) << 8) |
((clientData[index] & 0xff)));
// 判断合法性
if (cmd == 1 && responsePort > 0) {
// 构建一份回送数据
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
byteBuffer.put(UDPConstants.HEADER);
byteBuffer.putShort((short) 2);
byteBuffer.putInt(port);
byteBuffer.put(sn);
int len = byteBuffer.position();
// 直接根据发送者构建一份回送信息
DatagramPacket responsePacket = new DatagramPacket(buffer,
len,
receivePack.getAddress(),
responsePort);
ds.send(responsePacket);
System.out.println("UDPProvider response to:" + clientIp + "\tport:" + responsePort + "\tdataLen:" + len);
} else {
System.out.println("UDPProvider receive cmd nonsupport; cmd:" + cmd + "\tport:" + port);
}
}
} catch (Exception ignored) {
} finally {
close();
}
// 完成
System.out.println("UDPProvider Finished.");
}
private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
/**
* 提供结束
*/
void exit() {
done = true;
close();
}
}
}