@Override
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
in.order(ByteOrder.LITTLE_ENDIAN);
//...
}
byte cur_b = in.get(); //这个是byte
int cur_i = cur_b & 0xff; //做运算时要转成整形
get()
get(byte[])
get(byte[], int, int)
get(int)
getChar()
getChar(int)
getDouble()
getDouble(int)
getEnum(int, Class)
getEnum(Class)
getEnumInt(int, Class)
getEnumInt(Class)
getEnumSet(int, Class)
getEnumSet(Class)
getEnumSetInt(int, Class)
getEnumSetInt(Class)
getEnumSetLong(int, Class)
getEnumSetLong(Class)
getEnumSetShort(int, Class)
getEnumSetShort(Class)
getEnumShort(int, Class)
getEnumShort(Class)
getFloat()
getFloat(int)
getHexDump()
getHexDump(int)
getInt()
getInt(int)
getLong()
getLong(int)
getMediumInt()
getMediumInt(int)
getObject()
getObject(ClassLoader)
getPrefixedString(int, CharsetDecoder)
getPrefixedString(CharsetDecoder)
getShort()
getShort(int)
getSlice(int)
getSlice(int, int)
getString(int, CharsetDecoder)
getString(CharsetDecoder)
getUnsigned()
getUnsigned(int)
getUnsignedInt()
getUnsignedInt(int)
getUnsignedMediumInt()
getUnsignedMediumInt(int)
getUnsignedShort()
getUnsignedShort(int)
public class CrLfTerminatedCommandLineDecoder
extends CumulativeProtocolDecoder {
private Command parseCommand(IoBuffer in) {
// Convert the bytes in the specified buffer to a
// Command object.
...
}
protected boolean doDecode(
IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
// Remember the initial position.
int start = in.position();
// Now find the first CRLF in the buffer.
byte previous = 0;
while (in.hasRemaining()) {
byte current = in.get();
if (previous == '\r' && current == '\n') {
// Remember the current position and limit.
int position = in.position();
int limit = in.limit();
try {
in.position(start);
in.limit(position);
// The bytes between in.position() and in.limit()
// now contain a full CRLF terminated line.
out.write(parseCommand(in.slice()));
} finally {
// Set the position to point right after the
// detected line and set the limit to the old
// one.
in.position(position);
in.limit(limit);
}
// Decoded one line; CumulativeProtocolDecoder will
// call me again until I return false. So just
// return true until there are no more lines in the
// buffer.
return true;
}
previous = current;
}
// Could not find CRLF in the buffer. Reset the initial
// position to the one we recorded above.
in.position(start);
return false;
}
}
*/
除此之外,还要将未解析完的数据和标志位等放到IoSession中
Context ctx = getContext(session);
private Context getContext(IoSession session) {
Context context = (Context) session.getAttribute(CONTEXT);
if (context == null) {
context = new Context();
session.setAttribute(CONTEXT, context);
}
return context;
}
应用场景比如,一个端口9090专门负责数据接收,另外一个端口8080接收来自web端的指令并传送给终端并返回数据。
原理是在一个主函数中开两个mina服务(端口),但此时两个服务中的IoSession是不能互访的,所以要在主进程中管理他们
public class Main {
private static final Set sessions = Collections.synchronizedSet(new HashSet());//这个至关重要
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
private static final int PORT_DTU = 9090;
private static final int PORT_WEB = 8080;
private static final ProtocolCodecFactory textLineCodecFactory = new TextLineCodecFactory(Charset.forName("UTF-8"));
public static void main(String[] args) {
try {
new MinaxServer(PORT_DTU, textLineCodecFactory, new MinaxDtuIoHandler()).start();
LOGGER.info("Server started at port {}.", PORT_DTU);
new MinaxServer(PORT_WEB, textLineCodecFactory, new MinaxWebIoHandler()).start();
LOGGER.info("Server started at port {}.", PORT_WEB);
} catch (IOException ioe) {
LOGGER.error("Can't start server!", ioe);
}
while (true) {
System.out.printf("session count:%d\n", Main.getSessions().size());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static Set getSessions() {
return sessions;
}
}
之后在传送端的IoHandler中加以管理,即在SessionCreated时,加入到Set中,sessionClosed的时候从Set中Remove掉
@Override
public void sessionCreated(IoSession session) throws Exception {
// super.sessionCreated(session);
Main.getSessions().add(session);
// String message = String.format("IP:%s, Welcome to dtu server\n", session.getRemoteAddress());
// session.write(message);
logger.debug("{}:session[{}]Created...", session.getRemoteAddress(), session.getId());
}
@Override
public void sessionOpened(IoSession session) throws Exception {
// super.sessionOpened(session);
logger.debug("sessionOpened...");
}
@Override
public void sessionClosed(IoSession session) throws Exception {
Main.getSessions().remove(session);
// super.sessionClosed(session);
logger.debug("sessionClosed...");
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
logger.debug("messageReceived...");
logger.debug("...message:{}", message);
String jsonMessage = message.toString();
JSONObject o = JSON.parseObject(jsonMessage);
Integer dtu_id = o.getInteger("dtu_id");
Long session_id = o.getLong("session_id");
String action = o.getString("action");
String params = o.getString("params");
action = null == action ? "" : action.toLowerCase();
JSONObject p = JSON.parseObject(params);
Set sessions = Main.getSessions();//从主线程中取得session
switch (action) {
case "quit":
session.close(true);
break;
case "get_session_count":
session.write(sessions.size());
break;
case "broadcast":
String msg_bc = null == p ? null : p.getString("message");
if (null == msg_bc || msg_bc.length() == 0) {
msg_bc = "hello dtu!";
}
synchronized (sessions) {//注意同步
for (IoSession sess : sessions) {
// if (session.hashCode() == sess.hashCode()) {
// continue;
// }
if (sess.isConnected()) {
sess.write(msg_bc);
}
}
}
break;
default:
session.write("UNKOWN COMMAND");
break;
}
六、Web端的处理
对于Server接收端,一般采用长连接,http是无状态的,例如我发送了一个指令以取得在线的终端数据,发送到Server后,通过IoHandler处理,返回数据后在页面显示,这个连接就没有必要再保持了,可以直接关掉。相关代码
@RequestMapping(value = "/send", produces = "text/html;charset=UTF-8", method = RequestMethod.POST)
@ResponseBody
public String send(@RequestParam String cmd) {
logger.debug("...cmd:{}", cmd);
// 创建客户端连接器
IoConnector connector = new NioSocketConnector();
// 设置事件处理器
connector.setHandler(new ClientIoHandler());
// 设置编码过滤器和按行读取数据模式
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
// 创建连接
ConnectFuture future = connector.connect(new InetSocketAddress("localhost", 8080));
// 等待连接创建完成
future.awaitUninterruptibly();
// 获取连接会话
IoSession session = future.getSession();
// 产生当次发送请求标识,标识由客户端地址+随机数
// 这里的标识只是一个例子,标识可以任何方法产生,只要保持在系统中的唯一性
String flag = UUID.randomUUID().toString();
logger.debug("...flag:{}", flag);
// 将标识保存到当前session中
session.setAttribute(ClientIoHandler.SESSION_KEY, flag);
// 向服务器发送命令信息
session.write(cmd);
// 等待连接断开
session.getCloseFuture().awaitUninterruptibly();
connector.dispose();
// 通过标识获取保存的结果
Object result = ClientIoHandler.RESULTS.get(flag);
// 清除标识内容
ClientIoHandler.RESULTS.remove(flag);
// 将结果返回客户端
return result.toString();
}
public class ClientIoHandler extends IoHandlerAdapter {
private final Logger logger = LoggerFactory.getLogger(ClientIoHandler.class);
public static final String SESSION_KEY = "com.company.project.client.clientiohandle.SESSION_KEY";
public static final Map RESULTS = new ConcurrentHashMap();
public void messageReceived(IoSession session, Object message) throws Exception {
logger.debug("messageReceived...");
// 从session中取到标识
String flag = (String) session.getAttribute(SESSION_KEY);
logger.debug("...flag:{}", flag);
// 将从服务端接收到的message和标识绑定保存,可以保存到内存、文件、数据库等等
// 在这里简单的以标识为key,message为value保存到Map中
RESULTS.put(flag, message);
// 关闭session
session.close(true);
}
}