Java进阶第九天

1、网络编程

1.1 软件结构和常用协议
软件结构:
  C/S:通过客户端访问服务器
  B/S:通过浏览器访问服务器
  无论哪种结构,都离不开网络
网络编程:在一定协议下,实现两台计算机的通信的程序。
网络通信协议:位于同一网络的计算机进行连接和通信时需要遵守一定的协议,对数据的传输格式、速率、步骤等进行了统一规定,双方遵守该协议才可以实现数据交换。
常用协议:
TCP/IP协议:
  传输控制协议/因特网互联协议
  是因特网最基本、最广泛的协议
  定义了计算机如何连入因特网,数据如何在它们之间传输的标准
  分为数据链路层、网络层、传输层和应用层
  数据链路层:用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对网线、光纤提供的驱动。
  网络层:TCP/IP协议的核心,主要用于对传输的数据进行分组,将分组的数据发送到目标计算机或网络。
  传输层:主要使网络程序进行通信,在进行网络通信时,可以使用TCP协议或UDP协议。
  应用层:主要负责应用程序的协议。例如HTTP协议、FTP协议等。
1.2 网络通信协议分类
UDP 用户数据报协议
  面向无连接(传输数据时发送端和接收端不建立逻辑连接)
  不保证数据的完整性 不能用于传输重要数据
  消耗资源少 通信效率高
  通常用于音频、视频和普通数据的传输
  数据限制在64KB以内 超出不能一次发送
TCP 传输控制协议
  面向连接(传输数据之前发送端和接收端建立逻辑连接之后再传输数据)
  可靠无差错的数据传输
  在TCP中要明确客户端和服务器端,由客户端向服务器端发起请求,每次建立连接都要经过三次握手
  三次握手:在TCP协议中,在发送数据的准备阶段,客户端与服务器端的三次交互,保证连接的可靠
  第一次:客户端向服务器端发送连接请求,等待服务器端确认
  第二次:服务器端向客户端回送一个响应,通知客户端收到了连接请求
  第三次:客户端再次向服务器端发送确认信息,确认连接
  可以保证数据传输的安全性,应用广泛,如下载文件、浏览网页等
1.3 网络编程三要素
协议、IP地址、端口号
协议:
  计算机网络通信必须遵守的规则
IP地址:
  互联网协议地址。给一个网络中的每一台计算机的唯一编号。
  IPv4:4个字节,32位的二进制数。表示为a.b.c.d,如192.168.65.100。其中abcd都是0-255之间的十进制数,最多可以表示42亿个(2的32次方)。
  IPv6:16个字节,128位的二进制数。
  常用命令:ipconfig 查看ip设置
       ping IP地址 查看是否可以与该IP地址进行通信
  本机IP地址:127.0.0.1
  本地IP地址的域名:localhost
端口号:
  是一个逻辑端口,无法直接看到,可以通过一些软件查看端口号
  使用网络软件时,一打开该软件,OS就会为其分配一个随机端口号或一打开软件就向OS要指定的端口号
  2个字节,取值在0-65535之间。1024之前不能使用,已被系统分配给已知软件。端口号不能重复。
  常用端口号:网络端口 80 www.baidu.com:80
        数据库端口 MySQL 3306 Oracle 1521
        Tomcat服务器 8080
使用IP地址+端口号,就可以保证数据准确无误地发送到对方计算机的指定软件上。
1.4 TCP通信程序
使用TCP进行通信的Java程序
  TCP通信能实现两台计算机之间的数据交互,通信两端严格区分为客户端和服务器端。
通信步骤:
  (1)服务器端先启动,等待客户端的连接
  (2)客户端主动连接服务器端,连接成功才能通信。服务器端不能主动连接客户端。
  (3)客户端和服务器端建立逻辑连接。连接中包含一个IO对象,因为通信数据不仅是字符,所以是字节流对象。
  (4)客户端和服务器端使用这个IO对象进行通信。
客户端
  IP地址:端口号 Socket类
