一. 链路不复用的情况
public String get(String requestUrl) {
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
String result = null;
try {
URL url = new URL(requestUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(15000);
connection.setReadTimeout(60000);
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.connect();
Map<String, List<String>> map = connection.getHeaderFields();
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
StringBuffer sbf = new StringBuffer();
String line = null;
while ((line = br.readLine()) != null) {
sbf.append(line);
sbf.append("\r\n");
}
result = sbf.toString();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != br) {
br.close();
}
if (null != is) {
is.close();
}
} catch (Exception e) {
e.printStackTrace();
}
connection.disconnect();
}
return result;
}
public void getMore(View view) {
new Thread() {
@Override
public void run() {
final long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
String data= get("http://www.kuaidi100.com/query?type=yuantong&postid=222222222");
Log.i("liuyi", "data: "+data);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_time.setText("耗费时间: " +( System.currentTimeMillis() - start));
}
});
}
}.start();
}
执行结果:

image.png
二. 链路复用的情况
public void get(View view) {
new Thread() {
@Override
public void run() {
final long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
HttpUrl httpUrl = new HttpUrl("http://www.kuaidi100.com/query?type=yuantong&postid=222222222");
Request request = new Request();
request.setHttpUrl(httpUrl);
String data = netUtil.call(request);
Log.i("liuyi", "data: "+data);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_time.setText("耗费时间: " +( System.currentTimeMillis() - start));
}
});
}
}.start();
}
执行结果:

