我的位置: 主页 > 技术交流区 >

大牛老师教你:java socket模拟http客户端

时间:2018-12-29 17:01来源:未知 作者:北大青鸟科海技术 点击:
socket是网络之间节点通信的关键技术,关键是三部分 IP地址、 协议、 端口号 TCP/IP协议: 是目前世界上应用最为广泛的协议,是以TCP和IP为基础的不同层次上多个协议的集合,也成TCP
 socket是网络之间节点通信的关键技术,关键是三部分
   IP地址、协议、端口号
TCP/IP协议:
   是目前世界上应用最为广泛的协议,是以TCP和IP为基础的不同层次上多个协议的集合,也成TCP/IP协议族、或TCP/IP协议栈
            TCP:Transmission Control Protocol 传输控制协议
            IP:Internet Protocol 互联网协议
       3、TCP/IP五层模型
            应用层:HTTP、FTP、SMTP、Telnet等
            传输层:TCP/IP
            网络层:
            数据链路层:
            物理层:网线、双绞线、网卡等
下面的例子是模拟http请求,也就是桌面浏览器的基本功能,可以作为爬虫功能的基础


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

/**
 * 一个简单的HTTP客户端,发送HTTP请求,模拟浏览器 可打印服务器发送过来的HTTP消息
 
 */
public class HttpClient {
    private static String encoding = "UTF-8";

    public static void main(String[] args) {
        try {
        //访问本机,如果访问其他服务器,要改成其ip或者域名,其端口大部分是http协议的默认端口80。留意两点,一是很多服务器的协议是https(加密的http协议),访问方式不同;二是很多服务器有禁止爬虫功能,会返回错误信息。    
            Socket s = new Socket("localhost", 8080);
            OutputStreamWriter osw = new OutputStreamWriter(s.getOutputStream());
            StringBuffer sb = new StringBuffer();
            sb.append("GET /slsint_gd/reportview/reportViewList.jsp HTTP/1.1 ");
            sb.append("Host: localhost:8088 ");
            sb.append("Connection: Keep-Alive ");
            // 注,这是关键的关键,这里一定要一个回车换行,表示消息头完,不然服务器会等待
            sb.append(" ");
            osw.write(sb.toString());
            osw.flush();

            // --输出服务器传回的消息的头信息
            InputStream is = s.getInputStream();
            String line = null;
            int contentLength = 0;// 服务器发送回来的消息长度
            // 读取所有服务器发送过来的请求参数头部信息
            do {
                line = readLine(is, 0);
                // 如果有Content-Length消息头时取出
                if (line.startsWith("Content-Length")) {
                    contentLength = Integer.parseInt(line.split(":")[1].trim());
                }
                // 打印请求部信息
                System.out.print(line);
                // 如果遇到了一个单独的回车换行,则表示请求头结束
            } while (!line.equals(" "));

            // --输消息的体
            System.out.print(readLine(is, contentLength));

            // 关闭流
            is.close();

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
     * 这里我们自己模拟读取一行,因为如果使用API中的BufferedReader时,它是读取到一个回车换行后
     * 才返回,否则如果没有读取,则一直阻塞,直接服务器超时自动关闭为止,如果此时还使用BufferedReader
     * 来读时,因为读到最后一行时,最后一行后不会有回车换行符,所以就会等待。如果使用服务器发送回来的
     * 消息头里的Content-Length来截取消息体,这样就不会阻塞
     *
     * contentLe 参数 如果为0时,表示读头,读时我们还是一行一行的返回;如果不为0,表示读消息体,
     * 时我们根据消息体的长度来读完消息体后,客户端自动关闭流,这样不用先到服务器超时来关闭。
     */
    private static String readLine(InputStream is, int contentLe) throws IOException {
        ArrayList lineByteList = new ArrayList();
        byte readByte;
        int total = 0;
        if (contentLe != 0) {
            do {
                readByte = (byte) is.read();
                lineByteList.add(Byte.valueOf(readByte));
                total++;
            } while (total < contentLe);// 消息体读还未读完
        } else {
            do {
                readByte = (byte) is.read();
                lineByteList.add(Byte.valueOf(readByte));
            } while (readByte != 10);
        }

        byte[] tmpByteArr = new byte[lineByteList.size()];
        for (int i = 0; i < lineByteList.size(); i++) {
            tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue();
        }
        lineByteList.clear();

        return new String(tmpByteArr, encoding);
    }
}

下图是一个公网网站的结果,返回了错误信息,禁止爬虫的结果

 

----------------------------分隔线----------------------------