服务器端
  IP地址:端口号 ServerSocket类
  客户端和服务器端进行一次数据交互,需要4个IO流对象。客户端发送OutputStream,服务器接收InputStream,服务器回送OutputStream,客户端接收InputStream。
服务端需要明确两件事:
  (1)与哪个客户端进行的交互。服务器端有一个accept方法可以获取到请求的客户端对象。Socket s1 = server.accept();
  (2)交互需要多个IO流对象,服务器端没有IO流,服务器可以获取请求的客户端对象,使用每个客户端提供的IO流与每个客户端进行交互。
  服务器使用客户端的字节输入流读取客户端发送的数据。
  服务器使用客户端的字节输出流给客户端回写数据。
1.5 示例
TCP通信的客户端
  向服务器端发送连接请求,发送数据,读取服务器端回写的数据
  使用java.net包里的Socket类,实现客户端套接字(套接字就是包含了IP地址和端口号的网络单位)
  构造方法:Socket(String host, int port);host服务器的主机名称/IP地址 port服务器的端口号
  成员方法:OutputStream getOutputStream();返回此套接字的输出流
       InputStream getInputStream();返回此套接字的输入流
       close();关闭此套接字
  使用步骤:
  (1)创建Socket对象,绑定服务器的IP地址和端口号
  (2)调用Socket对象的getOutputStream方法,获取网络字节输出流对象
  (3)调用OutputStream对象的write方法,给服务器发送数据
  (4)调用Socket对象的getInputStream方法,获取网络字节输入流对象
  (5)调用InputStream对象的read方法,读取服务器回写的数据
  (6)调用Socket对象的close方法,关闭此客户端,释放资源
  注意:
  (1)客户端和服务器端进行交互,必须使用Socket对象提供的流对象,不能使用自己创建的流对象
  (2)创建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 os = socket.getOutputStream();
    //3、调用OutputStream对象的write方法,给服务器发送数据
    os.write(“我是客户端”.getBytes());
    //4、调用Socket对象的getInputStream方法,获取网络字节输入流对象
    InputStream is = socket.getInputStream();
    int len = 0;
    byte[] bytes = new byte[1024];
    len = is.read(bytes);
    System.out.println(new String(bytes, 0, len));
    //5、调用InputStream对象的read方法,读取服务器回写的数据
    //  while ((len = is.read(bytes)) != -1) {
    //    System.out.println(new String(bytes, 0, len));
    //  }
    //6、调用Socket对象的close方法,关闭此客户端,释放资源
    socket.close();
  }
}
TCP通信的服务器端
  接收客户端请求,读取客户端发送的数据,回写数据给客户端
  使用java.net包里的ServerSocket类,实现服务器端套接字(套接字就是包含了IP地址和端口号的网络单位)
  构造方法:ServerSocket(int port);因为客户端创建对象时绑定了服务器端指定的端口号,所以这里创建服务器对象时也要向系统请求指定的端口号,否则客户端会找不到该服务器
  成员方法:Socket accept();监听发送请求的客户端对象并连接
  实现步骤:
  (1)创建ServerSocket对象,向系统请求指定的端口号
  (2)调用ServerSocket对象的accept方法,获取到请求的客户端对象Socket
  (3)调用Socket对象的getInputStream方法,获取网络字节输入流对象
  (4)调用InputStream对象的read方法,读取客户端发送的数据
  (5)调用Socket对象的getOutputStream方法,获取网络字节输出流对象
  (6)调用OutputStream对象的write方法,给客户端回写数据
  (7)调用Socket对象和ServerSocket对象的close方法,关闭此客户端,释放资源
