使用TCP实现HTTP客户端

原创
2022/10/26 02:47
阅读数 108

主类:


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLSocketFactory;


public class HttpProcessor {

	static String encode = "UTF-8";

	public static final String GET = "GET";
	public static final String HEAD = "HEAD";
	public static final String POST = "POST";
	public static final String PUT = "PUT";
	public static final String DELETE = "DELETE";
	public static final String OPTIONS = "OPTIONS";

	public static final Integer EXPIRE_MILLISECOND = 15000;

	public static HttpEntity get(String url) throws IOException {
		return http(url, HttpProcessor.GET, null, null, HttpProcessor.EXPIRE_MILLISECOND);
	}

	public static HttpEntity get(String url, Integer expireMillisecond) throws IOException {
		return http(url, HttpProcessor.GET, null, null, expireMillisecond);
	}

	public static HttpEntity get(String url, Map<String, String> header, Integer expireMillisecond) throws IOException {
		return http(url, HttpProcessor.GET, null, header, expireMillisecond);
	}

	public static HttpEntity post(String url, byte[] data) throws IOException {
		return http(url, HttpProcessor.POST, data, null, HttpProcessor.EXPIRE_MILLISECOND);
	}

	public static HttpEntity post(String url, byte[] data, Integer expireMillisecond) throws IOException {
		return http(url, HttpProcessor.POST, data, null, expireMillisecond);
	}

	public static HttpEntity post(String url, byte[] data, Map<String, String> header, Integer expireMillisecond)
			throws IOException {
		return http(url, HttpProcessor.POST, data, header, expireMillisecond);
	}

	public static HttpEntity http(String url, String method, byte[] data, Map<String, String> header,
			Integer expireMillisecond) throws IOException {

		if (header == null) {
			header = new HashMap<String, String>();
		}
		if (!header.containsKey("Accept")) {
			header.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
		}
		if (!header.containsKey("User-Agent")) {
			header.put("User-Agent",
					"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
		}
		if (!header.containsKey("Connection")) {
			header.put("Connection", "close");
		}
		if (data != null) {
			header.put("Content-Length", String.valueOf(data.length));
		}

		StringBuilder headerBuilder = new StringBuilder();

		URL address = new URL(url);

		String host = address.getHost();
		Integer port = (address.getPort() == -1) ? (url.toLowerCase().startsWith("https") ? 443 : 80)
				: address.getPort();

		headerBuilder.append(method).append(" ").append(address.getPath()).append(" HTTP/1.1").append("\r\n");
		headerBuilder.append("Host: ").append(host).append("\r\n");
		for (String key : header.keySet()) {
			headerBuilder.append(key).append(": ").append(header.get(key)).append("\r\n");
		}

		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		stream.write(headerBuilder.toString().getBytes(encode));
		stream.write("\r\n".getBytes(encode));
		if (data != null) {
			stream.write(data);
		}

		data = stream.toByteArray();
		stream.close();
		return socket(host, port, data, expireMillisecond);
	}

	public static HttpEntity socket(String host, Integer port, byte[] data, Integer expireMillisecond)
			throws UnknownHostException, IOException {

		Socket socket = (port != 443) ? new Socket(host, port) : SSLSocketFactory.getDefault().createSocket(host, port);

		socket.setSoTimeout(expireMillisecond);

		socket.getOutputStream().write(data);
		socket.getOutputStream().flush();

		HttpEntity entity = new HttpEntity();
		// 解析响应码
		String line = ByteUtils.readLineString(socket.getInputStream(), encode);
		if (line == null) {
			throw new RuntimeException("请求无响应");
		}
		Integer code = Integer.valueOf(StringUtil.stringCutCenter(line, " ", " "));
		entity.setCode(code);
		// 解析head
		line = ByteUtils.readLineString(socket.getInputStream(), encode);
		while (!CommonUtil.isNullOrEmpty(line.trim())) {
			try {
				String[] attrs = line.split(":");
				if (attrs.length != 2) {
					continue;
				}
				String key = attrs[0].trim();
				String value = attrs[1].trim();
				if (entity.getHeader().containsKey(key)) {
					value = entity.getHeader().get(key) + ";" + value;
				}
				entity.getHeader().put(key, value);
			} finally {
				line = ByteUtils.readLineString(socket.getInputStream(), encode);
			}
		}
		// 判断是否压缩
		String value = entity.getHeader().get("Content-Encoding");
		entity.setGzip((value != null && value.contains("gzip")) ? true : false);
		// 判断是否分段加载
		value = entity.getHeader().get("Transfer-Encoding");
		entity.setChunked((value != null && value.contains("chunked")) ? true : false);
		// 解析内容
		if (!entity.isChunked()) {
			Integer length = Integer.valueOf(entity.getHeader().get("Content-Length"));
			byte[] body = ByteUtils.read(socket.getInputStream(), length);
			entity.setBody(body);
			return entity;
		}
		// 分段解析内容
		String chunk = ByteUtils.readLineString(socket.getInputStream(), encode);
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		while (chunk != null && !CommonUtil.isNullOrEmpty(chunk.trim()) && !"0".equals(chunk.trim())) {
			try {
				Integer length = Integer.valueOf(chunk.trim(), 16);
				while (length > 0) {
					byte[] chunked = new byte[length];
					int readed = socket.getInputStream().read(chunked);
					length = length - readed;
					stream.write(chunked, 0, readed);
				}
			} finally {
				chunk = ByteUtils.readLineString(socket.getInputStream(), encode);
				chunk = ByteUtils.readLineString(socket.getInputStream(), encode);
			}
		}
		entity.setBody(stream.toByteArray());
		stream.close();
		return entity;
	}
}

模型类:


import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;


@SuppressWarnings("serial")
public class HttpEntity implements Serializable {

