一、mina总体框架与案例:

1.总体结构图:



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

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

Client.java:

  1. import java.net.InetSocketAddress;
  2. import java.nio.charset.Charset;
  3. import java.util.Random;
  4. import org.apache.mina.core.future.ConnectFuture;
  5. import org.apache.mina.core.future.IoFutureListener;
  6. import org.apache.mina.core.service.IoConnector;
  7. import org.apache.mina.core.service.IoHandlerAdapter;
  8. import org.apache.mina.core.session.IoSession;
  9. import org.apache.mina.filter.codec.ProtocolCodecFilter;
  10. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
  11. import org.apache.mina.transport.socket.nio.NioSocketConnector;
  12. public class Client extends IoHandlerAdapter {
  13. private Random random = new Random(System.currentTimeMillis());
  14. public Client() {
  15. IoConnector connector = new NioSocketConnector();
  16. connector.getFilterChain().addLast(
  17. "text",
  18. new ProtocolCodecFilter(new TextLineCodecFactory(Charset
  19. .forName(Server.ENCODE))));
  20. connector.setHandler(this);
  21. ConnectFuture future = connector.connect(new InetSocketAddress(
  22. "127.0.0.1", Server.PORT));
  23. future.awaitUninterruptibly();
  24. future.addListener(new IoFutureListener<ConnectFuture>() {
  25. @Override
  26. public void operationComplete(ConnectFuture future) {
  27. IoSession session = future.getSession();
  28. while (!session.isClosing()) {
  29. try {
  30. Thread.sleep(100);
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. String message = "你好。我roll了" + random.nextInt(100) + "点.";
  35. session.write(message);
  36. }
  37. }
  38. });
  39. connector.dispose();
  40. }
  41. @Override
  42. public void messageReceived(IoSession session, Object message)
  43. throws Exception {
  44. System.out.println("批复:" + message.toString());
  45. }
  46. @Override
  47. public void messageSent(IoSession session, Object message) throws Exception {
  48. System.out.println("报告:" + message.toString());
  49. }
  50. @Override
  51. public void exceptionCaught(IoSession session, Throwable cause)
  52. throws Exception {
  53. cause.printStackTrace();
  54. session.close(true);
  55. }
  56. public static void main(String[] args) {
  57. for (int i = 0; i < 10; i++) {
  58. new Client();
  59. }
  60. }
  61. }

ServerHandler.java:

  1. import java.net.InetSocketAddress;
  2. import java.util.regex.Matcher;
  3. import java.util.regex.Pattern;
  4. import org.apache.mina.core.service.IoHandlerAdapter;
  5. import org.apache.mina.core.session.IdleStatus;
  6. import org.apache.mina.core.session.IoSession;
  7. public class ServerHandler extends IoHandlerAdapter {
  8. @Override
  9. public void exceptionCaught(IoSession session, Throwable cause)
  10. throws Exception {
  11. cause.printStackTrace();
  12. session.close(false);
  13. }
  14. public void messageReceived(IoSession session, Object message)
  15. throws Exception {
  16. String s = message.toString();
  17. System.out.println("收到请求:" + s);
  18. if (s != null) {
  19. int i = getPoint(s);
  20. if (session.isConnected()) {
  21. if (i >= 95) {
  22. session.write("运气不错,你能够出去了.");
  23. session.close(false);
  24. return;
  25. }
  26. Integer count = (Integer) session.getAttribute(Server.KEY);
  27. count++;
  28. session.setAttribute(Server.KEY, count);
  29. session.write("抱歉。你运气太差了,第" + count + "次请求未被通过。继续在小黑屋呆着吧.");
  30. } else {
  31. session.close(true);
  32. }
  33. }
  34. }
  35. @Override
  36. public void messageSent(IoSession session, Object message) throws Exception {
  37. System.out.println("发给client:" + message.toString());
  38. }
  39. @Override
  40. public void sessionClosed(IoSession session) throws Exception {
  41. long l = session.getCreationTime();
  42. System.out.println("来自" + getInfo(session) + "的会话已经关闭,它已经存活了"
  43. + (System.currentTimeMillis() - 1) + "毫秒");
  44. }
  45. @Override
  46. public void sessionCreated(IoSession session) throws Exception {
  47. System.out.println("给" + getInfo(session) + "创建了一个会话");
  48. }
  49. @Override
  50. public void sessionIdle(IoSession session, IdleStatus status)
  51. throws Exception {
  52. System.out.println("来自" + getInfo(session) + "的会话闲置,状态为"
  53. + status.toString());
  54. }
  55. public void sessionOpened(IoSession session) throws Exception {
  56. session.setAttribute(Server.KEY, 0);
  57. System.out.println("和" + getInfo(session) + "的会话已经打开.");
  58. }
  59. public String getInfo(IoSession session) {
  60. if (session == null) {
  61. return null;
  62. }
  63. InetSocketAddress address = (InetSocketAddress) session
  64. .getRemoteAddress();
  65. int port = address.getPort();
  66. String ip = address.getAddress().getHostAddress();
  67. return ip + ":" + port;
  68. }
  69. public int getPoint(String s) {
  70. if (s == null) {
  71. return -1;
  72. }
  73. Pattern p = Pattern.compile("^[\u0041-\uFFFF,]*(\\d+).*$");
  74. Matcher m = p.matcher(s);
  75. if (m.matches()) {
  76. return Integer.valueOf(m.group(1));
  77. }
  78. return 0;
  79. }
  80. }

Server.java:

  1. import java.io.IOException;
  2. import java.net.InetSocketAddress;
  3. import java.nio.charset.Charset;
  4. import org.apache.mina.filter.codec.ProtocolCodecFilter;
  5. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
  6. import org.apache.mina.transport.socket.SocketAcceptor;
  7. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
  8. public class Server {
  9. public static final int PORT = 2534;
  10. public static String ENCODE = "UTF-8";
  11. public static final String KEY = "roll";
  12. public static void main(String[] args){
  13. SocketAcceptor acceptor = new NioSocketAcceptor();
  14. acceptor.getFilterChain().addLast(
  15. "text",
  16. new ProtocolCodecFilter(new TextLineCodecFactory(Charset
  17. .forName(ENCODE))));
  18. acceptor.setHandler(new ServerHandler());
  19. try {
  20. acceptor.bind(new InetSocketAddress(PORT));
  21. System.out.println("游戏開始,你想出去吗,来,碰碰运气吧!");
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. acceptor.dispose();
  25. }
  26. }
  27. }

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

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

二、mina 核心源代码分析:

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

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

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

AbstractPollingIoAcceptor 类的 startupAcceptor 方法例如以下:

  1. /**
  2. * This method is called by the doBind() and doUnbind()
  3. * methods. If the acceptor is null, the acceptor object will
  4. * be created and kicked off by the executor. If the acceptor
  5. * object is null, probably already created and this class
  6. * is now working, then nothing will happen and the method
  7. * will just return.
  8. */
  9. private void startupAcceptor() throws InterruptedException {
  10. // If the acceptor is not ready, clear the queues
  11. // TODO : they should already be clean : do we have to do that ?
  12. if (!selectable) {
  13. registerQueue.clear();
  14. cancelQueue.clear();
  15. }
  16. // start the acceptor if not already started
  17. Acceptor acceptor = acceptorRef.get();
  18. //这里仅仅会启动一个worker
  19. if (acceptor == null) {
  20. lock.acquire();
  21. acceptor = new Acceptor();
  22. if (acceptorRef.compareAndSet(null, acceptor)) {
  23. executeWorker(acceptor);
  24. } else {
  25. lock.release();
  26. }
  27. }
  28. }

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

  1. protected final void executeWorker(Runnable worker) {
  2. executeWorker(worker, null);
  3. }
  4. protected final void executeWorker(Runnable worker, String suffix) {
  5. String actualThreadName = threadName;
  6. if (suffix != null) {
  7. actualThreadName = actualThreadName + '-' + suffix;
  8. }
  9. executor.execute(new NamePreservingRunnable(worker, actualThreadName));
  10. }

简述:有类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方法的核心代码例如以下:

  1. private class Acceptor implements Runnable {
  2. public void run() {
  3. assert (acceptorRef.get() == this);
  4. int nHandles = 0;
  5. // Release the lock
  6. lock.release();
  7. while (selectable) {
  8. try {
  9. // Detect if we have some keys ready to be processed
  10. // The select() will be woke up if some new connection
  11. // have occurred, or if the selector has been explicitly
  12. // woke up
  13. //调用了NioSocketAcceptor的select方法,获取了selectKey
  14. int selected = select();
  15. // this actually sets the selector to OP_ACCEPT,
  16. // and binds to the port on which this class will
  17. // listen on
  18. nHandles += registerHandles();
  19. // Now, if the number of registred handles is 0, we can
  20. // quit the loop: we don't have any socket listening
  21. // for incoming connection.
  22. if (nHandles == 0) {
  23. acceptorRef.set(null);
  24. if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
  25. assert (acceptorRef.get() != this);
  26. break;
  27. }
  28. if (!acceptorRef.compareAndSet(null, this)) {
  29. assert (acceptorRef.get() != this);
  30. break;
  31. }
  32. assert (acceptorRef.get() == this);
  33. }
  34. if (selected > 0) {
  35. // We have some connection request, let's process
  36. // them here.
  37. processHandles(selectedHandles());
  38. }
  39. // check to see if any cancellation request has been made.
  40. nHandles -= unregisterHandles();
  41. } catch (ClosedSelectorException cse) {
  42. // If the selector has been closed, we can exit the loop
  43. break;
  44. } catch (Throwable e) {
  45. ExceptionMonitor.getInstance().exceptionCaught(e);
  46. try {
  47. Thread.sleep(1000);
  48. } catch (InterruptedException e1) {
  49. ExceptionMonitor.getInstance().exceptionCaught(e1);
  50. }
  51. }
  52. }
  53. // Cleanup all the processors, and shutdown the acceptor.
  54. if (selectable && isDisposing()) {
  55. selectable = false;
  56. try {
  57. if (createdProcessor) {
  58. processor.dispose();
  59. }
  60. } finally {
  61. try {
  62. synchronized (disposalLock) {
  63. if (isDisposing()) {
  64. destroy();
  65. }
  66. }
  67. } catch (Exception e) {
  68. ExceptionMonitor.getInstance().exceptionCaught(e);
  69. } finally {
  70. disposalFuture.setDone();
  71. }
  72. }
  73. }
  74. }

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

代码例如以下:

  1. /**
  2. * This method will process new sessions for the Worker class. All
  3. * keys that have had their status updates as per the Selector.selectedKeys()
  4. * method will be processed here. Only keys that are ready to accept
  5. * connections are handled here.
  6. * <p/>
  7. * Session objects are created by making new instances of SocketSessionImpl
  8. * and passing the session object to the SocketIoProcessor class.
  9. */
  10. @SuppressWarnings("unchecked")
  11. private void processHandles(Iterator<H> handles) throws Exception {
  12. while (handles.hasNext()) {
  13. H handle = handles.next();
  14. handles.remove();
  15. // Associates a new created connection to a processor,
  16. // and get back a session
  17. //这里调用了NioSocketAcceptor的accept方法
  18. S session = accept(processor, handle);
  19. if (session == null) {
  20. continue;
  21. }
  22. initSession(session, null, null);
  23. // add the session to the SocketIoProcessor
  24. // 这步处理add操作,会触发对client请求的异步处理。
  25. session.getProcessor().add(session);
  26. }
  27. }

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

  1. /**
  2. * {@inheritDoc}
  3. */
  4. @Override
  5. protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {
  6. SelectionKey key = handle.keyFor(selector);
  7. if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {
  8. return null;
  9. }
  10. // accept the connection from the client
  11. SocketChannel ch = handle.accept();
  12. if (ch == null) {
  13. return null;
  14. }
  15. return new NioSocketSession(this, processor, ch);
  16. }

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

  1. /**
  2. * Starts the inner Processor, asking the executor to pick a thread in its
  3. * pool. The Runnable will be renamed
  4. */
  5. private void startupProcessor() {
  6. Processor processor = processorRef.get();
  7. if (processor == null) {
  8. processor = new Processor();
  9. if (processorRef.compareAndSet(null, processor)) {
  10. executor.execute(new NamePreservingRunnable(processor, threadName));
  11. }
  12. }
  13. // Just stop the select() and start it again, so that the processor
  14. // can be activated immediately.
  15. wakeup();
  16. }

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

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

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

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

  1. private class Processor implements Runnable {
  2. public void run() {
  3. assert (processorRef.get() == this);
  4. int nSessions = 0;
  5. lastIdleCheckTime = System.currentTimeMillis();
  6. for (;;) {
  7. try {
  8. // This select has a timeout so that we can manage
  9. // idle session when we get out of the select every
  10. // second. (note : this is a hack to avoid creating
  11. // a dedicated thread).
  12. long t0 = System.currentTimeMillis();
  13. //调用了NioProcessor
  14. int selected = select(SELECT_TIMEOUT);
  15. long t1 = System.currentTimeMillis();
  16. long delta = (t1 - t0);
  17. if ((selected == 0) && !wakeupCalled.get() && (delta < 100)) {
  18. // Last chance : the select() may have been
  19. // interrupted because we have had an closed channel.
  20. if (isBrokenConnection()) {
  21. LOG.warn("Broken connection");
  22. // we can reselect immediately
  23. // set back the flag to false
  24. wakeupCalled.getAndSet(false);
  25. continue;
  26. } else {
  27. LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));
  28. // Ok, we are hit by the nasty(讨厌的) epoll
  29. // spinning.
  30. // Basically, there is a race condition
  31. // which causes a closing file descriptor not to be
  32. // considered as available as a selected channel, but
  33. // it stopped the select. The next time we will
  34. // call select(), it will exit immediately for the same
  35. // reason, and do so forever, consuming 100%
  36. // CPU.
  37. // We have to destroy the selector, and
  38. // register all the socket on a new one.
  39. registerNewSelector();
  40. }
  41. // Set back the flag to false
  42. wakeupCalled.getAndSet(false);
  43. // and continue the loop
  44. continue;
  45. }
  46. // Manage newly created session first
  47. nSessions += handleNewSessions();
  48. updateTrafficMask();
  49. // Now, if we have had some incoming or outgoing events,
  50. // deal with them
  51. if (selected > 0) {
  52. //LOG.debug("Processing ..."); // This log hurts one of the MDCFilter test...
  53. //触发了详细的调用逻辑
  54. process();
  55. }
  56. // Write the pending requests
  57. long currentTime = System.currentTimeMillis();
  58. flush(currentTime);
  59. // And manage removed sessions
  60. nSessions -= removeSessions();
  61. // Last, not least, send Idle events to the idle sessions
  62. notifyIdleSessions(currentTime);
  63. // Get a chance to exit the infinite loop if there are no
  64. // more sessions on this Processor
  65. if (nSessions == 0) {
  66. processorRef.set(null);
  67. if (newSessions.isEmpty() && isSelectorEmpty()) {
  68. // newSessions.add() precedes startupProcessor
  69. assert (processorRef.get() != this);
  70. break;
  71. }
  72. assert (processorRef.get() != this);
  73. if (!processorRef.compareAndSet(null, this)) {
  74. // startupProcessor won race, so must exit processor
  75. assert (processorRef.get() != this);
  76. break;
  77. }
  78. assert (processorRef.get() == this);
  79. }
  80. // Disconnect all sessions immediately if disposal has been
  81. // requested so that we exit this loop eventually.
  82. if (isDisposing()) {
  83. for (Iterator<S> i = allSessions(); i.hasNext();) {
  84. scheduleRemove(i.next());
  85. }
  86. wakeup();
  87. }
  88. } catch (ClosedSelectorException cse) {
  89. // If the selector has been closed, we can exit the loop
  90. break;
  91. } catch (Throwable t) {
  92. ExceptionMonitor.getInstance().exceptionCaught(t);
  93. try {
  94. Thread.sleep(1000);
  95. } catch (InterruptedException e1) {
  96. ExceptionMonitor.getInstance().exceptionCaught(e1);
  97. }
  98. }
  99. }
  100. try {
  101. synchronized (disposalLock) {
  102. if (disposing) {
  103. doDispose();
  104. }
  105. }
  106. } catch (Throwable t) {
  107. ExceptionMonitor.getInstance().exceptionCaught(t);
  108. } finally {
  109. disposalFuture.setValue(true);
  110. }
  111. }
  112. }

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

  1. private void read(S session) {
  2. IoSessionConfig config = session.getConfig();
  3. int bufferSize = config.getReadBufferSize();
  4. IoBuffer buf = IoBuffer.allocate(bufferSize);
  5. final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();
  6. try {
  7. int readBytes = 0;
  8. int ret;
  9. try {
  10. if (hasFragmentation) {
  11. while ((ret = read(session, buf)) > 0) {
  12. readBytes += ret;
  13. if (!buf.hasRemaining()) {
  14. break;
  15. }
  16. }
  17. } else {
  18. ret = read(session, buf);
  19. if (ret > 0) {
  20. readBytes = ret;
  21. }
  22. }
  23. } finally {
  24. buf.flip();
  25. }
  26. if (readBytes > 0) {
  27. IoFilterChain filterChain = session.getFilterChain();
  28. filterChain.fireMessageReceived(buf);
  29. buf = null;
  30. if (hasFragmentation) {
  31. if (readBytes << 1 < config.getReadBufferSize()) {
  32. session.decreaseReadBufferSize();
  33. } else if (readBytes == config.getReadBufferSize()) {
  34. session.increaseReadBufferSize();
  35. }
  36. }
  37. }
  38. if (ret < 0) {
  39. scheduleRemove(session);
  40. }
  41. } catch (Throwable e) {
  42. if (e instanceof IOException) {
  43. if (!(e instanceof PortUnreachableException)
  44. || !AbstractDatagramSessionConfig.class.isAssignableFrom(config.getClass())
  45. || ((AbstractDatagramSessionConfig) config).isCloseOnPortUnreachable()) {
  46. scheduleRemove(session);
  47. }
  48. }
  49. IoFilterChain filterChain = session.getFilterChain();
  50. filterChain.fireExceptionCaught(e);
  51. }
  52. }

