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,一经查实,立即删除!

相关文章

如何查看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 …

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 #…

关于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 格式的文件 …

微信朋友圈广告详细说明

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

EasyExcel 导出文件的格式化

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

oracle闪回某个时间点的数据库,oracle11g 使用闪回查询恢复表到过去某一个时间点...

一、新建测试表并插入数据&#xff1a;oracle二、模拟表数据误删并提交&#xff1a;测试三、使用闪回查询来查误删前表的数据&#xff1a;(表误删是在15:08分左右误删&#xff0c;所以在15:08分以前表的数据仍是在的)&#xff1a;spa四、用如今的数据与误删前的数据作对比&…

opengl微开发之1-从零開始

对OpenGL有一点了解之后&#xff0c;如今開始真正编写代码。 今天的内容&#xff1a; 使用FreeGLUT创建OpenGL的上下文环境 初始化GLEW 创建一个OpenGL的的模板范例 第一步&#xff1a; 一个OpenGL的上下文能够同意我们传递命令究竟层硬件&#xff0c;所以须要一个上下文环境。…

Git客户端(Windows系统)的使用(Putty)(转)

本文环境&#xff1a; 操作系统&#xff1a;Windows XP SP3 Git客户端&#xff1a;TortoiseGit-1.8.14.0-32bit 一、安装Git客户端 全部安装均采用默认&#xff01; 1. 安装支撑软件 msysgit: http://msysgit.github.io/ 当前最新版本为v1.9.5。 2. 安装TortoiseGit 首先&#…

两道二分coming~

第一道&#xff1a;poj 1905Expanding Rods 题意&#xff1a;两道墙&#xff08;距离L&#xff09;之间架一根棒子&#xff0c;棒子受热会变长&#xff0c;弯曲&#xff0c;长度变化满足公式&#xff08; s(1n*C)*L&#xff09;&#xff0c;求的是弯曲的高度h。 首先来看这个图…

十天学Linux内核之第七天---电源开和关时都发生了什么

十天学Linux内核之第七天---电源开和关时都发生了什么 原文:十天学Linux内核之第七天---电源开和关时都发生了什么说实话感觉自己快写不下去了&#xff0c;其一是有些勉强跟不上来&#xff0c;其二是感觉自己越写越差&#xff0c;刚开始可能是新鲜感以及很多读者的鼓励&#xf…

dede php 里加nofollow,织梦导航栏目如何加nofollow

很多用织梦程序做网站的朋友会发现&#xff0c;dedecms后台并没有直接加nofollow的选项。那如果想要给联系我们、关于我们等导航栏目加上nofollow该怎么解决呢&#xff1f;笔者在网上也找了一些资料&#xff0c;有通过修改数据库增加参数的方法&#xff0c;但没有成功。今天笔者…

【Cocos2d-Js实战教学(1)横版摇杆八方向移动】

本教程主要通过搭建一个横版摇杆八方向移动的实例&#xff0c;让大家如何用Cocos2dx-Js来做一款游戏&#xff0c;从基础了解Cocos2dx-Js的基本实现原理&#xff0c;从创建工程&#xff0c;到各个知识点的梳理。 教程分为上下两讲&#xff1a; 上讲有2个小节&#xff1a; 1&…

在单文档中显示我的第一个对话框

在单文档中显示我的第一个对话框 (2010-04-19 21:19:50) 转载▼标签&#xff1a; it 今天编写了在单文档中创建对话框以及调用对话框的代码&#xff0c;收获很多&#xff1b; 1&#xff1a;OnInitDialog函数的加载问题尤其是在VS上的加载&#xff1b;&#xff08;这一点花了…

主机Window不能访问该虚拟机Linux Samba文件服务提供了一个文件夹

我最近遇到一个问题。虚拟机Linux中间Samba服务常开。主办“\\192.168.229.200” (我的虚拟机Linux址)訪问不了Share文件夹&#xff08;/var/test&#xff09;&#xff0c;并且经过数次的重新启动&#xff0c;检查配置。再重新启动&#xff0c;都没法解决。后来突然想到了主机管…

Linux进程间通信(四) - 共享内存

共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高&#xff0c;因为进程可以直接读写内存&#xff0c;而不需要任何数据的拷贝。对于像管道和消息队列等通信方式&#xff0c;则需要在内核和用户空间进行四次的数据拷贝&#xff0c;而共享内存则只拷贝两次数据&…

jQuery EasyUI使用教程之基本的拖放

2019独角兽企业重金招聘Python工程师标准>>> <jQuery EasyUI最新版下载> 本教程将为你展示如何使HTML元素可拖动。在这个示例中我们将创建3个DIV元素&#xff0c;然后使它们能够被拖动。 查看演示 首先&#xff0c;我们创建3个div元素&#xff1a; < div i…

在Linux中head命令,Linux 中 head 命令实例

原标题&#xff1a;Linux 中 head 命令实例head命令将每个文件的前10行打印到标准输出。对于多个文件&#xff0c;在每个文件前面加上一个给出文件名的头。如果没有文件&#xff0c;或者文件为-&#xff0c;则读取标准输入。如何使用head命令使用head显示/etc/passwd前十行内容…