连接管理类ConnectionToAppleServer,话说客户端服务器都准备完了,该连接了,连接到服务器的类设计方式是:由于连接到服务器是一个动作对象,该对象有很多方法,并且要实例化各种连接配置,所以顶级类设计为了一个抽象类。
连接这个类使用了TLS加密连接,为了掩饰本人不好的加密基础,这个类的源码如下
/* KeyManagerFactory 使用的算法,默认是sun公司的suunx509,除非特定的ibM环境才换成ibmx509 */
private static final String ALGORITHM = ((Security.getProperty(“ssl.KeyManagerFactory.algorithm”) == null) ? “sunx509″ : Security.getProperty(“ssl.KeyManagerFactory.algorithm”));
/* 使用TLS协议 */
private static final String PROTOCOL = “TLS”;
/* PKCS12,对应p12证书 */
public static final String KEYSTORE_TYPE_PKCS12 = “PKCS12″;
/* 数字证书库*/
public static final String KEYSTORE_TYPE_JKS = “JKS”;
static {
//加密使用的是BouncyCastleProvider
Security.addProvider(new BouncyCastleProvider());
}
private KeyStore keyStore;
private SSLSocketFactory socketFactory;
private AppleServer server;
//三个属性
//属性的set/get方法和类构造方法就不在一一说。
//创建SSLSocketFacotry
protected SSLSocketFactory createSSLSocketFactoryWithTrustManagers(TrustManager[] trustManagers) throws KeystoreException {
logger.debug(“Creating SSLSocketFactory”);
// 获取key管理器并初始化
try {
KeyStore keystore = getKeystore();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(ALGORITHM);
try {
char[] password = KeystoreManager.getKeystorePasswordForSSL(server);
kmf.init(keystore, password);
} catch (Exception e) {
e = KeystoreManager.wrapKeystoreException(e);
throw e;
}
// 获取 SSLContext 去创建SSLSocketFactory
SSLContext sslc = SSLContext.getInstance(PROTOCOL);
//初始化SSLContext
sslc.init(kmf.getKeyManagers(), trustManagers, null);
//获取创建工厂
return sslc.getSocketFactory();
} catch (Exception e) {
throw new KeystoreException(“Keystore exception: ” + e.getMessage(), e);
}
}
public SSLSocketFactory createSSLSocketFactory() throws KeystoreException {
return createSSLSocketFactoryWithTrustManagers(new TrustManager[] { new ServerTrustingTrustManager() });
}
//ServerTrustingTrustManager信任所有服务器
public SSLSocketFactory getSSLSocketFactory() throws KeystoreException {
if (socketFactory == null) socketFactory = createSSLSocketFactory();
return socketFactory;
}
//获取SSLSocket连接
public SSLSocket getSSLSocket() throws KeystoreException, CommunicationException {
SSLSocketFactory socketFactory = getSSLSocketFactory();
logger.debug(“Creating SSLSocket to ” + getServerHost() + “:” + getServerPort());
try {
if (ProxyManager.isUsingProxy(server)) {
//如果服务器配置了代理就去校验
return tunnelThroughProxy(socketFactory);
} else {
//否则创建一个
return (SSLSocket) socketFactory.createSocket(getServerHost(), getServerPort());
}
} catch (Exception e) {
throw new CommunicationException(“Communication exception: ” + e, e);
}
}
//校验原有的服务器代理信息
private SSLSocket tunnelThroughProxy(SSLSocketFactory socketFactory) throws UnknownHostException, IOException {
SSLSocket socket;
//如果已经设置那么根据设置好的地址和端口
String tunnelHost = ProxyManager.getProxyHost(server);
Integer tunnelPort = ProxyManager.getProxyPort(server);
Socket tunnel = new Socket(tunnelHost, tunnelPort);
doTunnelHandshake(tunnel, getServerHost(), getServerPort());
/* overlay the tunnel socket with SSL */
socket = (SSLSocket) socketFactory.createSocket(tunnel, getServerHost(), getServerPort(), true);
/* 添加握手回调函数监听*/
socket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent event) {
logger.debug(“Handshake finished!”);
logger.debug(“\t CipherSuite:” + event.getCipherSuite());
logger.debug(“\t SessionId ” + event.getSession());
logger.debug(“\t PeerHost ” + event.getSession().getPeerHost());
}
});
return socket;
}
//判断是否服务器能正常访问
private void doTunnelHandshake(Socket tunnel, String host, int port) throws IOException {
OutputStream out = tunnel.getOutputStream();
String msg = “CONNECT ” + host + “:” + port + ” HTTP/1.0\n” + “User-Agent: BoardPad Server” + “\r\n\r\n”;
byte b[] = null;
try { //We really do want ASCII7 — the http protocol doesn’t change with locale.
b = msg.getBytes(“ASCII7″);
} catch (UnsupportedEncodingException ignored) { //If ASCII7 isn’t there, something serious is wrong, but Paranoia Is Good ™
b = msg.getBytes();
}
out.write(b);
out.flush();
// We need to store the reply so we can create a detailed error message to the user.
byte reply[] = new byte[200];
int replyLen = 0;
int newlinesSeen = 0;
boolean headerDone = false; //Done on first newline
InputStream in = tunnel.getInputStream();
while (newlinesSeen < 2) {
int i = in.read();
if (i < 0) {
throw new IOException(“Unexpected EOF from proxy”);
}
if (i == ‘\n’) {
headerDone = true;
++newlinesSeen;
} else if (i != ‘\r’) {
newlinesSeen = 0;
if (!headerDone && replyLen < reply.length) {
reply[replyLen++] = (byte) i;
}
}
}
/*
* Converting the byte array to a string is slightly wasteful
* in the case where the connection was successful, but it’s
* insignificant compared to the network overhead.
*/
String replyStr;
try {
replyStr = new String(reply, 0, replyLen, “ASCII7″);
} catch (UnsupportedEncodingException ignored) {
replyStr = new String(reply, 0, replyLen);
}
/* We check for Connection Established because our proxy returns HTTP/1.1 instead of 1.0 */
if (replyStr.toLowerCase().indexOf(“200 connection established”) == -1) {
throw new IOException(“Unable to tunnel through. Proxy returns \”” + replyStr + “\””);
}
/* tunneling Handshake was successful! */
}
它的子类有两个一个是反馈服务器连接ConnectionToFeedbackServer一个是推送服务器连接ConnectionToNotificationServer。由于子类只是提供了服务器地址和端口,故不再说明