MINA2 源代码学习--源代码结构梳理

一、mina总体框架与案例:

1.总体结构图:

简述:以上是一张来自网上比較经典的图,总体上揭示了mina的结构,当中IoService包括clientIoConnector和服务端IoAcceptor两部分。即不管是client还是服务端都是这个结构。IoService封装了网络传输层(TCP和UDP),而IoFilterChain中mina自带的filter做了一些主要的操作之外,支持扩展。经过FilterChain之后终于调用IoHandler,IoHandler是详细实现业务逻辑的处理接口,详细的业务实现可扩展。


2.一个可执行的案例(案例来自网上,转载后试验):
Client.java:

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Random;import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;public class Client extends IoHandlerAdapter {private Random random = new Random(System.currentTimeMillis());public Client() {IoConnector connector = new NioSocketConnector();connector.getFilterChain().addLast("text",new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName(Server.ENCODE))));connector.setHandler(this);ConnectFuture future = connector.connect(new InetSocketAddress("127.0.0.1", Server.PORT));future.awaitUninterruptibly();future.addListener(new IoFutureListener<ConnectFuture>() {@Overridepublic void operationComplete(ConnectFuture future) {IoSession session = future.getSession();while (!session.isClosing()) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}String message = "你好。我roll了" + random.nextInt(100) + "点.";session.write(message);}}});connector.dispose();}@Overridepublic void messageReceived(IoSession session, Object message)throws Exception {System.out.println("批复:" + message.toString());}@Overridepublic void messageSent(IoSession session, Object message) throws Exception {System.out.println("报告:" + message.toString());}@Overridepublic void exceptionCaught(IoSession session, Throwable cause)throws Exception {cause.printStackTrace();session.close(true);}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Client();}}
}

ServerHandler.java:

import java.net.InetSocketAddress;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;public class ServerHandler extends IoHandlerAdapter {@Overridepublic void exceptionCaught(IoSession session, Throwable cause)throws Exception {cause.printStackTrace();session.close(false);}public void messageReceived(IoSession session, Object message)throws Exception {String s = message.toString();System.out.println("收到请求:" + s);if (s != null) {int i = getPoint(s);if (session.isConnected()) {if (i >= 95) {session.write("运气不错,你能够出去了.");session.close(false);return;}Integer count = (Integer) session.getAttribute(Server.KEY);count++;session.setAttribute(Server.KEY, count);session.write("抱歉。你运气太差了,第" + count + "次请求未被通过。继续在小黑屋呆着吧.");} else {session.close(true);}}}@Overridepublic void messageSent(IoSession session, Object message) throws Exception {System.out.println("发给client:" + message.toString());}@Overridepublic void sessionClosed(IoSession session) throws Exception {long l = session.getCreationTime();System.out.println("来自" + getInfo(session) + "的会话已经关闭,它已经存活了"+ (System.currentTimeMillis() - 1) + "毫秒");}@Overridepublic void sessionCreated(IoSession session) throws Exception {System.out.println("给" + getInfo(session) + "创建了一个会话");}@Overridepublic void sessionIdle(IoSession session, IdleStatus status)throws Exception {System.out.println("来自" + getInfo(session) + "的会话闲置,状态为"+ status.toString());}public void sessionOpened(IoSession session) throws Exception {session.setAttribute(Server.KEY, 0);System.out.println("和" + getInfo(session) + "的会话已经打开.");}public String getInfo(IoSession session) {if (session == null) {return null;}InetSocketAddress address = (InetSocketAddress) session.getRemoteAddress();int port = address.getPort();String ip = address.getAddress().getHostAddress();return ip + ":" + port;}public int getPoint(String s) {if (s == null) {return -1;}Pattern p = Pattern.compile("^[\u0041-\uFFFF,]*(\\d+).*$");Matcher m = p.matcher(s);if (m.matches()) {return Integer.valueOf(m.group(1));}return 0;}
}

Server.java:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;public class Server {public static final int PORT = 2534;public static String ENCODE = "UTF-8";public static final String KEY = "roll";public static void main(String[] args){ SocketAcceptor acceptor = new NioSocketAcceptor();acceptor.getFilterChain().addLast("text",new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName(ENCODE))));acceptor.setHandler(new ServerHandler());try {acceptor.bind(new InetSocketAddress(PORT));System.out.println("游戏開始,你想出去吗,来,碰碰运气吧!");} catch (IOException e) {e.printStackTrace();acceptor.dispose();}}
}

本案例依赖的jar例如以下图:


简述:以上是依赖mina实现的一个可执行的案例,就不多说了,结合总体的结构图和案例实现能够看出mina框架还是非常轻量级的。以下分析一下mina的源代码结构和一些时序流程。

二、mina 核心源代码分析:

1.mina的启动时序(结合上面的案例):


简述:SocketAcceptor作为服务端对外启动接口类,在bind网络地址的时候,会触发服务端一系列服务的启动,从调用链能够清晰找到相应的源代码阅读。

当中AbstractPollingIoAcceptor是一个核心类,它会调用自身的startupAcceptor方法,来启动一个存放Acceptor的线程池用来处理client传输过来的请求。
AbstractPollingIoAcceptor 类的 startupAcceptor 方法例如以下:

/*** This method is called by the doBind() and doUnbind()* methods.  If the acceptor is null, the acceptor object will* be created and kicked off by the executor.  If the acceptor* object is null, probably already created and this class* is now working, then nothing will happen and the method* will just return.*/
private void startupAcceptor() throws InterruptedException {// If the acceptor is not ready, clear the queues// TODO : they should already be clean : do we have to do that ?

if (!selectable) { registerQueue.clear(); cancelQueue.clear(); } // start the acceptor if not already started Acceptor acceptor = acceptorRef.get(); //这里仅仅会启动一个worker if (acceptor == null) { lock.acquire(); acceptor = new Acceptor(); if (acceptorRef.compareAndSet(null, acceptor)) { executeWorker(acceptor); } else { lock.release(); } } }

上面调用到 AbstractIoService 的 executeWorker方法例如以下:

protected final void executeWorker(Runnable worker) {executeWorker(worker, null);
}protected final void executeWorker(Runnable worker, String suffix) {String actualThreadName = threadName;if (suffix != null) {actualThreadName = actualThreadName + '-' + suffix;}executor.execute(new NamePreservingRunnable(worker, actualThreadName));
}

简述:有类AbstractPollingIoAcceptor 的 startupAcceptor方法(上文)能够看到,一个SocketAcceptor仅仅启动了一个Worker线程(即代码中的Acceptor对象)而且把他加到线程池中。反过来讲,也能够看出AbstractIoService维护了Worker的线程池。(ps:这个Worker就是服务端处理请求的线程)。


2.Mina处理client链接的过程(启动后):

概述:从1中的启动时序能够看到,启动过程通过创建SocketAcceptor将有类AbstractPollingIoAcceptor的内部类Acceptor放到了 AbstractIoService的线程池里面,而这个Acceptor就是处理client网络请求的worker。而以下这个时序就是线程池中每一个worker处理client网络请求的时序流程。

处理请求时序: 

简述:worker线程Acceptor的run方法中会调用NioSocketAcceptor或者AprSocketAccetpor的select方法。
ps:APR(Apache Protable Runtime Library,Apache可移植执行库)是能够提供非常好的可拓展性、性能以及对底层操作系统一致性操作的技术,说白了就是apache实现的一套标准的通讯接口。

AprSocketAcceptor先不做深入了解,主要了解下NioSocketAcceptor,NioSocketAcceptor顾名思义,它调用了java NIO的API实现了NIO的网络连接处理过程。

AbstractPolling$Acceptor 的run方法的核心代码例如以下:

 private class Acceptor implements Runnable {public void run() {assert (acceptorRef.get() == this);int nHandles = 0;// Release the locklock.release();while (selectable) {try {// Detect if we have some keys ready to be processed// The select() will be woke up if some new connection// have occurred, or if the selector has been explicitly// woke up//调用了NioSocketAcceptor的select方法,获取了selectKeyint selected = select();// this actually sets the selector to OP_ACCEPT,// and binds to the port on which this class will// listen onnHandles += registerHandles();// Now, if the number of registred handles is 0, we can// quit the loop: we don't have any socket listening// for incoming connection.if (nHandles == 0) {acceptorRef.set(null);if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {assert (acceptorRef.get() != this);break;}if (!acceptorRef.compareAndSet(null, this)) {assert (acceptorRef.get() != this);break;}assert (acceptorRef.get() == this);}if (selected > 0) {// We have some connection request, let's process// them here.processHandles(selectedHandles());}// check to see if any cancellation request has been made.nHandles -= unregisterHandles();} catch (ClosedSelectorException cse) {// If the selector has been closed, we can exit the loopbreak;} catch (Throwable e) {ExceptionMonitor.getInstance().exceptionCaught(e);try {Thread.sleep(1000);} catch (InterruptedException e1) {ExceptionMonitor.getInstance().exceptionCaught(e1);}}}// Cleanup all the processors, and shutdown the acceptor.if (selectable && isDisposing()) {selectable = false;try {if (createdProcessor) {processor.dispose();}} finally {try {synchronized (disposalLock) {if (isDisposing()) {destroy();}}} catch (Exception e) {ExceptionMonitor.getInstance().exceptionCaught(e);} finally {disposalFuture.setDone();}}}}

简述:从上面的代码中能够看出一个典型的网络请求处理的程序,在循环中拿到处理的请求后就调用AbstractPollingIoAcceptor的processHandles()对网络请求做处理。

代码例如以下:

    /*** This method will process new sessions for the Worker class.  All* keys that have had their status updates as per the Selector.selectedKeys()* method will be processed here.  Only keys that are ready to accept* connections are handled here.* <p/>* Session objects are created by making new instances of SocketSessionImpl* and passing the session object to the SocketIoProcessor class.*/@SuppressWarnings("unchecked")private void processHandles(Iterator<H> handles) throws Exception {while (handles.hasNext()) {H handle = handles.next();handles.remove();// Associates a new created connection to a processor,// and get back a session//这里调用了NioSocketAcceptor的accept方法S session = accept(processor, handle);if (session == null) {continue;}initSession(session, null, null);// add the session to the SocketIoProcessor// 这步处理add操作,会触发对client请求的异步处理。

session.getProcessor().add(session); } }

NioSocketAcceptor的accept方法new了一个包装Process处理线程的session实例:而且在调用session.getProcessor().add(session)的操作的时候触发了对client请求的异步处理。

/*** {@inheritDoc}*/
@Override
protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {SelectionKey key = handle.keyFor(selector);if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {return null;}// accept the connection from the clientSocketChannel ch = handle.accept();if (ch == null) {return null;}return new NioSocketSession(this, processor, ch);
}

再看上面时序图:有一步是AbstractPollingIoProcessor调用了startupProcessor方法。代码例如以下:

 /*** Starts the inner Processor, asking the executor to pick a thread in its* pool. The Runnable will be renamed*/
private void startupProcessor() {Processor processor = processorRef.get();if (processor == null) {processor = new Processor();if (processorRef.compareAndSet(null, processor)) {executor.execute(new NamePreservingRunnable(processor, threadName));}}// Just stop the select() and start it again, so that the processor// can be activated immediately.wakeup();
}

简述:这个startupProcessor方法在调用 session里包装的processor的add方法是,触发了将处理client请求的processor放入异步处理的线程池中。兴许详细Processor怎么处理client请求的流程,涉及到FilterChain的过滤。以及Adapter的调用。用来处理业务逻辑。详细的异步处理时序看以下的时序图:


简述:这个时序就是将待处理的client链接,通过NIO的形式接受请求,并将请求包装成Processor的形式放到处理的线程池中异步的处理。

在异步的处理过程中则调用了Processor的run方法,详细的filterchain的调用和业务Adapter的调用也是在这一步得到处理。

值得注意的是。Handler的调用是封装在DefaultFilterchain的内部类诶TairFilter中触发调用的。Processor的run方法代码例如以下:

 private class Processor implements Runnable {public void run() {assert (processorRef.get() == this);int nSessions = 0;lastIdleCheckTime = System.currentTimeMillis();for (;;) {try {// This select has a timeout so that we can manage// idle session when we get out of the select every// second. (note : this is a hack to avoid creating// a dedicated thread).long t0 = System.currentTimeMillis();//调用了NioProcessorint selected = select(SELECT_TIMEOUT);long t1 = System.currentTimeMillis();long delta = (t1 - t0);if ((selected == 0) && !wakeupCalled.get() && (delta < 100)) {// Last chance : the select() may have been// interrupted because we have had an closed channel.if (isBrokenConnection()) {LOG.warn("Broken connection");// we can reselect immediately// set back the flag to falsewakeupCalled.getAndSet(false);continue;} else {LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));// Ok, we are hit by the nasty(讨厌的) epoll// spinning.// Basically, there is a race condition// which causes a closing file descriptor not to be// considered as available as a selected channel, but// it stopped the select. The next time we will// call select(), it will exit immediately for the same// reason, and do so forever, consuming 100%// CPU.// We have to destroy the selector, and// register all the socket on a new one.registerNewSelector();}// Set back the flag to falsewakeupCalled.getAndSet(false);// and continue the loopcontinue;}// Manage newly created session firstnSessions += handleNewSessions();updateTrafficMask();// Now, if we have had some incoming or outgoing events,// deal with themif (selected > 0) {//LOG.debug("Processing ..."); // This log hurts one of the MDCFilter test...//触发了详细的调用逻辑process();}// Write the pending requestslong currentTime = System.currentTimeMillis();flush(currentTime);// And manage removed sessionsnSessions -= removeSessions();// Last, not least, send Idle events to the idle sessionsnotifyIdleSessions(currentTime);// Get a chance to exit the infinite loop if there are no// more sessions on this Processorif (nSessions == 0) {processorRef.set(null);if (newSessions.isEmpty() && isSelectorEmpty()) {// newSessions.add() precedes startupProcessorassert (processorRef.get() != this);break;}assert (processorRef.get() != this);if (!processorRef.compareAndSet(null, this)) {// startupProcessor won race, so must exit processorassert (processorRef.get() != this);break;}assert (processorRef.get() == this);}// Disconnect all sessions immediately if disposal has been// requested so that we exit this loop eventually.if (isDisposing()) {for (Iterator<S> i = allSessions(); i.hasNext();) {scheduleRemove(i.next());}wakeup();}} catch (ClosedSelectorException cse) {// If the selector has been closed, we can exit the loopbreak;} catch (Throwable t) {ExceptionMonitor.getInstance().exceptionCaught(t);try {Thread.sleep(1000);} catch (InterruptedException e1) {ExceptionMonitor.getInstance().exceptionCaught(e1);}}}try {synchronized (disposalLock) {if (disposing) {doDispose();}}} catch (Throwable t) {ExceptionMonitor.getInstance().exceptionCaught(t);} finally {disposalFuture.setValue(true);}}
}

简述:这么一坨代码能够看出,这个处理器也调用了java的Nio API是一个NIO模型。当中select和process方法各自是从session拿到要处理的请求,并进行处理。而详细的Processor实例是NioProcessor。从加入凝视的代码中有一步调用了自身的process方法,这步调用触发了详细业务逻辑的调用。能够结合代码和时序图看下。在Process方法中会调用reader(session)或wirte(session)方法,然后调用fireMessageReceived方法,这种方法又调用了callNextMessageReceived方法致使触发了整个FilterChain和Adapter的调用。read方法的核心代码例如以下:

private void read(S session) {IoSessionConfig config = session.getConfig();int bufferSize = config.getReadBufferSize();IoBuffer buf = IoBuffer.allocate(bufferSize);final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();try {int readBytes = 0;int ret;try {if (hasFragmentation) {while ((ret = read(session, buf)) > 0) {readBytes += ret;if (!buf.hasRemaining()) {break;}}} else {ret = read(session, buf);if (ret > 0) {readBytes = ret;}}} finally {buf.flip();}if (readBytes > 0) {IoFilterChain filterChain = session.getFilterChain();filterChain.fireMessageReceived(buf);buf = null;if (hasFragmentation) {if (readBytes << 1 < config.getReadBufferSize()) {session.decreaseReadBufferSize();} else if (readBytes == config.getReadBufferSize()) {session.increaseReadBufferSize();}}}if (ret < 0) {scheduleRemove(session);}} catch (Throwable e) {if (e instanceof IOException) {if (!(e instanceof PortUnreachableException)|| !AbstractDatagramSessionConfig.class.isAssignableFrom(config.getClass())|| ((AbstractDatagramSessionConfig) config).isCloseOnPortUnreachable()) {scheduleRemove(session);}}IoFilterChain filterChain = session.getFilterChain();filterChain.fireExceptionCaught(e);}
}

从这段代码并结合上面的时序图能够看出来触发整个FilterChain的调用以及IoHandler的调用。

三、类结构分析

參考第一部分的总体结构图,画一下每一个部分大致的类结构图:

简述: 从类继承结构图来看,能够看到在IOService体系下,存在IoConnector和IoAcceptor两个大的分支体系。IoConnector是做为client的时候使用,IoAcceptor是作为服务端的时候使用。实际上在Mina中,有三种worker线程各自是:Acceptor、Connector 和 I/O processor。


(1) Acceptor Thread 作为server端的链接线程,实现了IoService接口。线程的数量就是创建SocketAcceptor的数量。


(2) Connector Thread 作为client请求建立的链接线程,实现了IoService接口,维持了一个和服务端Acceptor的一个链接,线程的数量就是创建SocketConnector的数量。


(3) I/O processorThread 作为I/O真正处理的线程,存在于server端和client。线程的数量是能够配置的,默认是CPU个数+1。

上面那个图仅仅是表述了IoService类体系,而I/O Processor的类体系并不在当中,见下图:


简述:IOProcessor主要分为两种。各自是AprIOProcessor和NioProcessor,Apr的解释见上文:ps:APR(Apache Protable Runtime Library,Apache可移植执行库)。

NioProcessor也是Nio的一种实现,用来处理client连接过来的请求。在Processor中会调用到 FilterChain 和 Handler,见上文代码。先看下FilterChain的类结构图例如以下:


Filter 和 Handler的类结构例如以下:


Handler的类结构例如以下:


Mina的session类结构图例如以下:

Mina的Buffer的类结构图例如以下:

版权声明:本文博主原创文章,博客,未经同意不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459712.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab水力学工具箱,新浪潮水工设计软件

新浪潮水工设计工具箱是一款强大的水利电力工程设计辅助软件&#xff0c;集合了结构设计、水闸设计、挡土墙设计、地基基础设计、水力学计算、堤防设计及查询工具7个模块的40多个程序&#xff0c;为从事水利电力工程设计用户的设计工作提供了多种便利。主要功能一、结构设计1、…

[BZOJ 1026] [SCOI 2009] Windy数 【数位DP】

题目链接&#xff1a;BZOJ - 1026 题目分析 这道题是一道数位DP的基础题&#xff0c;对于完全不会数位DP的我来说也是难题.. 对于询问 [a,b] 的区间的答案&#xff0c;我们对询问进行差分&#xff0c;求 [0,b] - [0,a-1] 的答案。这样就化繁为简了。 具体过程见代码中的注释。 …

求一个简单的php购物车,PHP简单实现购物车

首先&#xff0c;判断是否登录、if(isset($_session[‘user_id‘])){存在&#xff1b;把用户id和商品id&#xff0c;加入购物车表}else{不存在&#xff1b;使用cookie把商品ID和主机IP放入一个数组&#xff0c;把这个数组存入cookie&#xff1b;浏览器允许存放300个cookie&…

如何查看mac系统是32位还是64位的操作系统

&#xff08;一&#xff09;点击工具栏左上角点击 &#xff08;苹果Logo&#xff09;标志&#xff0c;关于本机 --> 更多信息 --> 系统报告 -->(左侧栏中)软件 &#xff08;二&#xff09;打开终端&#xff0c;输入命令 uname -a 回车 x86_64 表示系统为64位 i68…

3ds max删除了对象后,还是将原来所有对象输出的原因

原因是场景中除了 几何体 外还有 图形&#xff0c;如下图 将这些图形删除&#xff0c;几何体就都正常输出了。 转载于:https://www.cnblogs.com/qingsunny/p/4236530.html

后代选择器

1 <!DOCTYPE HTML>2 <html>3 <head>4 <meta http-equiv"Content-Type" content"text/html; charsetutf-8">5 <title>后代选择器</title>6 <style type"text/css">7 .first span{8 color:red;9 …

matlab怎么把变量一起换掉,MATLAB只是简单地把表达式里的变量名替换成数值,而不给出结果...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼%1.1.2.1快群%堆芯慢化能力%kesi_macrzeros(5,1);%for i1:5%kesi_macrkesi(1,i)*N(1,i)*micro_s1(1,i)%endkesi_macrkesi_H*N_H*micro_s1_Hkesi_O*N_O*micro_s1_Okesi_Zr*N_Zr*micro_s1_Zrkesi_5*N_5*micro_s1_5kesi_8*N_8*micro_s…

u-boot分析(八)----串口初始化

u-boot分析&#xff08;八&#xff09; 上篇博文我们按照210的启动流程&#xff0c;分析到了内存初始化&#xff0c;今天我们继续按照u-boot的启动流程对串口的初始化进行分析。 今天我们会用到的文档&#xff1a; 1. 2440芯片手册&#xff1a;http://download.csdn.net…

linux 开启防火墙的指定端口

2019独角兽企业重金招聘Python工程师标准>>> 通过下面的命令可以开启允许对外访问的网络端口&#xff1a; /sbin/iptables -I INPUT -p tcp --dport 8011 -j ACCEPT #开启8011端口 /etc/rc.d/init.d/iptables save #保存配置 /etc/rc.d/init.d/iptables restart #…

纽曼皮尔逊准则Matlab实现,纽曼-皮尔逊准则,Neyman Pearson Criterion,在线英语词典,英文翻译,专业英语...

胡安曼纽尔方吉奥(阿根廷) 世界冠军 - 1951, 1954-1957参加大奖赛次数: 51 赢得分站赛冠军次数: 22 赢得杆位数: 27很多人都把方吉奥看作是最伟大的车手。在整整七个赛季的f1比赛中(他为了从一场几乎致命的事故中康复而错过了一个赛季)&#xff0c;他获得了五次世界总冠军的头衔…

SpriteBuilder中的粒子系统属性

一个粒子发射器可以有2种模式&#xff0c;放射状和重力的(radial or gravity) 放射状模式允许你去使用发射器创建粒子旋涡状环绕在指定位置的效果。 当启用重力效果&#xff0c;你可以使得粒子在任何方向任意飞行&#xff0c;曲线或直线&#xff0c;有重力或无重力。 注意&…

Java:JDK安装

访问Oracle网站www.oracle.com/technetwork/java/javase/downloads下载jdk安装JDK时&#xff0c;不建议安装在有空格的路径名下&#xff0c;例如该目录c:\Program Files设置执行路径&#xff1a;在我的电脑_高级选项_环境变量中将jdk/bin的绝对路径加入到PATH中&#xff0c;以分…

基于matlab 宗晓萍,基于ADAMS和MATLAB的机械臂控制仿真

基于ADAMS和MATLAB的机械臂控制仿真宗晓萍;李月月【期刊名称】《微计算机信息》【年(卷),期】2009(000)035【摘要】运用多体系统动力学分析软件ADAMS建立虚拟模型,运用MATLAB 建立控制方案,对虚拟模型进行控制,将两者结合起来对二自由度机械臂关节角度进行控制,结果表明ADAMS和…

[转载]服务器和应用系统迁移方案

服务器和应用系统迁移方案 一、迁移方案总体思路 新旧系统的迁移是一个整体系统工程。迁移必须保证用户系统建设的相关要求&#xff0c;在迁移过程中&#xff0c;我们需要重点考虑几个问题&#xff1a; 1、数据迁移如何保障“业务中断停机时间”。业务中断对用用户无论是生产环…

记录知识点或技术方案信息

1 ActiveReports .NET报表控件的领导者 2 SignalR :.Net使用SignalR实现消息推送功能(聊天室) 3 Modernizr&#xff1a;在我们使用HTML5/CSS3技术的时候&#xff0c;如何更好地处理不支持HTML5/CSS3特性的旧版本浏览器问题&#xff1b;如果你正在使用最新的HTML5/CSS3来构建你…

Erlang 位串和二进制数据

http://blog.chinaunix.net/xmlrpc.php?rblog/article&uid25876834&id3300393 因为在本人工作中&#xff0c;服务端Erlang和客户端的flash通信都是发送二进制数据&#xff08;协议&#xff09;来通信&#xff0c;Erlang处理起来二进制数据真的很方便&#xff0c;在空余…

关于webstorm 配置 banbel

2019独角兽企业重金招聘Python工程师标准>>> file type : javascript files scope: project files program: ..............appData\Roaming\npm\babel.cmd argumemt: $FileName$ --out-file $FileNameWithoutExtension$.js -s ps: --out-put 输出es5 格式的文件 …

php imap 附件,学习猿地-PHP-imap 使用参考

1. 相关资料php-imap 用于获取邮件 &#xff0c;但是没有详细的官方文档 &#xff0c;在这里整理一份分享给大家composer require php-imap/php-imap2. php-imap 成员2.1 构造方法参数说明示例$imapPath服务地址{imap.163.com:143}$login登录名email163.com$password登录密码**…

微信朋友圈广告详细说明

根据官方发布的微信广告系统介绍&#xff0c;朋友圈广告来源于微信广告的一部分&#xff0c;与公众号广告形成了一整个体系。关于公众号的广告&#xff0c;简单说&#xff0c;他可以把微信公众号变成广告牌&#xff0c;会根据用户的阅读习惯以及个人信息来进行广告的投放。每一…

EasyExcel 导出文件的格式化

阿里开源的这个库&#xff0c;让 Excel 导出不再复杂&#xff08;既要能写&#xff0c;还要写的好看&#xff09; 之前聊了 EasyExcel 的内容导出&#xff0c;本文主要说一下导出文件的格式化&#xff0c;格式化包括工作表/单元格样式和内容格式化。毕竟&#xff0c;有时候还是…