从这段代码并结合上面的时序图能够看出来触发整个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的类结构图例如以下:

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

MINA2 源代码学习--源代码结构梳理的更多相关文章

  1. JDK源代码学习系列04----ArrayList

                                                                             JDK源代码学习系列04----ArrayList 1 ...

  2. lua源代码学习(一)lua的c api外围实现

    工作后,整个人已经比較松懈了.尽管一直在看lua的源代码.可是一直是比較零碎的时间,没有系统的整理,所以还是收获不多.由于近期工作也不是非常忙了,就想整理下lua的源代码学习的笔记.加深下印象,并分享 ...

  3. struts2源代码学习之初始化(一)

    看struts2源代码已有一段时日,从今天開始,就做一个总结吧. 首先,先看看怎么调试struts2源代码吧,主要是下面步骤: 使用Myeclipse创建一个webproject 导入struts2须 ...

  4. [Java] LinkedList / Queue - 源代码学习笔记

    简单地画了下 LinkedList 的继承关系,如下图.只是画了关注的部分,并不是完整的关系图.本博文涉及的是 Queue, Deque, LinkedList 的源代码阅读笔记.关于 List 接口 ...

  5. 开源中国安卓client源代码学习(一) 渐变启动界面

    开源中国安卓client源代码学习(一) 渐变启动界面 准备学习安卓开发, 看到网上有人推荐开源中国安卓client的源代码, 说里面包括了大部分技术, 于是准备好好研究研究. 特开通此系列博客来记录 ...

  6. 读Flask源代码学习Python--config原理

    读Flask源代码学习Python--config原理 个人学习笔记,水平有限.如果理解错误的地方,请大家指出来,谢谢!第一次写文章,发现好累--!. 起因   莫名其妙在第一份工作中使用了从来没有接 ...

  7. nginx源代码学习资源(不断更新)

    nginx源代码学习是一个痛苦又快乐的过程,以下列出了一些nginx的学习资源. 首先要做的当然是下载一份nginx源代码,能够从nginx官方站点下载一份最新的. 看了nginx源代码,发现这是一份 ...

  8. JDK源代码学习系列07----Stack

                                                                   JDK源代码学习系列07----Stack 1.Stack源代码很easy ...

  9. Caffe学习系列(二)Caffe代码结构梳理,及相关知识点归纳

    前言: 通过检索论文.书籍.博客,继续学习Caffe,千里之行始于足下,继续努力.将自己学到的一些东西记录下来,方便日后的整理. 正文: 1.代码结构梳理 在终端下运行如下命令,可以查看caffe代码 ...

