如何让你的传输更安全——NIO模式和BIO模式实现SSL协议通信
对于SSL/TLS协议,如果要每个开发者都自己去实现显然会带来不必要的麻烦,正是为了解决这个问题Java为广大开发者提供了Java安全套接字扩展——JSSE,它包含了实现Internet安全通信的一系列包的集合,是SSL和TLS的纯Java实现,同时它是一个开放的标准,每个公司都可以自己实现JSSE,通过它可以透明地提供数据加密、服务器认证、信息完整性等功能,就像使用普通的套接字一样使用安全套接字,大大减轻了开发者的负担,使开发者可以很轻松将SSL协议整合到程序中,并且JSSE能将安全隐患降到了最低点。
在用JSSE实现SSL通信过程中主要会遇到以下类和接口,由于过程中涉及到加解密、密钥生成等运算的框架和实现,所以也会间接用到JCE包的一些类。如图为JSSE接口的主要类图:
JSSE API主要类图
① 通信核心类——SSLSocket和SSLServerSocket。对于使用过socket进行通信开发的朋友比较好理解,它们对应的就是Socket与ServerSocket,只是表示实现了SSL协议的Socket和ServerSocket,同时它们也是Socket与ServerSocket的子类。SSLSocket负责的事情包括设置加密套件、管理SSL会话、处理握手结束时间、设置客户端模式或服务器模式。
② 客户端与服务器端Socket工厂——SSLSocketFactory和SSLServerSocketFactory。在设计模式中工厂模式是专门用于生产出需要的实例,这里也是把SSLSocket、SSLServerSocket对象创建的工作交给这两个工厂类。
③ SSL会话——SSLSession。安全通信握手过程需要一个会话,为了提高通信的效率,SSL协议允许多个SSLSocket共享同一个SSL会话,在同一个会话中,只有第一个打开的SSLSocket需要进行SSL握手,负责生成密钥及交换密钥,其余SSLSocket都共享密钥信息。
④ SSL上下文——SSLContext。它是对整个SSL/TLS协议的封装,表示了安全套接字协议的实现。主要负责设置安全通信过程中的各种信息,例如跟证书相关的信息。并且负责构建SSLSocketFactory、SSLServerSocketFactory和SSLEngine等工厂类。
⑤ SSL非阻塞引擎——SSLEngine。假如你要进行NIO通信,那么将使用这个类,它让通过过程支持非阻塞的安全通信。
⑥ 密钥管理器——KeyManager。此接口负责选择用于证实自己身份的安全证书,发给通信另一方。KeyManager对象由KeyManagerFactory工厂类生成。
⑦ 信任管理器——TrustManager。此接口负责判断决定是否信任对方的安全证书,TrustManager对象由TrustManagerFactory工厂类生成。
⑧ 密钥证书存储设施——KeyStore。这个对象用于存放安全证书,安全证书一般以文件形式存放,KeyStore负责将证书加载到内存。
通过上面这些类就可以完成SSL协议的安全通信了,在利用SSL/TLS进行安全通信时,客户端跟服务器端都必须要支持SSL/TLS协议,不然将无法进行通信。而且客户端和服务器端都可能要设置用于证实自己身份的安全证书,并且还要设置信任对方的哪些安全证书。
关于身份认证方面有个名词叫客户端模式,一般情况客户端要对服务器端的身份进行验证,但是无需向服务器证实自己的身份,这样不用向对方证实自己身份的通信端我们就说它处于客户模式,否则成它处于服务器模式。SSLSocket的setUseClientMode(Boolean mode)方法可以设置客户端模式或服务器模式。
BIO模式实现SSL通信
使用BIO模式实现SSL通信除了对一些证书密钥生成外,只需使用JDK自带的SSLServerSocket和SSLSocket等相关类的API即可实现,简洁直观。
① 解决证书问题。
一般而言作为服务器端必须要有证书以证明这个服务器的身份,并且证书应该描述此服务器所有者的一些基本信息,例如公司名称、联系人名等。证书由所有人以密码形式签名,基本不可伪造,证书获取的途径有两个:一是从权威机构购买证书,权威机构担保它发出的证书的真实性,而且这个权威机构被大家所信任,进而你可以相信这个证书的有效性;另外一个是自己用JDK提供的工具keytool创建一个自我签名的证书,这种情况下一般是我只想要保证数据的安全性与完整性,避免数据在传送的过程中被窃听或篡改,此时身份的认证已不重要,重点已经在端与端传输的秘密性上,证书的作用只体现在加解密签名。
另外,关于证书的一些概念在这里陈述,一个证书是一个实体的数字签名,这个实体可以是一个人、一个组织、一个程序、一个公司、一个银行,同时证书还包含这个实体的公共钥匙,此公共钥匙是这个实体的数字关联,让所有想同这个实体发生信任关系的其他实体用来检验签名。而这个实体的数字签名是实体信息用实体的私钥加密后的数据,这条数据可以用这个实体的公共钥匙解密,进而鉴别实体的身份。这里用到的核心算法是非对称加密算法。
SSL协议通信涉及密钥储存的文件格式比较多,很容易搞混,例如xxx.cer、xxx.pfx、xxx.jks、xxx.keystore、xxx.truststore等格式文件。如图,搞清楚他们有助于理解后面的程序,.cer格式文件俗称证书,但这个证书中没有私钥,只包含了公钥;.pfx格式文件也称为证书,它一般供浏览器使用,而且它不仅包含了公钥,还包含了私钥,当然这个私钥是加密的,不输入密码是解不了密的;.jks格式文件表示java密钥存储器(java key store),它可以同时容纳N个公钥跟私钥,是一个密钥库;.keystore格式文件其实跟.jks基本是一样的,只是不同公司叫法不太一样,默认生成的证书存储库格式;.truststore格式文件表示信任证书存储库,它仅仅包含了通信对方的公钥,当然你可以直接把通信对方的jks作为信任库(就算如此你也只能知道通信对方的公钥,要知道密钥都是加密的,你无从获取,只要算法不被破解)。有些时候我们需要把pfx或cert转化为jks以便于用java进行ssl通信,例如一个银行只提供了pfx证书,而我们想用java进行ssl通信时就要将pfx转化为jks格式。
密钥存储文件格式
按照理论上,我们一共需要准备四个文件,两个keystore文件和两个truststore文件,通信双方分别拥有一个keystore和一个truststore,keystore用于存放自己的密钥和公钥,truststore用于存放所有需要信任方的公钥。这里为了方便直接使用jks即keystore替代truststore(免去证书导来导去),因为对方的keystore包含了自己需要的信任公钥。
下面使用jdk自带的工具分别生成服务器端证书,通过如下命令并输入姓名、组织单位名称、组织名称、城市、省份、国家信息即可生成证书密码为tomcat的证书,此证书存放在密码也为tomcat的tomcat.jks证书存储库中。如果你继续创建证书将继续往tomcat.jks证书存储库中添加证书。如果你仅仅输入keytool -genkey -alias tomcat -keyalg RSA -keypass tomcat -storepass tomcat,不指定证书存储库的文件名及路径,则工具会在用户的home directory目录下生产一个“.keystore”文件作为证书存储库。
类似的,客户端证书也用此方式进行生成。如下
② 服务端TomcatSSLServer.java
public class TomcatSSLServer {
private static final String SSL_TYPE = "SSL";
private static final String KS_TYPE = "JKS";
private static final String X509 = "SunX509";
private final static int PORT = 443;
private static TomcatSSLServer sslServer;
private SSLServerSocket svrSocket;
public static TomcatSSLServer getInstance() throws Exception {
if (sslServer == null) {
sslServer = new TomcatSSLServer();
}
return sslServer;
}
private TomcatSSLServer() throws Exception{
SSLContext sslContext = createSSLContext();
SSLServerSocketFactory serverFactory = sslContext.getServerSocketFactory();
svrSocket =(SSLServerSocket) serverFactory.createServerSocket(PORT);
svrSocket.setNeedClientAuth(true);
String[] supported = svrSocket.getEnabledCipherSuites();
svrSocket.setEnabledCipherSuites(supported);
}
private SSLContext createSSLContext() throws Exception{
KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
String serverKeyStoreFile = "c:\\tomcat.jks";
String svrPassphrase = "tomcat";
char[] svrPassword = svrPassphrase.toCharArray();
KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);
kmf.init(serverKeyStore, svrPassword);
String clientKeyStoreFile = "c:\\client.jks";
String cntPassphrase = "client";
char[] cntPassword = cntPassphrase.toCharArray();
KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
clientKeyStore.load(new FileInputStream(clientKeyStoreFile),cntPassword);
tmf.init(clientKeyStore);
SSLContext sslContext = SSLContext.getInstance(SSL_TYPE);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext;
}
public void startService() {
SSLSocket cntSocket = null;
BufferedReader ioReader = null;
PrintWriter ioWriter = null;
String tmpMsg = null;
while( true ) {
try {
cntSocket =(SSLSocket) svrSocket.accept();
ioReader = new BufferedReader(new InputStreamReader(cntSocket.getInputStream()));
ioWriter = new PrintWriter(cntSocket.getOutputStream());
while ( (tmpMsg = ioReader.readLine()) != null) {
System.out.println("客户端通过SSL协议发送信息:"+tmpMsg);
tmpMsg="欢迎通过SSL协议连接";
ioWriter.println(tmpMsg);
ioWriter.flush();
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(cntSocket != null) cntSocket.close();
} catch(Exception ex) {ex.printStackTrace();}
}
}
}
public static void main(String[] args) throws Exception {
TomcatSSLServer.getInstance().startService();
}
}
基本顺序是先得到一个SSLContext实例,再对SSLContext实例进行初始化,密钥管理器及信任管理器作为参数传入,证书管理器及信任管理器按照指定的密钥存储器路径和密码进行加载。接着设置支持的加密套件,最后让SSLServerSocket开始监听客户端发送过来的消息。
③ 客户端TomcatSSLClient.java
public class TomcatSSLClient {
private static final String SSL_TYPE = "SSL";
private static final String X509 = "SunX509";
private static final String KS_TYPE = "JKS";
private SSLSocket sslSocket;
public TomcatSSLClient(String targetHost,int port) throws Exception {
SSLContext sslContext = createSSLContext();
SSLSocketFactory sslcntFactory =(SSLSocketFactory) sslContext.getSocketFactory();
sslSocket = (SSLSocket) sslcntFactory.createSocket(targetHost, port);
String[] supported = sslSocket.getSupportedCipherSuites();
sslSocket.setEnabledCipherSuites(supported);
}
private SSLContext createSSLContext() throws Exception{
KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
String clientKeyStoreFile = "c:\\client.jks";
String cntPassphrase = "client";
char[] cntPassword = cntPassphrase.toCharArray();
KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
clientKeyStore.load(new FileInputStream(clientKeyStoreFile),cntPassword);
String serverKeyStoreFile = "c:\\tomcat.jks";
String svrPassphrase = "tomcat";
char[] svrPassword = svrPassphrase.toCharArray();
KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);
kmf.init(clientKeyStore, cntPassword);
tmf.init(serverKeyStore);
SSLContext sslContext = SSLContext.getInstance(SSL_TYPE);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext;
}
public String sayToSvr(String sayMsg) throws IOException{
BufferedReader ioReader = new BufferedReader(new InputStreamReader(
sslSocket.getInputStream()));
PrintWriter ioWriter = new PrintWriter(sslSocket.getOutputStream());
ioWriter.println(sayMsg);
ioWriter.flush();
return ioReader.readLine();
}
public static void main(String[] args) throws Exception {
TomcatSSLClient sslSocket = new TomcatSSLClient("127.0.0.1",443);
BufferedReader ioReader = new BufferedReader(new InputStreamReader(System.in));
String sayMsg = "";
String svrRespMsg= "";
while( (sayMsg = ioReader.readLine())!= null ) {
svrRespMsg = sslSocket.sayToSvr(sayMsg);
if(svrRespMsg != null && !svrRespMsg.trim().equals("")) {
System.out.println("服务器通过SSL协议响应:"+svrRespMsg);
}
}
}
}
客户端的前面操作基本跟服务器端的一样,先创建一个SSLContext实例,再用密钥管理器及信任管理器对SSLContext进行初始化,当然这里密钥存储的路径是指向客户端的client.jks。接着设置加密套件,最后使用SSLSocket进行通信。
注意服务器端有行代码svrSocket.setNeedClientAuth(true);它是非常重要的一个设置方法,用于设置是否验证客户端的身份。假如我们把它注释掉或设置为false,此时客户端将不再需要自己的密钥管理器,即服务器不需要通过client.jks对客户端的身份进行验证,把密钥管理器直接设置为null也可以跟服务器端进行通信。
最后谈谈信任管理器,它的职责是决定是否信任远端的证书,那么它凭借什么去判断呢?如果不显式设置信任存储器的文件路径,将遵循如下规则:①如果系统属性javax.net.ssl.truststore指定了truststore文件,那么信任管理器将去jre路径下的lib/security目录寻找这个文件作为信任存储器;②如果没设置①中的系统属性,则去寻找一个%java_home%/lib/security/jssecacerts文件作为信任存储器;③如果jssecacerts不存在而cacerts存在,则cacerts作为信任存储器。
至此,一个利用JSSE实现BIO模式的SSL协议通信的例子已完成。
NIO模式实现SSL通信
在jdk1.5之前,由于互联网还没快速发展起来,对于常见的应用使用BIO模式即可满足需求,而这时jdk的JSSE接口也仅仅只是提供了基于流的安全套接字,但随着网络的发展,BIO模型明显已经不足以满足一些高并发多连接接入的场景,体现在机器上就是要不同的线程模型以至于能最大程度地压榨计算器的运算,于是此时引入了NIO模式,原来基于流的阻塞模式IO只需使用SSLServerSocket和SSLSocket即可完成SSL通信,而JDK中对于NIO模式并没有提供与之对应的“SSLServerSocketChannel”和“SSLSocketChannel”,这是由NIO模式决定的,很难设计一个“SSLServerSocketChannel”类与Selector交互,强行地引入将带来更多的问题,这更像解决一个问题引入了三个问题,并且还会导致API更加复杂,另外Nio细节也不适合屏蔽,它应该由应用开发层去控制。所有的这些都决定了jdk不会也不能有NIO安全套接字。
jdk1.5为了支持NIO模式的SSL通信,引入了SSLEngine引擎,它负责了底层ssl协议的握手、加密、解密、关闭会话等等操作,根据前面SSL\TLS协议章节我们知道SSL协议在握手阶段会有十三个步骤,在握手过程中不会有应用层的数据传输,只有在握手认证完成后双方才会进行应用层数据交换。大致把握手分为四阶段,①客户端发送hello消息;②服务端响应hello消息且发送附带的认证消息;③客户端向客户端发送证书和其他认证消息;④完成握手。
SSLEngine在握手过程中定义了五种HandshakeStatus状态,【NEED_WRAP、NEED_UNWRAP、NEED_TASK、FINISHED、NOT_HANDSHAKING】,通过他们实现协议通信过程中状态管理,按照四个阶段其中的状态是这样转换的,刚开始它的状态为NEED_UNWRAP,表示等待解包,读取客户端数据并解包后,把状态置为NEED_WRAP,表示等待打包,打包完向客户端响应数据后状态又重置为NEED_UNWRAP,如此切换直至握手完成时状态被置为FINISHED,表示握手已经完成,此后状态置为NOT_HANDSHAKING,表示已经不在握手阶段了。另外还有一个NEED_TASK状态表示SSLEngine有额外的任务需要执行,而且这些任务都是比较耗时或者可能阻塞的,例如访问密钥文件、连接远程证书认证服务、密钥管理器使用何种认证方式作为客户端认证等等操作。为了保证NIO特性,这些操作不能直接由当前线程操作,当前线程只会把状态改为NEED_TASK,后面处理线程会交由其他线程处理。
看看程序是如何使用nio模式进行ssl通信的,主要看服务端如何实现。
public class NioSSLServer {
private SSLEngine sslEngine;
private Selector selector;
private SSLContext sslContext;
private ByteBuffer netInData;
private ByteBuffer appInData;
private ByteBuffer netOutData;
private ByteBuffer appOutData;
private static final String SSL_TYPE = "SSL";
private static final String KS_TYPE = "JKS";
private static final String X509 = "SunX509";
private final static int PORT = 443;
public void run() throws Exception {
createServerSocket();
createSSLContext();
createSSLEngine();
createBuffer();
while (true) {
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey selectionKey = it.next();
it.remove();
handleRequest(selectionKey);
}
}
}
private void createBuffer() {
SSLSession session = sslEngine.getSession();
appInData = ByteBuffer.allocate(session.getApplicationBufferSize());
netInData = ByteBuffer.allocate(session.getPacketBufferSize());
appOutData = ByteBuffer.wrap("Hello\n".getBytes());
netOutData = ByteBuffer.allocate(session.getPacketBufferSize());
}
private void createSSLEngine() {
sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
}
private void createServerSocket() throws Exception {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
selector = Selector.open();
ServerSocket serverSocket = serverChannel.socket();
serverSocket.bind(new InetSocketAddress(PORT));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
private void createSSLContext() throws Exception {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
String serverKeyStoreFile = "c:\\tomcat.jks";
String svrPassphrase = "tomcat";
char[] svrPassword = svrPassphrase.toCharArray();
KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
serverKeyStore.load(new FileInputStream(serverKeyStoreFile),svrPassword);
kmf.init(serverKeyStore, svrPassword);
String clientKeyStoreFile = "c:\\client.jks";
String cntPassphrase = "client";
char[] cntPassword = cntPassphrase.toCharArray();
KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
clientKeyStore.load(new FileInputStream(clientKeyStoreFile),cntPassword);
tmf.init(clientKeyStore);
sslContext = SSLContext.getInstance(SSL_TYPE);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
}
private void handleRequest(SelectionKey key) throws Exception {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel channel = ssc.accept();
channel.configureBlocking(false);
doHandShake(channel);
} else if (key.isReadable()) {
if (sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
SocketChannel sc = (SocketChannel) key.channel();
netInData.clear();
appInData.clear();
sc.read(netInData);
netInData.flip();
SSLEngineResult engineResult = sslEngine.unwrap(netInData,appInData);
doTask();
if (engineResult.getStatus() == SSLEngineResult.Status.OK) {
appInData.flip();
System.out.println(new String(appInData.array()));
}
sc.register(selector, SelectionKey.OP_WRITE);
}
} else if (key.isWritable()) {
SocketChannel sc = (SocketChannel) key.channel();
netOutData.clear();
SSLEngineResult engineResult = sslEngine.wrap(appOutData,netOutData);
doTask();
netOutData.flip();
while (netOutData.hasRemaining())
sc.write(netOutData);
sc.register(selector, SelectionKey.OP_READ);
}
}
private void doHandShake(SocketChannel sc) throws IOException {
boolean handshakeDone = false;
sslEngine.beginHandshake();
HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();
while (!handshakeDone) {
switch (hsStatus) {
case FINISHED:
break;
case NEED_TASK:
hsStatus = doTask();
break;
case NEED_UNWRAP:
netInData.clear();
sc.read(netInData);
netInData.flip();
do {
SSLEngineResult engineResult = sslEngine.unwrap(netInData,appInData);
hsStatus = doTask();
} while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP
&& netInData.remaining() > 0);
netInData.clear();
break;
case NEED_WRAP:
SSLEngineResult engineResult = sslEngine.wrap(appOutData,netOutData);
hsStatus = doTask();
netOutData.flip();
sc.write(netOutData);
netOutData.clear();
break;
case NOT_HANDSHAKING:
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
handshakeDone = true;
break;
}
}
}
private HandshakeStatus doTask() {
Runnable task;
while ((task = sslEngine.getDelegatedTask()) != null) {
new Thread(task).start();
}
return sslEngine.getHandshakeStatus();
}
public static void main(String[] args) throws Exception {
new NioSSLServer().run();
}
}
根据程序大致说明程序过程,①创建用于非阻塞通信的主要对象ServerSocketChannel和Selector、绑定端口、注册接收事件;②创建SSL上下文,此过程主要是根据前面创建好的密钥存储器tomcat.jks和client.jks去创建密钥管理器和信任管理器,并通过密钥管理器和信任管理器去初始化SSL上下文;③创建SSL引擎,主要通过SSL上下文创建SSL引擎,并将它设为不验证客户端身份;④创建缓冲区,使用SSL协议通信的过程中涉及到四个缓冲区,如下图,netInData表示实际从网络接收到的字节流,它是包含了SSL协议和应用数据的字节流,通过SSLEngine引擎进行认证解密等处理后的应用可直接使用的数据则用appInData表示,同样地,应用层要传递的数据为appOutData,而经过SSLEngine引擎认证加密处理后放到网络中传输的字节流则为netOutData;⑤接下去开始监听处理客户端的连接请求,一旦有可接受的连接则会先进行SSL协议握手,完成握手后才能进行传输,即对通道的读写操作。
握手操作是一个比较复杂的过程,必须要保证握手完成后才能进行应用层数据交换,所以这里使用一个while循环不断做握手操作直到完成。前面已经介绍了握手阶段会有五种状态,【NEED_WRAP、NEED_UNWRAP、NEED_TASK、FINISHED、NOT_HANDSHAKING】,由于SSL协议握手的报文都由SSLEngine引擎自动生成,所以我们只需对不同状态做不同操作即可,例如,NEED_UNWRAP状态则调用unwrap方法,NEED_WRAP则调用wrap方法,NEED_TASK则使用其他线程处理委托任务,握手报文自动由这些方法完成,当握手完成后状态则被置为FINISHED,接着状态变为NOT_HANDSHAKING,表示已经不在握手阶段了,已经可以进行应用层通信了,此时整个SSL握手结束。
应用层安全通信过程其实也是靠SSLEngine引擎的unwrap和wrap方法对数据进行加解密并且对通信双方进行认证,例如应用层读操作是将netInData和appInData传入unwrap方法,处理后的appInData即为应用需要的数据,而写操作则是将appOutData和netOutData传入wrap方法,处理后的netOutData即为传输给对方的数据。
至此,通过在网络与应用直接增加一个SSLEngine引擎层,则实现了安全通信,并且使用了NIO模式让服务端拥有更加出色的处理性能。
如何让你的传输更安全——NIO模式和BIO模式实现SSL协议通信的更多相关文章
- 如何让你的传输更安全--基于SSL协议的通信模式
之前发表在另一个平台的文章http://www.jointforce.com/jfperiodical/article/1218,现在发表到自己的博客上. 对于SSL/TLS协议,如果要每个开发者都自 ...
- (五:NIO系列) Reactor模式
出处:Reactor模式 本文目录 1. 为什么是Reactor模式 2. Reactor模式简介 3. 多线程IO的致命缺陷 4. 单线程Reactor模型 4.1. 什么是单线程Reactor呢? ...
- Eclipse中开发环境也想把Tomcat 的默认BIO模式改为NIO模式
1.1 问题 有时候,开发环境我们也想把Tomcat 的默认BIO模式改为NIO模式,该如何改呢? 1.2 方案 通过eclipse里面的server.xml进行修改. 1.3 步骤 首先我们来一起看 ...
- 加密传输SSL协议8_Apache服务器的安装
学习了那么多的理论的知识,下面通过在Apache服务器中安装和使用SSL协议,实现安全传输,但是首先要安装好Apache服务器. Apache服务器的安装 Linux下所有的软件的原码的安装都是三部曲 ...
- 加密传输SSL协议1_OpenSSL的安装
终于在自己不断的奋斗之后,来到科大的一波考试过去了,但是为了不使自己过于放松,回顾一下之前的东西,做一下笔记.所以新开一个专题笔记: Using Apache with SSL 引入:首先我们在平时的 ...
- tomcat7.0.27的bio,nio.apr高级运行模式(转)
一 前言 tomcat的运行模式有3种.修改他们的运行模式.3种模式的运行是否成功,可以看他的启动控制台,或者启动日志.或者登录他们的默认页面http://localhost:8080/查看其中的服务 ...
- 同步异步阻塞非阻塞Reactor模式和Proactor模式 (目前JAVA的NIO就属于同步非阻塞IO)
在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...
- 【Tomcat】Tomcat Connector的三种运行模式【bio、nio、apr】
Tomcat Connector(Tomcat连接器)有bio.nio.apr三种运行模式 bio bio(blocking I/O,阻塞式I/O操作),表示Tomcat使用的是传统的Java I/O ...
- Tomcat 6 支持 NIO -- Tomcat的四种基于HTTP协议的Connector性能比较(转载)
Tomcat从5.5版本开始,支持以下四种Connector的配置分别为: <Connector port="8081" protocol="org.apache. ...
随机推荐
- Maven仓库概述
什么是Maven仓库 在Maven世界中,任何一个依赖.插件或项目构建的输出,都可以称为构建.由于Maven引入了坐标机制,任何一个构建都可以由其坐标唯一标识.坐标是一个构建在Maven世界中的逻辑表 ...
- Spark-1.6.0之Application运行信息记录器JobProgressListener
JobProgressListener类是Spark的ListenerBus中一个很重要的监听器,可以用于记录Spark任务的Job和Stage等信息,比如在Spark UI页面上Job和Stage运 ...
- BeanUtils Exception 之 FastHashMap
这里仅仅是为了记录一件十分奇怪的事情,在使用BeanUtils的过程中,所有的依赖包都添加了, common logging common collections ··· 在为boolean 这种基本 ...
- 剑指Offer——Trie树(字典树)
剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种的单词.对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位 ...
- OpenCV相机标定
标签(空格分隔): Opencv 相机标定是图像处理的基础,虽然相机使用的是小孔成像模型,但是由于小孔的透光非常有限,所以需要使用透镜聚焦足够多的光线.在使用的过程中,需要知道相机的焦距.成像中心以及 ...
- 1076. Forwards on Weibo (30) - 记录层的BFS改进
题目如下: Weibo is known as the Chinese version of Twitter. One user on Weibo may have many followers, a ...
- 在Debian/Ubuntu系统中安装*.sh与*.bin文件
在Debian/Ubuntu系统中安装*.sh与*.bin文件的基本方法.一,安装*.sh文件运行命令行至文件目录下,执行:sudo sh *.sh直接运行在命令行中执行:sudo chmod +x ...
- 【java集合框架源码剖析系列】java源码剖析之java集合中的折半插入排序算法
注:关于排序算法,博主写过[数据结构排序算法系列]数据结构八大排序算法,基本上把所有的排序算法都详细的讲解过,而之所以单独将java集合中的排序算法拿出来讲解,是因为在阿里巴巴内推面试的时候面试官问过 ...
- Android初级教程理论知识(第八章网络编程二)
HttpClient 发送get请求 创建一个客户端对象 HttpClient client = new DefaultHttpClient(); 创建一个get请求对象 HttpGet hg = n ...
- Hadoop:Hadoop简介及环境配置
http://blog.csdn.net/pipisorry/article/details/51243805 Hadoop简介 下次写上... 皮皮blog 配置hadoop环境可能出现的问题 每次 ...