	private Integer code;

	private byte[] body;

	private boolean isGzip;

	private boolean chunked;

	private Map<String, String> header = new HashMap<String, String>();

	public boolean isChunked() {
		return chunked;
	}

	public void setChunked(boolean chunked) {
		this.chunked = chunked;
	}

	public boolean isGzip() {
		return isGzip;
	}

	public void setGzip(boolean isGzip) {
		this.isGzip = isGzip;
	}

	public Integer getCode() {
		return code;
	}

	public void setCode(Integer code) {
		this.code = code;
	}

	public byte[] getBody() {
		if (isGzip) {
			return GZIPUtils.uncompress(this.body);
		}
		return body;
	}

	public String getBody(String encode) throws UnsupportedEncodingException {
		return new String(this.getBody(), encode);
	}

	public void setBody(byte[] body) {
		this.body = body;
	}

	public Map<String, String> getHeader() {
		return header;
	}

	public void setHeader(Map<String, String> header) {
		this.header = header;
	}

}

工具类ByteUtils:

package org.coody.framework.core.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

@SuppressWarnings("unchecked")
public class ByteUtils {

	public static byte[] readLine(InputStream inputStream) throws IOException {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		while (true) {
			byte a = (byte) inputStream.read();
			if (a == '\n' || a == 0) {
				break;
			}
			outputStream.write(a);
		}

		return outputStream.toByteArray();
	}