随机推荐

  1. Python什么是二次开发的意义?python在.net项目采用

    任何人都知道python在.net该项目是做什么的啊? 辅助用途,用作"二次开发"..net站点的话python主要是CGI才用.能够用python编写B/S程序. 解释一下二次开 ...

  2. gem5 设定checkpiont以及从checkpoint开始运行

    同spec2006中间bzip2一个例子,如何设置checkpoint .以及从checkpoint继续以启动运行.这样做的目的是为了,采纳automic运行N指令,然后detailed运行M指令. ...

  3. POJ1470 Closest Common Ancestors 【Tarjan的LCA】

    非常裸的模版题,只是Tarjan要好好多拿出来玩味几次 非常有点巧妙呢,tarjan,大概就是当前结点和它儿子结点的羁绊 WA了俩小时,,,原因是,这个题是多数据的(还没告诉你T,用scanf!=EO ...

  4. DotNet基础

    DotNet基础 URL特殊字符转义 摘要: URL中一些字符的特殊含义,基本编码规则如下: 1.空格换成加号(+) 2.正斜杠(/)分隔目录和子目录 3.问号(?)分隔URL和查询 4.百分号(%) ...

  5. WPF界面设计技巧(6)—玩玩数字墨水手绘涂鸦

    原文:WPF界面设计技巧(6)-玩玩数字墨水手绘涂鸦 想让你的程序支持鼠标及手写笔涂鸦吗?只要敲入“<InkCanvas/>”这几个字符,你就会领悟什么叫“很好很强大”,今天我们来做一个手 ...

  6. Cloudera hadoop-2.3.0-cdh5.1.0 在Centos 6.5 下的安装

    安装前准备 1.  虚拟机3个.安装Centos 6.5, 内存设置为4GB 2.  配置yum源为163(不配置,在安装软件时,慢的要死) 3. 关闭防火墙( iptables).disabled ...

  7. 设置 zend studio 默认编码为UTF8

    今天用zend studio 打开文件时发现为乱码,这肯定是编码出了问题,我看了一下果然是编码出了问题,默认的是以GBK编码方式打开,我换utf8编码打开就好了,换编码打开的方法是: 1点击工具栏中的 ...

  8. J2EE的13个规范之JDBC

    假设让你接触一样新的东西.你可能感觉无所适从,可是假设本来就是旧事物的话,你学习起来还难吗? 一.ODBC,我们的老朋友 ODBC(Open Database Connectivity)是微软公司与数 ...

  9. javascript推断的浏览器类型

    <script> window["MzBrowser"]={};(function() { if(MzBrowser.platform) return; var ua ...

  10. Visual Studio 有哪些好用的插件?

    推荐一批绝大部分都是免费的能较好增强写代码舒适度的. .NET Demon -- (非免费)安装后可以连续编译, 如果不买License的话过期后也还有代码改动后自动保存的上好功能, 安装它之后再也不 ...