基础

在第1章中,我们简要介绍了Apache MINA。在本章中,我们将了解客户端/服务器体系结构以及有关基于MINA的服务器和客户端的详细信息。

我们还将基于TCP和UDP公开一些非常简单的服务器和客户端实现。

基于MINA的应用程序架构

最常问的问题是:“基于MINA的应用程序看起来如何”?在本文中,我们将了解基于MINA的应用程序的体系结构。试图从基于MINA的演示文稿中收集信息。

鸟瞰图:

在这里,我们可以看到MINA是您的应用程序(无论是客户端还是服务器)与底层网络层之间的粘合剂,它可以提供一个基于TCP,UDP,VM内通信甚至RS-232C串行协议的客户端。

您只需在MINA上设计应用程序,而无需处理newtork层的所有复杂性。

让我们现在深入了解细节。下图显示了MINA的内部结构,以及每个MINA组件的作用:

图片出处

从广义上讲,基于MINA的应用程序分为3层

I / O服务 - 执行实际I / O.

I / O过滤器链 - 将字节过滤/转换为所需的数据结构,反之亦然

I / O处理程序 - 这里存在实际的业务逻辑

因此,为了创建基于MINA的应用程序,您必须:

创建I / O服务 - 从已有的服务(* Acceptor)中选择或创建自己的服务。

创建过滤器链 - 从现有过滤器中选择或创建自定义过滤器以转换请求/响应。

创建I / O处理程序 - 编写业务逻辑,处理不同的消息。

通过阅读下面这两页,您可以更深入了解:

服务器架构

客户端架构

当然,MINA提供的不仅仅是这些,而且您将需要处理许多方面,例如消息编码/解码,网络配置如何扩展等等......我们将进一步研究那些下一章的各个方面。

服务器架构

我们在上一节中介绍了MINA应用程序架构。现在让我们关注服务器架构。基本上,服务器在端口上侦听传入请求,处理它们并发送回复。它还为每个客户端创建和处理会话(每当我们有基于TCP或UDP的协议时),这将在第4章中进行更广泛的解释。

IOAcceptor在网络上侦听传入的连接/数据包

对于新连接,将创建一个新会话,并在该会话中处理来自IP地址/端口组合的所有后续请求

为会话接收的所有数据包都按照图中的指定遍历过滤器链。过滤器可用于修改数据包的内容(如转换为对象,添加/删除信息等)。为了转换为/从原始字节转换为高级对象,PacketEncoder / Decoder特别有用。

最后,数据包或转换后的对象登陆IOHandler。 IOHandlers可用于满足业务需求。

会话创建

每当客户端连接MINA服务器时,我们将创建一个新会话来将持久数据存储到其中。即使未连接协议,也会创建此会话。以下架构显示了MINA如何处理传入连接:

传入消息处理

我们现在将解释MINA如何处理传入的消息。

假设已创建会话,任何新的传入消息都将导致选择器被唤醒

客户端架构

我们简要介绍了基于MINA的服务器架构,让我们看看客户端的外观。客户端需要连接到服务器,发送消息并处理响应。

客户端首先创建一个IOConnector(用于连接到Socket的MINA Construct),启动与Server的绑定

创建连接后,将创建一个会话并与Connection关联

应用程序/客户端写入会话,导致数据在遍历过滤器链后发送到服务器

从服务器接收的所有响应/消息都遍历过滤器链并落在IOHandler处进行处理

简单的TCP服务端

本教程将引导您完成构建基于MINA的程序的过程。本教程将介绍构建时间服务器。本教程需要以下先决条件:

MINA 2.x核心

JDK 1.5或更高版本

SLF4J 1.3.0或更高版本

Log4J 1.2用户:slf4j-api.jar,slf4j-log4j12.jar和Log4J 1.2.x

Log4J 1.3用户:slf4j-api.jar,slf4j-log4j13.jar和Log4J 1.3.x

java.util.logging用户:slf4j-api.jar和slf4j-jdk14.jar

重要提示:请确保使用与您的日志记录框架匹配的正确的slf4j - * .jar。

例如,slf4j-log4j12.jar和log4j-1.3.x.jar不能一起使用,并且会出现故障。