	public static String readLineString(InputStream inputStream, String encode) {
		try {
			byte[] data = readLine(inputStream);
			if (CommonUtil.isNullOrEmpty(data)) {
				return null;
			}
			return new String(data, encode);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static byte[] read(InputStream inputStream) {
		ByteArrayOutputStream outputStream = null;
		try {
			outputStream = buildToOutputStream(inputStream);
			return outputStream.toByteArray();
		} catch (Exception e) {

			return null;
		} finally {
			try {
				outputStream.close();
			} catch (IOException e) {

			}
		}
	}

	public static byte[] read(InputStream inputStream, Integer length) {
		if (length < 1) {
			return null;
		}
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try {
			while (length > 0) {
				byte[] chunked = new byte[length];
				int readed = inputStream.read(chunked);
				length = length - readed;
				outputStream.write(chunked, 0, readed);
			}
			byte[] bytes = outputStream.toByteArray();
			outputStream.close();
			return bytes;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public static byte[] read(SocketChannel channel, Integer length) {
		if (length < 1) {
			return null;
		}
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try {
			while (length > 0) {
				ByteBuffer buffer = ByteBuffer.allocate(length);
				int readed = channel.read(buffer);
				length = length - readed;
				byte[] data = new byte[buffer.remaining()];
				buffer.get(data);
				outputStream.write(data);
			}
			byte[] bytes = outputStream.toByteArray();
			outputStream.close();
			return bytes;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public static <T extends OutputStream> T buildToOutputStream(InputStream inputStream) {
		OutputStream outputStream = new ByteArrayOutputStream();
		try {
			buildToOutputStream(inputStream, outputStream);
			return (T) outputStream;
		} catch (Exception e) {

			return null;
		}
	}

	public static <T extends OutputStream> T buildToOutputStream(InputStream inputStream, OutputStream outputStream) {
		if (outputStream == null) {
			outputStream = new ByteArrayOutputStream();
		}
		try {
			outputStream = new ByteArrayOutputStream();
			byte[] buff = new byte[1024];
			int rc = 0;
			while ((rc = inputStream.read(buff, 0, 1024)) > 0) {
				outputStream.write(buff, 0, rc);
			}
			return (T) outputStream;
		} catch (Exception e) {

			return null;
		}
	}
}

工具类CommonUtil:


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;


public class CommonUtil {


	public static boolean isNullOrEmpty(Object obj) {
		try {
			if (obj == null) {
				return true;
			}
			if (obj instanceof CharSequence) {
				return ((CharSequence) obj).length() == 0;
			}
			if (obj instanceof Collection) {
				return ((Collection<?>) obj).isEmpty();
			}
			if (obj instanceof Map) {
				return ((Map<?, ?>) obj).isEmpty();
			}
			if (obj instanceof Object[]) {
				Object[] object = (Object[]) obj;
				if (object.length == 0) {
					return true;
				}
				boolean empty = true;
				for (int i = 0; i < object.length; i++) {
					if (!isNullOrEmpty(object[i])) {
						empty = false;
						break;
					}
				}
				return empty;
			}
			return false;
		} catch (Exception e) {
			return true;
		}

	}


}

工具类StringUtil:


import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringUtil {

	
	public static String stringCutCenter(String allTxt, String firstTxt, String lastTxt) {
		try {
			String tmp = "";
			int n1 = allTxt.indexOf(firstTxt);
			if (n1 == -1) {
				return "";
			}
			tmp = allTxt.substring(n1 + firstTxt.length(), allTxt.length());
			int n2 = tmp.indexOf(lastTxt);
			if (n2 == -1) {
				return "";
			}
			tmp = tmp.substring(0, n2);
			return tmp;
		} catch (Exception e) {
			return "";
		}
	}

	public static List<String> stringCutCenters(String allTxt, String firstTxt, String lastTxt) {
		try {
			List<String> results = new ArrayList<String>();
			while (allTxt.contains(firstTxt)) {
				int n = allTxt.indexOf(firstTxt);
				allTxt = allTxt.substring(n + firstTxt.length(), allTxt.length());
				n = allTxt.indexOf(lastTxt);
				if (n == -1) {
					return results;
				}
				String result = allTxt.substring(0, n);
				results.add(result);
				allTxt = allTxt.substring(n + firstTxt.length(), allTxt.length());
			}
			return results;
		} catch (Exception e) {
			return null;
		}
	}

}

工具类GZIPUtils:

package org.coody.framework.core.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 
 * @author Coody
 * 
 */
public class GZIPUtils {

	public static final String GZIP_ENCODE_UTF_8 = "UTF-8";

	/**
	 * 字符串压缩为GZIP字节数组
	 * 
	 * @param str
	 * @return
	 */
	public static byte[] compress(String str) {
		try {
			return compress(str.getBytes(GZIP_ENCODE_UTF_8));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 字符串压缩为GZIP字节数组
	 * 
	 * @param str
	 * @param encoding
	 * @return
	 */
	public static byte[] compress(byte[] data) {
		if (data == null || data.length == 0) {
			return null;
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		GZIPOutputStream gzip = null;
		try {
			gzip = new GZIPOutputStream(out);
			gzip.write(data);
		} catch (IOException e) {
		} finally {
			try {
				gzip.close();
			} catch (Exception e2) {
			}
			try {
				out.close();
			} catch (Exception e2) {
			}
		}
		return out.toByteArray();
	}

	/**
	 * GZIP解压缩
	 * 
	 * @param bytes
	 * @return
	 */
	public static byte[] uncompress(byte[] bytes) {
		if (bytes == null || bytes.length == 0) {
			return null;
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		GZIPInputStream ungzip = null;
		try {
			ungzip = new GZIPInputStream(in);
			byte[] buffer = new byte[256];
			int n;
			while ((n = ungzip.read(buffer)) >= 0) {
				out.write(buffer, 0, n);
			}

		} catch (IOException e) {
		} finally {
			try {
				in.close();
			} catch (Exception e2) {
			}
			try {
				out.close();
			} catch (Exception e2) {
			}
			try {
				ungzip.close();
			} catch (Exception e2) {
				// TODO: handle exception
			}
		}

		return out.toByteArray();
	}
}

 

 

用法:工具类都非常简单,几个常用的方法都可以抽离。无其他依赖。

        HttpEntity entity = HttpProcessor.get("https://www.oschina.net/");
		System.out.println("响应码->" + entity.getCode());
		System.out.println("响应内容->\r\n" + entity.getBody("UTF-8"));

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部