1.项目结构
最好自己在翻一番源码,本人研究了2天netty但是没有研究明白发现这两款框架与很多相似之处,比如
netty的默认线程数为cpu核数*2,但是也自己可以根据构造传入线程数
mina的默认线程数为cpu核数+1,但是没找到从哪里可以传入线程数,有大佬可以回一下
netty主要针对的是channel
mina主要针对的是session
等等,这俩框架还在研究中,
mima有很多默认的实现,但是大多数支持重写
有什么问题可以讨论,我也多学习学习,我也可以把我研究netty的记录发出来
也可以看看官网api
http://mina.apache.org/mina-project/userguide/ch1-getting-started/ch1.4-first-steps.html
2.maven依赖
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.6</version>
<type>jar.sha256</type>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>org.rxtx</groupId>
<artifactId>rxtx</artifactId>
<version>2.1.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
3.日志管理(必配)
log4j.properties
###set log levels -for more verbose logging change 'info' to 'debug'###
log4j.rootLogger=info,stdout,file
###direct log messages to stdout###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
###direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=D://log//mina//mina.log
log4j.appender.file.MaxFileSize=10240KB
## Keep one backup file
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%p %t %c - %m%n
服务端
MyServer.java
package com.mina.test.firstdemo;
import org.apache.mina.core.future.CloseFuture;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.service.IoService;
import org.apache.mina.core.service.IoServiceListener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.keepalive.KeepAliveFilter;
import org.apache.mina.filter.keepalive.KeepAliveMessageFactory;
import org.apache.mina.filter.keepalive.KeepAliveRequestTimeoutHandler;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
public class MyServer {
private final static Logger log = LoggerFactory.getLogger(MyServer.class);
public static void main(String[] args) throws IOException {
/** Io流接收者(socket服务端) 默认开启线程数为cpu盒数*2 */
IoAcceptor acceptor = new NioSocketAcceptor();
try {
/** 设置缓冲区buffer大小 */
acceptor.getSessionConfig().setReadBufferSize(2048);
/** 读写通道10秒内无操作进入空闲状态*/
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
/** 自定义服务器事务处理器*/
acceptor.setHandler(new MyServerHandler());
/** 自定义服务器监听器 */
acceptor.addListener(new MyListener());
/** 自定义心跳监听器*/
KeepAliveFilter kaf = new KeepAliveFilter(new KeepAliveMessageFactoryImpl());
/** 这个还没有搞懂*/
kaf.setRequestTimeoutHandler(new MyKeepAliveRequestTimeoutHandler());
/** 设置心跳监听执行时间 单位秒*/
kaf.setRequestInterval(1);
/** 把心跳监听器加入(注册)到 服务器里*/
acceptor.getFilterChain().addLast("heart", kaf);
/** 把日志加入(注册)到 服务器 不加会报错*/
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
/** 设置服务器的 解码格式*/
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(
new TextLineCodecFactory(Charset.forName("UTF-8")
, LineDelimiter.WINDOWS.getValue(),LineDelimiter.WINDOWS.getValue())));
/** 服务器绑定ip 和 端口号 或者叫监听的端口*/
acceptor.bind(new InetSocketAddress("127.0.0.1",6666));
log.info("服务端已开启!!!!!!!!!!!!!!!");
} catch (IOException e) {
e.printStackTrace();
}finally {
}
}
}
/**
*@Description 事务处理器
*@Author Mr.CongShuo
*@Date 2021/3/10 0010
*@Time 17:03
*/
class MyServerHandler extends IoHandlerAdapter {
/** 定义日志*/
private final static Logger log = LoggerFactory.getLogger(MyServerHandler.class);
/** 定义客户端关闭编码*/
private final static String QUIT = "quit";
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
String str = message.toString();
log.info("this message received is [" + str + "]");
if(QUIT.equals(str)) {
session.close(true);
return;
}
}
/** 事务处理器 */
public MyServerHandler() {
}
/** 客户端 连接监听 函数 当有客户端走了以后会在线程池里的selector 下的fdMap 注册 也就是session会话
*
* 创建新连接后,从I / O处理器线程调用。 因为应该从处理多个会话的I / O的同一线程中调用此方法,
* 所以请实现此方法以执行消耗最少时间的任务,例如套接字参数和用户定义的会话属性初始化
**/
@Override
public void sessionCreated(IoSession session) throws Exception {
super.sessionCreated(session);
System.out.println("==============" + Thread.currentThread().getName());
System.out.println("客户端为: " +session.getId() + "进来了");
}
/**
* 当建立完成后 会打开一个通道
* @param session
* @throws Exception
*/
@Override
public void sessionOpened(IoSession session) throws Exception {
session.write("恭喜你来了");
}
/**
* 客户端关闭会调用这个函数
* @param session
* @throws Exception
*/
@Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("线程为:" + Thread.currentThread().getName() +" 的,sessionId为:" + session.getId() + "的客户端关闭了");
}
/**
* 限制状态处理
* @param session
* @param status
* @throws Exception
*/
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
System.out.println("线程为:" + Thread.currentThread().getName() +" 的,sessionId为:" + session.getId() + "的客户端进入了闲置状态");
}
/**
* 异常状态处理
* @param session
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
System.out.println("线程为:" + Thread.currentThread().getName() +" 的,sessionId为:" + session.getId() + "的客户端处理发生了异常 " + cause.getMessage());
}
/***
* 向客户端发送数据
* @param session
* @param message
* @throws Exception
*/
@Override
public void messageSent(IoSession session, Object message) throws Exception {
System.out.println("向 线程为:" + Thread.currentThread().getName() +" 的,sessionId为:" + session.getId() + "的客户端处理发送数据");
}
}
/**
*@Description 监听器
*@Author Mr.CongShuo
*@Date 2021/3/10 0010
*@Time 17:03
*/
class MyListener implements IoServiceListener {
@Override
public void serviceActivated(IoService service) throws Exception {
System.out.println("thread-name :" + Thread.currentThread().getName());
System.out.println("我被激活了");
}
@Override
public void serviceIdle(IoService service, IdleStatus idleStatus) throws Exception {
System.out.println("thread-name :" + Thread.currentThread().getName());
System.out.println("我现在没事干了");
}
@Override
public void serviceDeactivated(IoService service) throws Exception {
System.out.println("thread-name :" + Thread.currentThread().getName());
System.out.println("我现在被停用了");
}
@Override
public void sessionCreated(IoSession session) throws Exception {
System.out.println("thread-name :" + Thread.currentThread().getName());
System.out.println("建立新回话 有人已经进来了 准备唠嗑了");
}
/***
* 当有客户端走了以后会在线程池里的selector 下的fdMap 删除 也就是注册到 线程池里的session会话
* @param session
* @throws Exception
*/
@Override
public void sessionDestroyed(IoSession session) throws Exception {
System.out.println("thread-name :" + Thread.currentThread().getName());
System.out.println("销毁回话 有人走了");
long id = session.getId();
CloseFuture closeFuture = session.close(true);
boolean closed = closeFuture.isClosed();
if(closed){
System.out.println("客户端 sessionId为: " + id + " 关闭成功");
closeFuture.setClosed();
}else{
System.out.println("客户端 sessionId为:" + id + "关闭失败");
}
}
}
/**
*@Description 心跳包
*@Author Mr.CongShuo
*@Date 2021/3/10 0010
*@Time 17:04
*/
class KeepAliveMessageFactoryImpl implements KeepAliveMessageFactory {
@Override
public boolean isRequest(IoSession session, Object message) {
System.out.println("====isRequest=====");
return false;
}
@Override
public boolean isResponse(IoSession session, Object message) {
System.out.println("======是否能相应客户端=======" + (null == message ? "" : message.toString()));
return session.isConnected();
}
@Override
public Object getRequest(IoSession session) {
System.out.println("======是否能请求到客户端======");
return null;
}
@Override
public Object getResponse(IoSession session, Object request) {
System.out.println("=====getResponse=====");
return null;
}
}
class MyKeepAliveRequestTimeoutHandler implements KeepAliveRequestTimeoutHandler{
@Override
public void keepAliveRequestTimedOut(KeepAliveFilter filter, IoSession session) throws Exception {
System.out.println(session.getId() + "走了");
}
}
客户端
package com.mina.test.firstdemo;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
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.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Scanner;
public class MyClient {
private final static Logger log = LoggerFactory.getLogger(MyClient.class);
public static void main(String[] args) {
IoConnector connector = new NioSocketConnector();
try {
connector.getSessionConfig().setReadBufferSize(2048);
connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 5);
connector.setHandler(new MyClientHandler());
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(
new TextLineCodecFactory(Charset.forName("UTF-8")
, LineDelimiter.WINDOWS.getValue(),LineDelimiter.WINDOWS.getValue())));
ConnectFuture connectFuture = connector.connect(new InetSocketAddress("127.0.0.1", 6666));
connectFuture.awaitUninterruptibly();
log.info("服务端已开启!!!!!!!!!!!!!!!");
IoSession session = connectFuture.getSession();
Scanner sc = new Scanner(System.in);
boolean quit = false;
while (!quit) {
String str = sc.next();
if (str.equalsIgnoreCase("quit")) {
quit = true;
}
session.write(str);
}
if (session != null) {
if (session.isConnected()) {
session.getCloseFuture().awaitUninterruptibly();
}
connector.dispose(true);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
}
}
}
class MyClientHandler extends IoHandlerAdapter {
/** 定义日志*/
private final static Logger log = LoggerFactory.getLogger(MyClientHandler.class);
@Override
public void messageSent(IoSession arg0, Object message) throws Exception {
System.out.println("客户端收到信息" + message.toString());
}
public MyClientHandler() {
}
@Override
public void sessionCreated(IoSession session) throws Exception {
}
@Override
public void sessionOpened(IoSession session) throws Exception {
}
@Override
public void sessionClosed(IoSession session) throws Exception {
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
String str = message.toString();
log.info("this message received is [" + str + "]");
}
}
还有功能没有研究到 可以探究
这个文章写得也挺好
https://blog.csdn.net/defonds/article/details/18315563