我们已经在Windows©2000 professional和linux上测试了这个程序。如果您在使用此程序时遇到任何问题,请随时与我们联系,以便与MINA开发人员交流。此外,本教程还试图保持独立于开发环境(IDE,editors..etc)。本教程适用于您熟悉的任何环境。为简洁起见,已删除编译命令和执行程序的步骤。如果您需要帮助学习如何编译或执行Java程序,请参阅Java教程

编写MINA时间服务器

我们将首先创建一个名为MinaTimeServer.java的文件。初始代码可以在下面找到:

public class MinaTimeServer {
public static void main(String[] args) {
// code will go here next
}
}

  

这段代码应该直截了当。我们只是定义了一个用于启动程序的主要方法。此时,我们将开始添加构成我们服务器的代码。首先,我们需要一个用于侦听传入连接的对象。由于该程序将基于TCP / IP,我们将在程序中添加SocketAcceptor。

import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer
{
public static void main( String[] args )
{
IoAcceptor acceptor = new NioSocketAcceptor();
}
}

  

使用NioSocketAcceptor类,我们可以继续定义处理程序类并将NioSocketAcceptor绑定到端口:

import java.net.InetSocketAddress;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer
{
private static final int PORT = 9123;
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.bind( new InetSocketAddress(PORT) );
}
}

  

如您所见,有一个对acceptor.setLocalAddress(new InetSocketAddress(PORT));的调用。此方法定义此服务器将侦听的主机和端口。最后一个方法是调用IoAcceptor.bind()。此方法将绑定到指定的端口并开始处理远程客户端。

接下来,我们在配置中添加一个过滤器。此过滤器将记录所有信息,例如新创建的会话,收到的消息,发送的消息,会话已关闭。下一个过滤器是ProtocolCodecFilter。此过滤器将二进制或协议特定数据转换为消息对象,反之亦然。我们使用现有的TextLine工厂,因为它将为您处理文本基础消息(您不必编写编解码器部分).

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset; import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer
{
public static void main( String[] args )
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.bind( new InetSocketAddress(PORT) );
}
}

  

此时,我们将定义将用于服务客户端连接的处理程序和当前时间的请求。处理程序类是必须实现接口IoHandler的类。对于几乎所有使用MINA的程序,这都成为程序的主力,因为它为来自客户端的所有传入请求提供服务。在本教程中,我们将扩展类IoHandlerAdapter。这是一个遵循适配器设计模式的类,它简化了需要编写的代码量,以满足传入实现IoHandler接口的类的要求。

import java.net.InetSocketAddress;
import java.nio.charset.Charset; import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer
{
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler( new TimeServerHandler() );
acceptor.bind( new InetSocketAddress(PORT) );
}
}

  

我们现在将添加NioSocketAcceptor配置。这将允许我们为将用于接受来自客户端的连接的套接字进行特定于套接字的设置。

import java.net.InetSocketAddress;
import java.nio.charset.Charset; import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer
{
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler( new TimeServerHandler() );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind( new InetSocketAddress(PORT) );
}
}

  

MinaTimeServer类中有2个新行。这些方法为会话设置IoHandler,输入缓冲区大小和空闲属性。将指定缓冲区大小,以告知底层操作系统为传入数据分配多少空间。第二行将指定何时检查空闲会话。在对setIdleTime的调用中,第一个参数定义在确定会话是否空闲时要检查的操作,第二个参数定义在会话被视为空闲之前必须发生的时间长度(以秒为单位)。

处理程序的代码如下所示:

import java.util.Date;

import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession; public class TimeServerHandler extends IoHandlerAdapter
{
@Override
public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
{
cause.printStackTrace();
}
@Override
public void messageReceived( IoSession session, Object message ) throws Exception
{
String str = message.toString();
if( str.trim().equalsIgnoreCase("quit") ) {
session.close();
return;
}
Date date = new Date();
session.write( date.toString() );
System.out.println("Message written...");
}
@Override
public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
{
System.out.println( "IDLE " + session.getIdleCount( status ));
}
}

  

此类中使用的方法是exceptionCaught,messageReceived和sessionIdle。应始终在处理程序中定义exceptionCaught以处理在处理远程连接的正常过程中引发的异常。如果未定义此方法,则可能无法正确报告异常。

exceptionCaught方法将只打印错误的堆栈跟踪并关闭会话。对于大多数程序,这将是标准做法,除非处理程序可以从异常条件中恢复。

