JAVA网络爬虫
HttpClient

导航

 

网络编程

  1. TCP通信–(C/S架构)

    • TCPClient

      package cn.day19.xiaoge.demo01;
      
      /*
          TCP通信的客户端: 向服务器发送链接请求, 给服务器发送数据, 读取服务器回写的数据
          表示客户端的类:
              java.net.Socket: 此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
              套接字: 包含了IP地址和端口号的网络单位
      
          构造方法:
              Socket(String host, int port) 创建一个流套接字并将其链接到指定主机上的指定端口号
              参数:
                  String host: 服务器主机名称/服务器的IP地址
                  int port: 服务器的端口号
          成员方法:
              OutputStream getOutputStream() 返回此套接字的输出流
              InputStream getInputStream() 返回此套接字的输入流
              void close() 关闭套接字
      
          实现步骤:
              1. 创建一个客户端套接字对象Socket, 构造方法绑定服务器的IP地址和端口号
              2. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
              3. 使用网络套接字输出流OutputStream对象中的方法write, 给服务器发送数据
              4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
              5. 使用网络套接字输入流InputStream对象中的方法read, 读取服务器会写的数据
              6. 释放资源(Socket)
          注意:
              1. 客户端和服务器端进行交互, 必须使用Socket中提供的网络流, 不能使用字节创建的流对象
              2. 当我们创建客户端对象Socket的时候, 就会去请求服务器和服务器经过3次握手建立连接服务器
                  这时如果服务器没有启动, 那么就会跑出异常
                  如果服务器已经启动, 那么就可以进行交互
       */
      
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.Socket;
      
      public class TCPClient {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个客户端套接字对象Socket, 构造方法绑定服务器的IP地址和端口号
              Socket socket = new Socket("127.0.0.1", 8888);
      
              // 2. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
              OutputStream gos = socket.getOutputStream();
      
              // 3. 使用网络套接字输出流OutputStream对象中的方法write, 给服务器发送数据
              gos.write("没意义".getBytes());
      
              // 4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
              InputStream gis = socket.getInputStream();
      
              // 5. 使用网络套接字输入流InputStream对象中的方法read, 读取服务器会写的数据
              byte[] bytes = new byte[1024];
              int len = gis.read(bytes);
              System.out.println(new String(bytes, 0, len));
      
              // 6. 释放资源(Socket)
              socket.close();
      
          }
      
      }
      
      
    • TCPServer

      package cn.day19.xiaoge.demo01;
      
      /*
          TCP通信的服务器端: 接收客户端的请求, 读取客户端发送的数据, 给客户端回写数据
          表示服务器的类:
              java.new.ServerSocket: 此类实现服务器套接字
          构造方法:
              ServerSocket(int port) 创建绑定到特定端口的服务器套接字
      
          服务器端必须明确一件事情, 必须的知道是那个客户端请求的数据
          所以可以使用accept方法获取到请求的客户的对象Socket
          成员方法:
              Socket accept() 侦听并接受到此套接字的链接
      
          服务器的实现步骤:
              1. 创建服务器ServerSocket对象和系统要指定的端口号
              2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端对象Socket
              3. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
              4. 使用网络字节输入流InputStream对象中的方法read, 读取客户端发送的数据
              5. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream度夏宁
              6. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写数据
              7. 释放资源(Socket, ServerSocket)
       */
      
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class TCPServer {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建服务器ServerSocket对象和系统要指定的端口号
              ServerSocket server = new ServerSocket(8888);
      
              // 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端对象Socket
              Socket socket = server.accept();
      
              // 3. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
              InputStream gis = socket.getInputStream();
      
              // 4. 使用网络字节输入流InputStream对象中的方法read, 读取客户端发送的数据
              byte[] bytes = new byte[1024];
              int len = gis.read(bytes);
              System.out.println(new String(bytes, 0, len));
      
              // 5. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream度夏宁
              OutputStream gos = socket.getOutputStream();
      
              // 6. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写数据
              gos.write("盘他".getBytes());
      
              // 7. 释放资源(Socket, ServerSocket)
              socket.close();
              server.close();
          }
      
      }
      
  2. 文件上传(本地上传到服务器)–(C/S架构)

    • TCPClient

      package cn.day19.xiaoge.demo02;
      
      /*
          文件上传案例的客户端: 读取本地文件, 上传到服务器, 读取服务器会写的数据
      
          明确:
              数据源: ./1.jpg
              目的地: 服务器
      
          实现步骤:
              1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源
              2. 创建一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号
              3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象
              4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件
              5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器
              6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象
              7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据
              8. 释放资源(FileInputStream, Socket)
       */
      
      import java.io.*;
      import java.net.Socket;
      
      public class TCPClient {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("/Users/xiaoge/IdeaProjects/day19-code/day19-code/1.jpg");
      
              // 2. 创建爱你一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号
              Socket socket = new Socket("127.0.0.1", 8888);
      
              // 3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象
              OutputStream os = socket.getOutputStream();
      
              // 4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件
              byte[] bytes = new byte[1024];
              int len = 0;
              while ((len = fis.read(bytes)) != -1) {
                  // 5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器
                  os.write(bytes, 0, len);  // 这里结束标记没有发送给服务器, 这样服务器变成堵塞状态
                  // 因为read方法没有读取到结束标记, 会进入堵塞状态
              }
      
              /*
                  解决: 服务器堵塞问题, 上传完文件, 给服务器写一个结束标记
                  void shutdownOutput() 禁用此套接字的输出流
                  对于 TCP 套接字, 任何以前写入的数据都将发送, 并且后跟 TCP 的正常连接终止序列
               */
              socket.shutdownOutput();
      
              // 6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象
              InputStream is = socket.getInputStream();
      
              // 7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据
              while ((len = is.read(bytes)) != -1) {
                  System.out.println(new String(bytes, 0, len));
              }
      
              // 8. 释放资源(FileInputStream, Socket)
              fis.close();
              socket.close();
          }
      
      }
      
      
    • TCPServer

      package cn.day19.xiaoge.demo02;
      
      /*
          文件上传案例服务器端: 读取客户端上传的文件, 保存到服务器的硬盘, 给客户端回写"上传成功"
      
          明确:
              数据源: 客户端上传的文件
              目的地: 服务器的硬盘: ./upload/1.jpg
      
          实现步骤:
              1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号
              2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象
              3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象
              4. 判断./upload文件夹是否存在, 不存在则创建
              5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地
              6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件
              7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上
              8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象
              9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功"
              10. 释放资源(FileOutputStream, Socket, ServerSocket)
       */
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class TCPServer {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号
              ServerSocket server = new ServerSocket(8888);
      
              // 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象
              Socket socket = server.accept();
      
              // 3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象
              InputStream is = socket.getInputStream();
      
              // 4. 判断./upload文件夹是否存在, 不存在则创建
              File file = new File("./upload");
              // 判断传递的路径文件夹是否存在
              if (!file.exists()){
                  // 创建你构造方法中传递的路径的文件夹
                  file.mkdirs();
              }
      
              // 5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地
              FileOutputStream fos = new FileOutputStream("./upload/1.jpg");
      
              // 6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件
              byte[] bytes = new byte[1024];
              int len = 0;
              while((len = is.read(bytes)) != -1){
                  // 这里会接受到客户端发送的, TCP正常连接的终止序列, 所以这里(read能读取到结束标记)
                  // 7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上
                  fos.write(bytes, 0, len);
              }
      
              // 8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象
              OutputStream os = socket.getOutputStream();
      
              // 9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功"
              os.write("上传成功".getBytes());
      
              // 10. 释放资源(FileOutputStream, Socket, ServerSocket)
              fos.close();
              socket.close();
              server.close();
          }
      
      }
      
  3. 文件上传到服务器(服务器使用多线程), 读取服务器会写数据–(C/S架构)

    • TCPClient

      package cn.day19.xiaoge.demo03;
      
      /*
          文件上传案例的客户端: 读取本地文件, 上传到服务器, 读取服务器会写的数据
      
          明确:
              数据源: ./1.jpg
              目的地: 服务器
      
          实现步骤:
              1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源
              2. 创建爱你一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号
              3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象
              4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件
              5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器
              6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象
              7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据
              8. 释放资源(FileInputStream, Socket)
       */
      
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.Socket;
      
      public class TCPClient {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("/Users/xiaoge/IdeaProjects/day19-code/day19-code/1.jpg");
      
              // 2. 创建爱你一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号
              Socket socket = new Socket("127.0.0.1", 8888);
      
              // 3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象
              OutputStream os = socket.getOutputStream();
      
              // 4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件
              byte[] bytes = new byte[1024];
              int len = 0;
              while ((len = fis.read(bytes)) != -1) {
                  // 5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器
                  os.write(bytes, 0, len);  // 这里结束标记没有发送给服务器, 这样服务器变成堵塞状态
                  // 因为read方法没有读取到结束标记, 会进入堵塞状态
              }
      
              /*
                  解决: 服务器堵塞问题, 上传完文件, 给服务器写一个结束标记
                  void shutdownOutput() 禁用此套接字的输出流
                  对于 TCP 套接字, 任何以前写入的数据都将发送, 并且后跟 TCP 的正常连接终止序列
               */
              socket.shutdownOutput();
      
              // 6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象
              InputStream is = socket.getInputStream();
      
              // 7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据
              while ((len = is.read(bytes)) != -1) {
                  System.out.println(new String(bytes, 0, len));
              }
      
              // 8. 释放资源(FileInputStream, Socket)
              fis.close();
              socket.close();
          }
      
      }
      
      
    • TCPServer

      package cn.day19.xiaoge.demo03;
      
      /*
          文件上传案例服务器端: 读取客户端上传的文件, 保存到服务器的硬盘, 给客户端回写"上传成功"
      
          明确:
              数据源: 客户端上传的文件
              目的地: 服务器的硬盘: ./upload/1.jpg
      
          实现步骤:
              1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号
              2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象
              3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象
              4. 判断./upload文件夹是否存在, 不存在则创建
              5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地
              6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件
              7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上
              8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象
              9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功"
              10. 释放资源(FileOutputStream, Socket, ServerSocket)
       */
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.Random;
      
      public class TCPServer {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号
              ServerSocket server = new ServerSocket(8888);
      
              /*
                  让服务器一直处于监听状态(死循环accept方法)
                  有一个客户端上传文件, 就保存一个文件
               */
              while(true) {
                  // 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象
                  Socket socket = server.accept();
      
                  /*
                      使用多线程技术提高程序效率
                      有一个客户端上传文件, 就开启一个线程, 完成文件的上传
                   */
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          try{
                              // 3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象
                              InputStream is = socket.getInputStream();
      
                              // 4. 判断./upload文件夹是否存在, 不存在则创建
                              File file = new File("./upload");
                              // 判断传递的路径文件夹/文件是否存在
                              if (!file.exists()){
                                  // 递归创建文件夹
                                  file.mkdirs();
                              }
                              // 打印构造方法中传递的路径
                              // System.out.println(file);
      
                              /*
                                  自定义一个文件的命名规则: 防止同名的文件被覆盖
                                  规则: 域名 + 毫秒值 + 随机数
                               */
                              String fileName = "xiaoge" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg";
      
                              // 5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地
                              FileOutputStream fos = new FileOutputStream(file + "/" + fileName);
      
                              // 6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件
                              byte[] bytes = new byte[1024];
                              int len = 0;
                              while((len = is.read(bytes)) != -1){
                                  // 这里会接受到客户端发送的, TCP正常连接的终止序列, 所以这里(read能读取到结束标记)
                                  // 7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上
                                  fos.write(bytes, 0, len);
                              }
      
                              // 8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象
                              OutputStream os = socket.getOutputStream();
      
                              // 9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功"
                              os.write("上传成功".getBytes());
      
                              // 10. 释放资源(FileOutputStream, Socket, ServerSocket)
                              fos.close();
                              socket.close();
                          }catch(IOException e) {
                              System.out.println(e);
                          }
                      }
                  }).start();
      
      
              }
      
              // 服务器不用关闭
              // server.close();
          }
      
      }
      
      
  4. 浏览器发送数据, 服务器接收并处理(B/S架构)

    • TCPServer

      package cn.day19.xiaoge.demo04;
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /*
          创建B/S版本TCP服务器
       */
      
      public class TCPServer {
      
          public static void main(String[] args) throws IOException {
              // 创建一个服务器ServerSocket, 和系统要指定的端口号
              ServerSocket server = new ServerSocket(8888);
      
              // 使用accept方法获取到请求的客户端对象(浏览器)
              Socket socket = server.accept();
      
              // 使用Socket对象中的方法getInputStream, 获取到网络字节输入InputStream对象
              InputStream is = socket.getInputStream();
      
              // 把is网络字节输入流对象, 转换为字符缓冲输入流
              BufferedReader br = new BufferedReader(new InputStreamReader(is));
      
              // 把客户端请求信息的第一行读取出来 GET /day19-code/web/index.html HTTP/1.1
              String line = br.readLine();
      
              // 把读取的信息进行切割, 只要中间部分 /day19-code/web/index.html
              String[] arr = line.split(" ");
      
              // 把路径前边的/去掉, 进行截取 day19-code/web/index.html
              String htmlPath = arr[1].substring(1);
      
              // 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
              OutputStream os = socket.getOutputStream();
      
              // 创建一个本地字节输入流, 构造方法中绑定要读取的html路径
              FileInputStream fis = new FileInputStream(htmlPath);
      
              // 写入HTTP协议响应头, 固定写法
              os.write("HTTP/1.1 200 OK\n".getBytes());
              os.write("Content-Type:text/html\n".getBytes());
              // 必须要写入空行, 否则浏览器不解析
              os.write("\n".getBytes());
      
              // 一读一写赋值文件, 把服务器读取的html文件会写到客户端(浏览器)
              byte[] bytes = new byte[1024];
              int len = 0;
              while((len = fis.read(bytes)) != -1){
                  os.write(bytes, 0, len);
              }
      
          }
      
      }
      
    • TCPServerThread–(多线程版本)

      package cn.day19.xiaoge.demo04;
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /*
          创建B/S版本TCP服务器
       */
      
      public class TCPServerThread {
      
          public static void main(String[] args) throws IOException {
              // 创建一个服务器ServerSocket, 和系统要指定的端口号
              ServerSocket server = new ServerSocket(8888);
      
              /*
                  浏览器解析服务器会写的html页面, 页面中如果有图片, 那么浏览器就会单独的开启一个线程, 读取服务器文件
                  我们就让服务器一直处于监听状态, 客户端请求一次, 那么服务器就会写一次
               */
              while(true) {
      
                  // 使用accept方法获取到请求的客户端对象(浏览器)
                  /*
                      accept是阻塞的, 不能放在线程里面,
                      这样线程创建不成功, 因为start()方法执行不到
                   */
                  Socket socket = server.accept();
      
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          try{
      
                              // 使用Socket对象中的方法getInputStream, 获取到网络字节输入InputStream对象
                              InputStream is = socket.getInputStream();
      
                              // 把is网络字节输入流对象, 转换为字符缓冲输入流
                              BufferedReader br = new BufferedReader(new InputStreamReader(is));
      
                              // 把客户端请求信息的第一行读取出来 GET /day19-code/web/index.html HTTP/1.1
                              String line = br.readLine();
      
                              // 把读取的信息进行切割, 只要中间部分 /day19-code/web/index.html
                              String[] arr = line.split(" ");
      
                              // 把路径前边的/去掉, 进行截取 day19-code/web/index.html
                              String htmlPath = arr[1].substring(1);
      
                              // 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
                              OutputStream os = socket.getOutputStream();
      
                              // 创建一个本地字节输入流, 构造方法中绑定要读取的html路径
                              FileInputStream fis = new FileInputStream(htmlPath);
      
                              // 写入HTTP协议响应头, 固定写法
                              os.write("HTTP/1.1 200 OK\n".getBytes());
                              os.write("Content-Type:text/html\n".getBytes());
                              // 必须要写入空行, 否则浏览器不解析
                              os.write("\n".getBytes());
      
                              // 一读一写赋值文件, 把服务器读取的html文件会写到客户端(浏览器)
                              byte[] bytes = new byte[1024];
                              int len = 0;
                              while((len = fis.read(bytes)) != -1){
                                  os.write(bytes, 0, len);
                              }
      
                              // 释放资源
                              fis.close();
                              socket.close();
                          }catch(IOException e){
                              System.out.println(e);
                          }
                      }
                  }).start();
              }
      
      
          }
      
      }
      
posted on 2019-12-01 21:29  gmlgxx  阅读(35)  评论(0)    收藏  举报