如何让你的传输更安全——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模式让服务端拥有更加出色的处理性能。

==========广告时间==========

鄙人的新书《Tomcat内核设计剖析》已经在京东预售了,有需要的朋友可以到 https://item.jd.com/12185360.html 进行预定。感谢各位朋友。

=========================

时间: 2024-10-03 06:47:40

如何让你的传输更安全——NIO模式和BIO模式实现SSL协议通信的相关文章

美通社CEO:更看好新媒体订阅收费模式

美通社CEO Ninan Chacko( 腾讯科技配图)腾讯科技 宗秀倩 3月8日报道"我更 看好订阅收费模式".面对新媒体内容免费还是收费的争论,美通社CEO Ninan Chacko日前向腾讯科技给出了自己的答案.他强调,在收费模式上,订阅收入和广告收入应该找到一个平衡点.去年年底,有分析师预测,<纽约时报>2012年数字订阅收入首次超过广告收入.这给予Ninan Chacko坚持看好订阅收费模式很大的信心.个性与同步的平衡美通社于1954年成立,自2002年进入中国大

FTP传输Port模式和 Passive模式

当你对一个FTP问题进行排错时候,你首先要问的一个问题是使用的是port模式的还是passive模式.因为这两种行为迥异,所以这两种模式引起的问题也不同:在过去,客户端缺省为active(port)模式:近来,由于Port模式的安全问题,许多客户端的FTP应用缺省为Passive模式. 数据传输的连接: port模式是服务器用20端口主动连接客户端的临时端口. Passive模式是客户端用临时分配的端口要要求服务器也分配一个临时端口来回应它. 模式体现在服务器是主动还是被动. FTP Port模

我的2017年文章汇总——Java及中间件篇

2018即将到来,大家看着2017给自己制定的计划有没有感慨?当你觉得过去一年没有什么进步时,那么请行动起来,能开始总是好的. 近期准备把过去一年写的文章按照分类重新整理推送一遍,包括:"分布式"."机器学习"."深度学习"."NLP"."Java深度"."Java并发核心"."JDK源码"."Tomcat内核". 本篇推送Java及中间件相关文章

SSL与TLS的区别以及介绍(转)

  SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层.SSL通过互相认证.使用数字签名确保完整性.使用加密确保私密性,以实现客户端和服务器之间的安全通讯.该协议由两层组成:SSL记录协议和SSL握手协议. TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性.该协议由两层组成:TLS记录协议和TLS握手协议. SSL是Netscape开发的专门用户保护W

非阻塞通道

非阻塞通道 非阻塞通道用于负责将数据读到缓冲区中,或将数据从缓冲区中写入,这个类的作用主要是用于屏蔽非SSL与SSL读写操作细节的不同,这个类实现了ByteChannel接口,此接口只有write.read两个操作字节流的方法,细节正是屏蔽在这两个操作中,例如非SSL通信时,报文本来就是明文,可直接读取,而对于SSL通信,报文属于加密后的密文,解密后才是真正需要的报文.同样地,对于非SSL通信直接写入,而对于SSL通信应该把报文加密后再套接字通道.下面看看这两种通道的详细解析. 非SSL通道--

【Android】Android网络编程概述

Android网络编程概述 原文来自:http://blog.csdn.net/kieven2008/article/details/8210737 首先,应该了解的几个问题: 1)Android平台网络相关API接口  a) java.net.*(标准Java接口)  java.net.*提供与联网有关的类,包括流.数据包套接字(socket).Internet协议.常见Http处理等.比如:创建URL,以及URLConnection/HttpURLConnection对象.设置链接参数.链接

Linux下ftp+ssl实现ftps

ftps与sftp: FTPS是借助ssl协议加密,ssl是为http/smtp等加密设计的::SFTP是借助ssh加密,ssh是为telnet/ftp等加密.建立传输通道而设计的.ssh建立传输通道就是为了加密和传输,而且这个通道是可以用来远程登录.创建加密通道对文件进行加密. 从原理上简单的讲:FTPS是ftp-over-ssl的意思,即ftp借助ssl协议加密传输,不但要用ftp服务器还要用ssl协议加密.sftp协议是ssh中的一条独立的协议,利用sftp服务器就可以传输数据. 下面笔者

Java NIO 系列教程

Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API.本系列教程将有助于你学习和理解Java NIO.感谢并发编程网的翻译和投递.  (关注ITeye官微,随时随地查看最新开发资讯.技术文章.)  [本文转载于 Java NIO 系列教程] Java NIO提供了与标准IO不同的IO工作方式:  Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channe

Java NIO 系列教程(转)

原文中说了最重要的3个概念,Channel 通道Buffer 缓冲区Selector 选择器其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞模式才加入的东西.以前的流总是堵塞的,一个线程只要对它进行操作,其它操作就会被堵塞,也就相当于水管没有阀门,你伸手接水的时候,不管水到了没有,你就都只能耗在接水(流)上.nio的Channel的加入,相当于增加了水龙头(有阀门),虽然一个时刻也只能接一个水管的水,但依赖轮换策略,在水量不大的时候,各