image.png
三. NetUtil
public class NetUtil {
private ConnectionPool connectionPool;
public NetUtil() {
connectionPool = new ConnectionPool();
}
public String call(Request request) {
HttpConnection httpConnection= connectionPool.getHttpConnection(request.getHttpUrl().getHost(),
request.getHttpUrl().getPort());
httpConnection.setHttpUrl(request.getHttpUrl());
String body = httpConnection.call(request);
//服务器 复用
if (httpConnection.isKeepAlive()) {
connectionPool.putHttpConnection(httpConnection);
} else {
httpConnection.close();
}
return body;
}
}
四. ConnectionPool
public class ConnectionPool {
private Deque<HttpConnection> httpConnections = new ArrayDeque<>();
public void putHttpConnection(HttpConnection httpConnection){
httpConnections.add(httpConnection);
}
public synchronized HttpConnection getHttpConnection(String host, int port) {
Iterator<HttpConnection> httpConnectionIterator = httpConnections.iterator();
HttpConnection httpConnection = null;
while(httpConnectionIterator.hasNext()){
httpConnection = httpConnectionIterator.next();
if (httpConnection.isSameAddress(host, port)) {
httpConnectionIterator.remove();
break;
}
}
if (null == httpConnection) {
httpConnection = new HttpConnection();
} else {
Log.d("liuyi", "从连接池中获得连接");
}
return httpConnection;
}
}
五. HttpConnection
public class HttpConnection {
private InputStream inputStream;
private OutputStream outputStream;
Socket socket;
HttpUrl httpUrl;
HttpCodec httpCodec = new HttpCodec();
public void setHttpUrl(HttpUrl httpUrl) {
this.httpUrl = httpUrl;
}
public boolean isSameAddress(String host, int port){
if(null == socket){
return false;
}
return TextUtils.equals(httpUrl.getHost(),host) && httpUrl.port == port;
}
private void createSocket() throws IOException {
if (null == socket || socket.isClosed()) {
if (httpUrl.protocol.equalsIgnoreCase("https")) {
socket = SSLSocketFactory.getDefault().createSocket();
} else {
socket = new Socket();
}
socket.connect(new InetSocketAddress(httpUrl.getHost(), httpUrl.getPort()));
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
}
}
public void close(){
if(null != socket){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String call(Request request) {
try {
createSocket();
} catch (IOException e) {
e.printStackTrace();
}
httpCodec.writeRequest(outputStream, request);
return parseBody();
}
String statusLine;
Map<String, String> headers;
public boolean isKeepAlive( ){
String[] status = statusLine.split(" ");
boolean isKeepAlive = false;
if(headers.containsKey("Connection")){
isKeepAlive = headers.get("Connection").equalsIgnoreCase("Keep-Alive");
}
return isKeepAlive;
}
public String parseBody( ) {
String body = null;
try {
statusLine =httpCodec.readLine(inputStream);
//获取服务器返回的响应头
headers = httpCodec.readHeaders(inputStream);
int contentLength = -1;
if(headers.containsKey("Content-Length")){
contentLength = Integer.valueOf(headers.get("Content-Length"));
}
if(contentLength > 0){
byte[] bodyBytes = httpCodec.readBytes(inputStream,contentLength);
body = new String(bodyBytes,"UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
}
return body ;
}
}
六. HttpUrl
public class HttpUrl {
public String file;
public String protocol;
public int port;
public String host;
public HttpUrl(String url) {
try {
URL localUrl = new URL(url);//url格式化
host = localUrl.getHost();
protocol = localUrl.getProtocol();
port = localUrl.getPort();
file = localUrl.getFile();
if (port == -1) {
//代表url中没有端口信息,就是使用默认端口,http:80,https:443
port = localUrl.getDefaultPort();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public String getFile() {
return file;
}
public void setFile(String file) {
this.file = file;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
}
七. HttpCodec
public class HttpCodec {
static final String SPACE = " ";//一个空格
static final String HTTP_VERSION = "HTTP/1.1";//http的版本信息
static final String COLON = ":";//冒号
static final String CRLF = "\r\n";
//http响应中 13 http结束
static final int CR = 13;
static final int LF = 10;//换行的ASCII码
private ByteBuffer byteBuffer;
public HttpCodec(){
byteBuffer = ByteBuffer.allocate(10 * 1024);
}
public void writeRequest(OutputStream os, Request request) {
StringBuffer sb = new StringBuffer();
sb.append(request.getMethod());
sb.append(SPACE);
sb.append(request.getHttpUrl().file);
sb.append(SPACE);//一个空格
sb.append(HTTP_VERSION);// HTTP/1.1
sb.append("\r\n");//一个回车换行
Map<String, String> headers = request.getHeaders();
for(Map.Entry<String, String> entry : headers.entrySet()){
sb.append(entry.getKey());
sb.append(COLON);//一个冒号
sb.append(SPACE);//一个空格
sb.append(entry.getValue());
sb.append(CRLF);//最后面跟一个回车和换行
}
sb.append(CRLF);//最后面跟一个回车和换行
try {
os.write(sb.toString().getBytes());
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean isEmptyLine(String line){
return TextUtils.equals(line,CRLF);
}
public byte[] readBytes(InputStream is, int length) throws IOException {
byte[] bytes = new byte[length];
int readNum = 0;
while(true){
readNum = is.read(bytes,readNum,length - readNum);
if(readNum == length){
return bytes;
}
}
}
public Map<String, String> readHeaders(InputStream is) throws IOException {
HashMap<String, String> headers = new HashMap<>();
while(true) {
String line = null;
try {
line = readLine(is);
} catch (Exception e) {
e.printStackTrace();
}
if (isEmptyLine(line)) {
//如果读到空行 \r\n 响应头读完了
break;
}
int index = line.indexOf(":");//因为服务器返回的响应头中的格式也是key: value的格式
if (index > 0) {
String key = line.substring(0, index);
//这里加2 是因为,value前面还有冒号和空格,所以,value的第一个位置,需要往后移
//减2是因为line后面有/r/n两个字节
String value = line.substring(index + 2, line.length() - 2);
headers.put(key, value);
}
}
return headers;
}
public String readLine(InputStream is) throws Exception {
byteBuffer.clear();
byteBuffer.mark();
byte b;
boolean isMaybeEofLine = false;
while((b = (byte)is.read()) != -1){
byteBuffer.put(b);
if(b == CR) {//如果读到一个 /r
isMaybeEofLine = true;
}
if (isMaybeEofLine) {
if(b == LF) {//如果读到一个 /n了,意味着,行结束了
byte[] lineBytes = new byte[byteBuffer.position()];//new一个一行数据大小的字节数据
byteBuffer.reset();
byteBuffer.get(lineBytes);
byteBuffer.clear();//清空
byteBuffer.mark();
return new String(lineBytes, "UTF-8");
}
}
}
return "";
}
}
八. Request
public class Request {
public static final String HEAD_HOST = "Host";
public static final String HEAD_CONNECTION = "Connection";
public static final String HEAD_VALUE_KEEP_ALIVE = "Keep-Alive";
private String method = "GET";
private HttpUrl httpUrl = null;
public HttpUrl getHttpUrl() {
return httpUrl;
}
public void setHttpUrl(HttpUrl httpUrl) {
this.httpUrl = httpUrl;
Map<String, String> tempHeaders = getHeaders();
if (!tempHeaders.containsKey(HEAD_HOST)) {
tempHeaders.put(HEAD_HOST,getHttpUrl().getHost());
}
if(!tempHeaders.containsKey(HEAD_CONNECTION)) {
tempHeaders.put(HEAD_CONNECTION, HEAD_VALUE_KEEP_ALIVE);
}
}
Map<String, String> headers = new HashMap<>();
public String getMethod() {
return method;
}
public Map<String, String> getHeaders() {
return headers;
}
}
八. 代码地址
https://gitee.com/luisliuyi/android-optimize-network01.git
网友评论