public class TCPServer {
  public static void main(String[] args) throws IOException {
    //1、创建ServerSocket对象,向系统请求指定的端口号
    ServerSocket serverSocket = new ServerSocket(8888);
    //2、调用ServerSocket对象的accept方法,获取到请求的客户端对象Socket
    Socket socket = serverSocket.accept();
    //3、调用Socket对象的getInputStream方法,获取网络字节输入流对象
    InputStream is = socket.getInputStream();
    int len = 0;
    byte[] bytes = new byte[1024];
    len = is.read(bytes);
    System.out.println(new String(bytes, 0, len));
    //4、调用InputStream对象的read方法,读取客户端发送的数据
    //  while ((len = is.read(bytes)) != -1) {
    //    System.out.println(len);
    //    System.out.println(new String(bytes, 0, len));
    //  }
    //5、调用Socket对象的getOutputStream方法,获取网络字节输出流对象
    OutputStream os = socket.getOutputStream();
    //6、调用OutputStream对象的write方法,给客户端回写数据
    os.write(“我是服务器”.getBytes());
    //7、调用Socket对象和ServerSocket对象的close方法,关闭此客户端,释放资源
    socket.close();
    serverSocket.close();
  }
}

2、文件上传案例

TCP通信的文件上传案例
  客户端读取本地文件,上传到服务器,服务器将上传的文件存到服务器的硬盘
  步骤:
  (1)客户端使用本地的字节输入流,读取要上传的文件
  (2)客户端使用网络字节输出流,将读取到的文件上传到服务器
  (3)服务器使用网络字节输入流,读取上传的文件
  (4)服务器使用本地的字节输出流,将文件存入服务器的硬盘
  (5)服务器使用网络字节输出流,给客户端回写“上传成功”
  (6)客户端使用网络字节输入流读取服务器回写的“上传成功”
  (7)关闭本地流对象,释放资源
  (8)关闭客户端和服务器,释放资源
  注意:客户端或服务器对本地硬盘进行读写,使用自己创建的字节流对象(本地流)
     客户端和服务器之间进行交互,使用Socket提供的网络字节流对象
文件上传客户端
 (1)客户端使用本地的字节输入流,读取要上传的文件
 (2)客户端使用网络字节输出流,将读取到的文件上传到服务器
 (6)客户端使用网络字节输入流读取服务器回写的“上传成功”
 (7)关闭本地流对象,释放资源
 (8)关闭客户端和服务器,释放资源
public class TCPClient {
  public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream(“H:\图片\壁纸.jpeg”);
    Socket socket = new Socket(“127.0.0.1”, 8888);
    OutputStream os = socket.getOutputStream();
    int len = 0;
    byte[] bytes = new byte[1024];
    while ((len = fis.read(bytes)) != -1) {
       //读取到-1就跳出了循环,不会将-1写入文件,所以服务器读取不到-1,服务器的循环会阻塞,我们需要在文件的内容写完之后手动写入一个结束标记
      os.write(bytes, 0, len);
    }
    socket.shutdownOutput();
    InputStream is = socket.getInputStream();
    while ((len = is.read(bytes)) != -1) {
      System.out.println(new String(bytes, 0, len));
    }
    fis.close();
    socket.close();
  }
}
文件上传服务器端
 (3)服务器使用网络字节输入流,读取上传的文件
 (4)服务器使用本地的字节输出流,将文件存入服务器的硬盘
 (5)服务器使用网络字节输出流,给客户端回写“上传成功”
 (7)关闭本地流对象,释放资源
 (8)关闭客户端和服务器,释放资源
  改进:
  (1)使用毫秒值对文件名命名,防止文件重名被覆盖
  (2)使用死循环让服务器持续监听客户端请求
  (3)使用多线程提高效率 一个客户端上传文件就开启一个线程
public class TCPServer {
  public static void main(String[] args) throws IOException {
    ServerSocket server = new ServerSocket(8888);
    while (true) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          try {
            Socket socket = server.accept();
            File file = new File(“G:\有的没的\upload”);
            String filename = “壁纸” + System.currentTimeMillis() + “.jpeg”;
            if (!file.exists()) {
              file.mkdir();
            }
            FileOutputStream fos = new FileOutputStream(file + “\” + filename);
            InputStream is = socket.getInputStream();
            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = is.read(bytes)) != -1) {
              fos.write(bytes, 0, len);
            }
            OutputStream os = socket.getOutputStream();
            os.write(“上传成功”.getBytes());
            fos.close();
            socket.close();
          }catch (IOException e) {
            System.out.println(e);
          }
        }
      }).start();
    }
    //server.close();
  }
}

