Spring Boot集成 mina 服务端搭建
<!--Mina框架依赖 -->
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.0.2</version>
</dependency>```
package com.example.netty;
import javax.annotation.PostConstruct;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class ServerHandler extends IoHandlerAdapter {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/* 在本类中调用其他service需要使用以下方法
*
* */
//1.声明service类
/*@Autowired
private TbBoxService tbBoxService;*/
private static ServerHandler serverHandler ;
//2通过@PostConstruct实现初始化bean之前进行的操作
@PostConstruct
public void init() {
serverHandler = this;
//serverHandler.tbBoxService = this.tbBoxService;.
//3.调用时需要加前缀 如 serverHandler.tbBoxService
}
@Override
public void sessionCreated(IoSession session) throws Exception { //用户连接到服务器
SocketSessionConfig cfg = (SocketSessionConfig) session.getConfig();
cfg.setSoLinger(0);
logger.info("[服务建立]" + session.getId());
}
@Override
public void messageReceived(IoSession session, Object message)throws Exception {//接收消息 收到消息可以自己定义处理类或者转发
IoBuffer bbuf = (IoBuffer) message;
byte[] byten = new byte[bbuf.limit()];
bbuf.get(byten, bbuf.position(), bbuf.limit());
String str = new String(byten);
logger.info("[收到消息]" + str);
}
@Override
public void sessionClosed(IoSession session) throws Exception { //用户从服务器断开
logger.info("[服务断开]" + session.getId());
}
@Override
public void messageSent(IoSession session, Object message){ //发送消息结束
logger.info("[发送消息结束]" + session.getId() + "message" + message);
}
@Override
public void sessionIdle(IoSession session, IdleStatus status)throws Exception {//重连
logger.info("[服务重连]" + session.getId() + "status" + status.toString());
}
@Override
public void exceptionCaught(IoSession session, Throwable cause)throws Exception {//连接发生异常
cause.printStackTrace();
logger.info("[服务异常]" + session.getId());
}
}
Spring boot 启动类直接加入启动项目时自动启动服务
@Bean
public IoAcceptor ioAcceptor() throws Exception {
// 创建一个非阻塞的server端的Socket
IoAcceptor acceptor = new NioSocketAcceptor();
// 添加日志过滤器
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
// acceptor.getFilterChain().addLast("codec",
// new ProtocolCodecFilter(new HCoderFactory(Charset.forName("UTF-8"))));
// 自定义解编码器 HCoderFactory
//配置之后需要自己编码或者解码下面有具体代码 配置和可解决粘包问题
// 设置Handler
acceptor.setHandler(new ServerHandler());
// 设置读取数据的缓存区大小
acceptor.getSessionConfig().setReadBufferSize(2048);
// 读写通道10秒内无操作进入空闲状态
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
try {
// 绑定端口
acceptor.bind(new InetSocketAddress(8085));
System.out.println("服务端在:" + 8085 + "端口启动");
}
catch (Exception e) {
e.printStackTrace();
}
return acceptor;
}
package mina;
import java.nio.charset.Charset;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;
/**
* 编码和解码器工厂类
* @author admin
*
*/
public class HCoderFactory implements ProtocolCodecFactory {
private final HEncoder encoder;
private final HDecoder decoder;
public HCoderFactory() {
this(Charset.forName("UTF-8"));
}
public HCoderFactory(Charset charSet) {
this.encoder = new HEncoder(charSet);
this.decoder = new HDecoder(charSet);
}
@Override
public ProtocolDecoder getDecoder(IoSession arg0) throws Exception {
return decoder;
}
@Override
public ProtocolEncoder getEncoder(IoSession arg0) throws Exception {
return encoder;
}
}
package mina;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 解码工具类
* @author admin
*
*/
public class HDecoder extends CumulativeProtocolDecoder {
private final Charset charset;
private static final Logger log = LoggerFactory.getLogger(HEncoder.class);
public HDecoder(Charset charset) {
this.charset = charset;
}
@Override
protected boolean doDecode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput protocolDecoderOutput)
throws Exception {
// 丢包,断包处理
if (ioBuffer.remaining() > 4)// 有包头,包头足够
{
ioBuffer.mark();// 标记当前position的快照标记mark,以便后继的reset操作能恢复position位置,开始是0
byte[] l = new byte[4];
ioBuffer.get(l);// 读取包头,占4个字节
if (ioBuffer.remaining() < 4)// 内容长度的4个字节不够,断包
{
ioBuffer.reset();
return false;//
} else {// 内容长度的4个字节数组足够
byte[] bytesLegth = new byte[4];// 内容长度
ioBuffer.get(bytesLegth);// 读取内容长度,int类型,占四个字节
int len = byteArrayToInt(bytesLegth);// 内容长度有多少
if (ioBuffer.remaining() < len)// 内容不够,断包
{
ioBuffer.reset();
return false;//
} else { // 消息内容足够
byte[] bytes = new byte[len];
ioBuffer.get(bytes, 0, len);
protocolDecoderOutput.write(new String(bytes, charset));// 读取内容,并且发送
if (ioBuffer.remaining() < 4) {// 包尾不够
ioBuffer.reset();
return false;//
} else {// 包尾足够
byte[] tails = new byte[4];
ioBuffer.get(tails);// 读取包尾
if (ioBuffer.remaining() > 0)// 最后如果粘了包,会再次调用doDeocde()方法,把剩余数据给doDeocde()方法处理
{
return true;
}
}
}
}
}
return false;// 断包,或者执行完,
}
public static int byteArrayToInt(byte[] bytes) {
int value = 0;
// 由高位到低位
for (int i = 0; i < 4; i++) {
int shift = (4 - 1 - i) * 8;
value += (bytes[i] & 0x000000FF) << shift;// 往高位游
}
return value;
}
}
package mina;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 编码工具类
*/
public class HEncoder implements ProtocolEncoder {
private static final Logger log = LoggerFactory.getLogger(HEncoder.class);
private final Charset charset;
public HEncoder(Charset charset) {
this.charset = charset;
}
/**
* 直接将数据发出去,数据格式,包头+消息长度(int)+消息内容(json字符串)+包尾 包头包尾是十六进制字符串00 aa bb cc,转化成字节数组0,
* -86, -69, -52四个字节
*
* @param session
* @param message
* @param out
* @throws Exception
*/
@Override
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
// 仿项目,解决断包,粘包问题
String value = (message == null ? "" : message.toString());// 消息值
log.info(value);
byte[] content = value.getBytes(charset);// 消息内容,字节数组
IoBuffer buf = IoBuffer.allocate(38 + content.length).setAutoExpand(true);// 缓冲区容量大小38字节加上字符长度
buf.put(new byte[] { 0, -86, -69, -52 });// 输入包开头固定值十六进制00 aa bb cc,转化成字节数组
buf.putInt(content.length);// int为4字节,一个字节等于2个16进制字符,所以有八位 00 00 00 0c,内容长度。
buf.put(content);// 消息内容
buf.put(new byte[] { 0, -86, -69, -52 });// 包尾
buf.flip();
out.write(buf);// 写入
}
@Override
public void dispose(IoSession session) throws Exception {
log.info("Dispose called,session is " + session);
}
}
测试 启动项目后 再写一个客户端即可测试(暂未配置编码 解码) 客户端编码 解码需要自行实现
public static void main(String[] args) {
try {
// 连接到服务器
Socket socket = new Socket("127.0.0.1", 8085);
try {
// 向服务器端发送信息的DataOutputStream
OutputStream out = socket.getOutputStream();
// 装饰标准输入流,用于从控制台输入
Scanner scanner = new Scanner(System.in);
while (true) {
String send = scanner.nextLine();
System.out.println("客户端:" + send);
byte[] by = send.getBytes("UTF-8");
by = send.getBytes();//把字符串转换成数组
out.write(by);
out.flush();
// 把从控制台得到的信息传送给服务器
// out.writeUTF("客户端:" + send);
// 读取来自服务器的信息
}
} finally {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}