messageReceived方法将从客户端接收数据并将当前时间写回客户端。如果从客户端收到的消息是“退出”,则会话将被关闭。此方法还将打印出客户端的当前时间。根据您使用的协议编解码器,传递给此方法的对象(第二个参数)以及传递给session.write(Object)方法的对象将不同。如果未指定协议编解码器,则很可能会收到IoBuffer对象,并且需要写出IoBuffer对象。

一旦会话在调用acceptor.getSessionConfig()。setIdleTime(IdleStatus.BOTH_IDLE,10);中指定的时间内保持空闲状态,将调用sessionIdle方法。

剩下要做的就是定义服务器将监听的套接字地址,并实际进行将启动服务器的调用。该代码如下所示:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset; import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer
{
private static final int PORT = 9123;
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler( new TimeServerHandler() );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind( new InetSocketAddress(PORT) );
}
}

  

试试时间服务器

此时,我们可以继续编译程序。编译完程序后,您可以运行该程序以测试发生的情况。测试程序最简单的方法是启动程序,然后telnet到程序:

Client Output

Server Output

user@myhost:~> telnet 127.0.0.1 9123 

Trying 127.0.0.1... 

Connected to 127.0.0.1. 

Escape character is '^]'. 

hello 

Wed Oct 17 23:23:36 EDT 2007 

quit 

Connection closed by foreign host. 

user@myhost:~>

MINA Time server started. 

Message written...

简单的TCP客户端

我们已经看到了客户端架构。让我们探索一个示例客户端实现。

我们将使用Sumup
Client
作为参考实现。

我们将删除样板代码并专注于重要的结构。在客户代码下面:

public static void main(String[] args) throws Throwable {
NioSocketConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(CONNECT_TIMEOUT); if (USE_CUSTOM_CODEC) {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));
} else {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
} connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.setHandler(new ClientSessionHandler(values));
IoSession session; for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress(HOSTNAME, PORT));
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
System.err.println("Failed to connect.");
e.printStackTrace();
Thread.sleep(5000);
}
} // wait until the summation is done
session.getCloseFuture().awaitUninterruptibly();
connector.dispose();
}

  

要构建客户端,我们需要执行以下操作

创建一个Connector

创建过滤器链

创建IOHandler并添加到Connector

绑定到服务器

让我们详细检查每一个

创建连接器

NioSocketConnector connector = new NioSocketConnector();

在这里,我们创建了一个NIO套接字连接器

创建过滤器链

if (USE_CUSTOM_CODEC) {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));
} else {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
}

  

我们将过滤器添加到连接器的过滤器链中。这里我们为过滤器链添加了一个ProtocolCodec。

创建IOHandler

connector.setHandler(new ClientSessionHandler(values));

  

在这里,我们创建ClientSessionHandler的一个实例,并将其设置为Connector的处理程序。

绑定到服务器

IoSession session;

for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress(HOSTNAME, PORT));
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
System.err.println("Failed to connect.");
e.printStackTrace();
Thread.sleep(5000);
}
}

  

这是最重要的东西。我们连接到远程服务器。因为,connect是一个异步任务,我们使用ConnectFuture类来了解连接何时完成。连接完成后,我们将获得相关的IoSession。要将任何消息发送到服务器,我们必须写入会话。来自服务器的所有响应/消息都应遍历Filter链,最后在IoHandler中处理。

简单的UDP服务端

我们将首先查看org.apache.mina.example.udp包中的代码。为了简化,我们只关注与MINA相关的结构。

要构建服务器,我们必须执行以下操作:

1.创建数据报套接字以侦听传入的客户端请求(请参阅MemoryMonitor.java)

2.创建一个IoHandler来处理MINA框架生成的事件(参见MemoryMonitorHandler.java)

这是解决第一点的代码片段:

NioDatagramAcceptor acceptor = new NioDatagramAcceptor();

acceptor.setHandler(new MemoryMonitorHandler(this));

  

在这里,我们创建一个NioDatagramAcceptor来监听传入的客户端请求,并设置IoHandler。变量'PORT'只是一个int。下一步是将日志记录筛选器添加到此DatagramAcceptor将使用的筛选器链中。 LoggingFilter是一个非常好的方式来查看MINA in Action。它在各个阶段生成日志语句,提供对MINA如何工作的深入了解。

DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();

chain.addLast("logger", new LoggingFilter());

  

接下来,我们将介绍UDP流量的一些更具体的代码。我们将设置接受器以重用该地址