3、B/S架构

模拟B/S服务器
  浏览器解析服务器回写的html页面,如果页面中含有图片,浏览器就会单独地开启一个线程,读取服务器的图片
  让服务器保持监听,浏览器请求一次,服务器回写一次,一张图片一个线程
public class TCPServer {
  public static void main(String[] args) throws IOException {
    ServerSocket server = new ServerSocket(8080);
    Socket socket = server.accept();
    InputStream is = socket.getInputStream();
    //把网络字节输入流对象is转换为缓冲字符输入流
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    //读取一行
    String getText = br.readLine();
    //将字符串按空格分割为字符串数组
    String[] getTextArr = getText.split(" “);
    //字符串数组的索引1是html文件的相对地址
    String htmlAddress = getTextArr[1];
    System.out.println(htmlAddress);
    //将最前面的/去掉
    htmlAddress = htmlAddress.substring(1);
    System.out.println(htmlAddress);
    OutputStream os = socket.getOutputStream();
    FileInputStream fis = new FileInputStream(htmlAddress);
    int len = 0;
    byte[] bytes = new byte[1024];
    //写入HTTP协议响应头,固定写法
    os.write(“HTTP/1.1 200 OK\r\n”.getBytes());
    os.write(“Content-Type:text/html\r\n”.getBytes());
    //写入空行,否则浏览器不解析
    os.write(”\r\n".getBytes());
    while ((len = fis.read(bytes)) != -1) {
      os.write(bytes, 0, len);
    }
    //  int len = 0;
    //  byte[] bytes = new byte[1024];
    //  while ((len = is.read(bytes)) != -1) {
    //    System.out.println(new String(bytes, 0, len));
    //  }
    fis.close();
    socket.close();
    server.close();
     /*
     启动服务器后使用浏览器访问http://127.0.0.1:8080/day09-code/web/index.html
     下面是服务器读取的客户端的请求信息:
    /
    //GET /day09-code/web/index.html HTTP/1.1
    //Host: 127.0.0.1:8080
    //Connection: keep-alive
    //Upgrade-Insecure-Requests: 1
    //User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
    //Sec-Fetch-User: ?1
    //Accept:    text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
/;q=0.8,application/signed-exchange;v=b3
    //Sec-Fetch-Site: none
    //Sec-Fetch-Mode: navigate
    //Accept-Encoding: gzip, deflate, br
    //Accept-Language: zh-CN,zh;q=0.9
    /

     服务器要给客户端回写一个html页面
     读取index.html页面文件,文件地址就是上面请求信息的第一行
    */
  }
}
服务器线程
public class TCPServerThread {
  public static void main(String[] args) throws IOException {
    ServerSocket server = new ServerSocket(8080);
    while (true) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          try {
            Socket socket = server.accept();
            InputStream is = socket.getInputStream();
            //把网络字节输入流对象is转换为缓冲字符输入流
            BufferedReader br = new BufferedReader(
                        new InputStreamReader(is));
            //读取一行
            String getText = br.readLine();
            //将字符串按空格分割为字符串数组
            String[] getTextArr = getText.split(" “);
            //字符串数组的索引1是html文件的相对地址
            String htmlAddress = getTextArr[1];
            System.out.println(htmlAddress);
            //将最前面的/去掉
            htmlAddress = htmlAddress.substring(1);
            System.out.println(htmlAddress);
            OutputStream os = socket.getOutputStream();
            FileInputStream fis = new FileInputStream(htmlAddress);
            int len = 0;
            byte[] bytes = new byte[1024];
            //写入HTTP协议响应头,固定写法
            os.write(“HTTP/1.1 200 OK\r\n”.getBytes());
            os.write(“Content-Type:text/html\r\n”.getBytes());
            //写入空行,否则浏览器不解析
            os.write(”\r\n".getBytes());
            while ((len = fis.read(bytes)) != -1) {
              os.write(bytes, 0, len);
            }
            fis.close();
            socket.close();
          } catch (IOException e) {
            System.out.println(e);
          }
        }
      }).start();
    }
    //server.close();
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值