import java.io.*;
import javax.net.ssl.*;
import java.net.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* @author lwh
* @date 2022/1/12
*/
public class Hs {
public static String sentRequest(String path) {
try{
//信任策略,我自己实现的策略,无需验证服务器的ssl证书
TrustManager[] tm = {new NonValidationVerX509TrustManager()};
//getInstance的第一个参数,具体查看https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
SSLContext sslContext = SSLContext.getInstance("SSL");
//第一个参数:密码仓库,我不需要验证证书,所以不需要,设置为空
//第二个参数:信任策略,传我自己的
//第三个参数:随机数
sslContext.init(null, tm, new SecureRandom());
//创建socket工厂
SSLSocketFactory sc = sslContext.getSocketFactory();
//创建访问的url
Socket in=sc.createSocket(path,443);
InputStream is=in.getInputStream();
OutputStream os=in.getOutputStream();
os.write("GET / HTTP/1.1\r\n".getBytes());
os.write(("Host:"+path+"\r\n").getBytes());
os.write("Connection:close\r\n\r\n".getBytes());
os.flush();
String s="";
int k;
k=is.read();
while(k!=-1) {
s=s+(char)k;
k=is.read();
}
return s;
} catch (MalformedURLException | NoSuchAlgorithmException | KeyManagementException e) {
throw new RuntimeException("HttpsURLConnection 的SSL 配置异常"+e);
} catch (IOException ioEx) {
throw new RuntimeException("请求失败,原因:"+ ioEx);
}
}
public static void main(String[] args)throws Exception{
final String s1 = Hs.sentRequest("cn.bing.com");
System.out.println(s1);
File fl=new File("/home/wzpad/java/1.html");
FileOutputStream os1=new FileOutputStream(fl);
os1.write(s1.getBytes());
}
//请求类型 post/get
public static enum HttpMethod{
GET,POST;
}
public static class NonValidationVerX509TrustManager implements X509TrustManager {
/**
* 这里是服务器验证客户端证书的,这里不需要验证
* @param chain 证书链,具体查看{@link X509TrustManager}
* @param authType 基于客户端证书的认证类型 比如RSA,具体查看{@link X509TrustManager}
* @throws CertificateException
*/
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
/**
* 这里是客户端验证服务器证书的,这里不需要验证
* @param chain 证书链,具体查看{@link X509TrustManager}
* @param authType 使用的密钥交换算法,具体查看{@link X509TrustManager}
* @throws CertificateException 证书验证失败,会抛出此异常
*/
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
System.out.println("不需要验证");
}
/**
* 获取受信任的证书列表
* @return 受信任的证书列表
*/
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}
我初学java 现在理解java.net.*包中,最重要的是几个类URL. Socket. ServerSocket搞不好这几个类是c写的,没有见过源码. 剩下的类诂计可以用这几个类自己推出来。再学一下,看能不能用ssl协议写出客户端和服务器验证的过程,不用封装好的ssl类验证。
这种方法取得的网页文件是不能用于代理的,因为它通不过浏览器的验证。但这种文件下载到本地后再用浏览器是可以打开的,因为读本地的网页文件,浏览器不读首部就不进行验证了。
读http 和https网页的区别是后者多了ssl认证。认证后的所有操作都一样,客户端发首部请求后,服务器发响应首部,中间用\r\n隔开,发正文数据。我们可以根据服务器返回的首部,正文就可以写出属于自己的URLConnection 各种类和类方法。我们可以用两对\r\n来取出首部,用读文件完成的标志-1来取出正文。首部中又有各种属性,如length. data. accept.根据这些首部数据又可以写出类的各种方法。
学网络编程过程中,我感觉有一个地方特别有用,那就是在交互中怎样判断对方客户端,或者服务器已传输完文件或者命令。又怎样在不知道长度或者不知道完成标志的情况下写循环语句。所以,如果我以后写协议,一定会在完成语句加一条over, 还有,数组在这种不知道长度的情况下怎样使用。我现在用字符串,因为它可以变长度。好象java中有一个类stringbuilder,我理解就是一个字符串。
补充强调一下,一个socket就 可以输入输出编程,双向的,所以一个socket内的输入输出不能拆开分在两个socket中编程。所以也不能分在两个线程中了。这样理解,一个socket就是一条独路,一个通道,如果分开就是两条路了。那样输入输出就没有关联了。还有编客户端socket 必须是先向服务器请求,也就是先输出,再等待服务器的响应,再输入编程。不能反了。同理,服务器的socket必是先输入后相应客户端请求,再输出数据。这是客户端服务器输入输出的编程顺序。