DatagramSessionConfig dcfg = acceptor.getSessionConfig();

dcfg.setReuseAddress(true);acceptor.bind(new InetSocketAddress(PORT));

  

当然,这里需要的最后一件事是调用bind()。

IoHandler实现

我们的服务器实现有三个重要的事件

1.会话创建

2.收到消息

3.会话结束

让我们详细看看它们中的每一个

会话创建事件

@Override

public void sessionCreated(IoSession session) throws Exception {

    SocketAddress remoteAddress = session.getRemoteAddress();

    server.addClient(remoteAddress);

}

  

在会话创建事件中,我们只调用addClient()函数,该函数在内部向UI添加Tab

消息接收事件

@Override

public void messageReceived(IoSession session, Object message) throws Exception {

    if (message instanceof IoBuffer) {

        IoBuffer buffer = (IoBuffer) message;

        SocketAddress remoteAddress = session.getRemoteAddress();

        server.recvUpdate(remoteAddress, buffer.getLong());

    }

 }

  

在消息接收事件中,我们只是转储消息中收到的数据。需要发送响应的应用程序可以处理消息并将响应写入此函数中的会话。

会话结束事件

@Override

public void sessionClosed(IoSession session) throws Exception {

    System.out.println("Session closed...");

    SocketAddress remoteAddress = session.getRemoteAddress();

    server.removeClient(remoteAddress);

}

  

在Session Closed,事件中,我们只是从UI中删除Client选项卡

简单的UDP客户端

让我们看一下上一节中UDP服务器的客户端代码。

要实现客户端,我们需要执行以下操作:

1.创建套接字并连接到服务器

2.设置IoHandler

3.收集空闲内存

4.将数据发送到服务器

我们将首先查看org.apache.mina.example.udp.client java包中的MemMonClient.java文件。代码的前几行简单明了。

connector = new NioDatagramConnector();

connector.setHandler( this );

ConnectFuture connFuture = connector.connect( new InetSocketAddress("localhost", MemoryMonitor.PORT ));

  

在这里,我们创建一个NioDatagramConnector,设置处理程序并连接到服务器。我遇到的一个问题是你必须在InetSocketAddress对象中设置主机,否则似乎没有任何工作。此示例主要是在Windows XP计算机上编写和测试的,因此其他地方可能会有所不同。接下来,我们将等待客户端已连接到服务器的确认。一旦我们知道我们已连接,我们就可以开始将数据写入服务器。这是代码:

connFuture.addListener( new IoFutureListener(){

            public void operationComplete(IoFuture future) {

                ConnectFuture connFuture = (ConnectFuture)future;

                if( connFuture.isConnected() ){

                    session = future.getSession();

                    try {

                        sendData();

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                } else {

                    log.error("Not connected...exiting");

                }

            }

        });

  

在这里,我们向ConnectFuture对象添加一个监听器,当我们收到客户端连接的回调时,我们将开始写入数据。将数据写入服务器将由名为sendData的方法处理。此方法如下所示:

private void sendData() throws InterruptedException {

    for (int i = 0; i < 30; i++) {

        long free = Runtime.getRuntime().freeMemory();

        IoBuffer buffer = IoBuffer.allocate(8);

        buffer.putLong(free);

        buffer.flip();

        session.write(buffer);

        try {

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

            throw new InterruptedException(e.getMessage());

        }

    }

}

  

此方法将每秒一次将可用内存量写入服务器30秒。在这里你可以看到我们分配了一个足够大的IoBuffer来保存一个长变量,然后将可用内存量放在缓冲区中。然后翻转此缓冲区并将其写入服务器。

我们的UDP客户端实现已完成。

总结

在本章中,我们研究了基于MINA的应用程序架构,用于客户端和服务器。我们还讨论了示例TCP服务器/客户端以及UDP服务器和客户端的实现。

在接下来的章节中,我们将讨论MINA核心构造和高级主题

参考 : http://mina.apache.org/mina-project/userguide/ch2-basics/ch2-basics.html

转载于:https://www.cnblogs.com/fubinhnust/p/9940956.html

Mina Basics 02-基础的更多相关文章

  1. JavaScript学习02 基础语法

    JavaScript学习02 基础语法 JavaScript中很多基础内容和Java中大体上基本一样,所以不需要再单独重复讲了,包括: 各种算术运算符.比较运算符.逻辑运算符: if else语句.s ...

  2. linux 02 基础命令

    linux 02 基础命令 1.alias 别名 pyvip@Vip:~/demo$ alias lh="ls -lh" #将ls -lh的功能赋给lh(lh原来并没有意义)这个赋 ...

  3. .NET工作准备--02基础知识

    (已过时) 框架基础,语法基础,字符串&集合&流,常见类和接口; 02.net基础(重点) -第一部分 框架基础 1.基础概念 CTS(Common Type System),CLS( ...

  4. Python入门 —— 02基础语法

    基础语法入门学习推荐: 简明 Python 教程 下文仅为入门推荐书籍的补充与重点 多行语句:末尾使用斜杠 (  ) ,将一行分为多行 var = item1 + item2 + item3 注释: ...

  5. linux(02)基础shell命令

    Linux(02)之shell命令 一,Linux命令行的组成结构 在我们的linux启动,登陆成功之后会显示: 这就是linux的命令行的组成结构 二,常见命令 1,Linux系统命令操作语法格式 ...

  6. java面试02——基础

    1. JDK . JRE 和JVM有什么区别? JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境. JRE:Java Runtim ...

  7. Java单体应用 - Markdown - 02.基础语法

    原文地址:http://www.work100.net/training/monolithic-markdown-basic.html 更多教程:光束云 - 免费课程 基础语法 序号 文内章节 视频 ...

  8. 02 基础 卸载JDK 安装JDK Java程序运行机制

    基础 JDK:Java Development Kit(Java开发者工具 包含JRE和JVM) JRE:Java Runtime Environment(java运行时环境,包含JVM) JVM:J ...

  9. mongodb学习02基础知识

    文档 键值对的一个有序集 文档被表示为对象 文档必须有一个 _id 键 {"greeting" : "Hello, world!"}` 键 文档的键是字符串 区 ...

随机推荐

  1. 少儿编程Scratch第一讲:Scratch完美的初体验

    素材及视频下载 链接:https://pan.baidu.com/s/1qX0T2B_zczcLaCCpiRrsnA提取码:xfp8 都说未来是人工智能.计算机程式控制的时代,如何让青少年接触计算机编 ...

  2. alg-最长不重复子串

    class Solution { public: int lengthOfLongestSubstring(const std::string& s) { int max_length = 0 ...

  3. @suppressWarnings("unchecked") java 中是什么意思 (一般放dao查询方法上)

    J2SE 提供的最后一个批注是 @SuppressWarnings.该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默. 一点背景:J2SE 5.0 为 Java 语言增加 ...

  4. CNVD

    漏洞编号 漏洞名称 漏洞积分 奖励时间 CNVD-2020-15798 中国平乐县委员会组织部网站管理系统登录存在弱口令漏洞 1.0 2020-03-09 09:58:46.0 CNVD-2020-1 ...

  5. AJ学IOS(29)UI之Quartz2D自定义下载控件_画各种图形

    AJ分享,必须精品 效果 自定义控件过程 主要过程在上一篇里有介绍了,这里主要介绍下代码实现 先做好要放的view 然后实现呢主要就是四步: 1:获取上下文 2:拼接路径 3:把路径添加到上下文. 4 ...

  6. PLSQL Developer 中文乱码踩坑记

    环境 操作系统版本: Windows 7 PL/SQL 版本: 12.0.1.1814 原因 由于 Oracle 服务器端和客户端字符集编码不一致引起的. 注意点 写在最前面,减少踩坑!!! 网上教程 ...

  7. Daily Scrum 12/24/2015

    Process: Zhaoyang: Some UI change and compile the Caffe in the IOS. Yandong: Do some code integratio ...

  8. ORA-0245

    经常有客户报错ORA-0245 1.11.2 rac环境, rman存在snap控制文件路径,默认是文件系统[非共享,导致备份控制文件报错] 解决方法:将snap路径配置到ASM磁盘组共享路径[nfs ...

  9. 为什么选择python?

    Why python? 那些最好的程序员不是为了得到更高的薪水或者得到公众的仰慕而编程,他们只是觉得这是一件有趣的事情. —— Linux 之父 Linux Torvalds 作为一个使用主义的学习者 ...

  10. 【题解】P2831 愤怒的小鸟 - 状压dp

    P2831愤怒的小鸟 题目描述 \(Kiana\) 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 \((0,0)\) 处,每次 \(Kiana\) 可以 ...