Java foundation - network programming articles

1. Network Programming Fundamentals

1.1 software architecture

C / S structure : called the Client / Server structure, and refers to a client server architecture. Common program has QQ, Thunder and other software.

B / S structure : called the Browser / Server structure refers to the structure of the browser and the server. Common browsers Google, Firefox and so on.

Both architectures have their own advantages, but no matter what architecture can do without the support of the network. Network programming , is in a certain protocol, to achieve two computers communication program.

1.2 Network Communication Protocol

Network communication protocol: communication protocol is a rule that must be followed on the computer, only to comply with these rules, in order to communicate between computers. This is like driving in a road car must comply with traffic rules, the agreement on the transmission format, transmission speed, data, and so do the steps

Uniform provisions, the communicating parties must comply with the final completion of data exchange.

TCP / IP protocols: Transmission Control Protocol / Internet Protocol (Transmission Control Protocol / Internet Protocol) , is

Internet most basic, the most widely used protocol. It defines how the computer connected to the Internet, and how the standard data transfer therebetween. it

The interior contains a series of protocols for handling data communication and uses a hierarchical model 4 layers, each layer the next layer call it provided

Agreement to complete their needs.

1.3 protocol classification

Communication protocol is quite complex, java.net package contains the classes and interfaces that provide low-level details of the communication. We can use these classes and interfaces to specialize in web application development, regardless of the details of communication.

  • TCP: Transmission Control Protocol (Transmission Control Protocol). TCP protocol is a communication protocol for the connection, i.e. the data prior to transmission,

    在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。

    • 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
      1. 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
      2. 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
      3. 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
    • 完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
  • UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需

    要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个

    数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应

    用中,例如视频会议、QQ聊天等。

1.4 网络编程三要素

  1. 协议:计算机网络通信必须遵守的规则,已经介绍过了,不再赘述。

  2. IP地址:

    • 什么是IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。

    • IP地址分类:

    • IPv4:是一个32位的二进制数,通常被分为4个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。
    • IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。有资料显示,全球IPv4地址在2011年2月分配完毕。 为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进 制数,表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。

    • 常用命令:

      • 查看本机IP地址,在控制台输入: ipconfig

      • 检查网络是否连通,在控制台输入:

        ping 空格 IP地址
        ping 220.181.57.216
        【特殊的IP地址】
        本机IP地址: 127.0.0.1 、 localhost 。
  3. 端口号:

    ​ 网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分这些进程呢?

    ​ 如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。

    ​ 端口号:用两个字节表示的整数,它的取值范围是0-65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。

    ​ 利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。

2. TCP通信程序

2.1 TCP通信程序介绍

​ TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)服务端(Server)

  • 两端通信步骤
    1. 服务端程序,需要事先启动,等待客户端的连接。
    2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
  • 在Java中,提供了两个类用于实现TCP通信程序
    1. 客户端: java.net.Socket 类表示。创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建 立连接开始通信。
    2. 服务端: java.net.ServerSocket 类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端 的连接。

2.2 Socket类

Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。

  • 构造方法:

    • public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。

    • 回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。

    • 代码

      Socket client = new Socket("127.0.0.1", 6666);
  • 成员方法:

    • public InputStream getInputStream() : 返回此套接字的输入流。
      • 如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
      • 关闭生成的InputStream也将关闭相关的Socket。
    • public OutputStream getOutputStream() : 返回此套接字的输出流。
      • 如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
      • 关闭生成的OutputStream也将关闭相关的Socket。
    • public void close() :关闭此套接字
      • 一旦一个socket被关闭,它不可再使用。
      • 关闭此socket也将关闭相关的InputStream和OutputStream 。
    • public void shutdownOutput() : 禁用此套接字的输出流
      • 任何先前写出的数据将被发送,随后终止输出流。

2.3 ServerSocket类

ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求。

  • 构造方法:public ServerSocket(int port):使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指 定的端口号上,参数port就是端口号。

    • 代码

      ServerSocket server = new ServerSocket(6666);
  • 成员方法:public Socket accept():侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法 会一直阻塞直到建立连接。

2.4 简单的TCP网络程序

  • TCP通信分析图解

    1. 【服务端】启动,创建ServerSocket对象,等待连接。
    2. 【客户端】启动,创建Socket对象,请求连接。
    3. 【服务端】接收连接,调用accept方法,并返回一个Socket对象。
    4. 【客户端】Socket对象,获取OutputStream,向服务端写出数据。
    5. 【服务端】Scoket对象,获取InputStream,读取客户端发送的数据。

    6. 【服务端】Socket对象,获取OutputStream,向客户端回写数据。
    7. 【客户端】Scoket对象,获取InputStream,解析回写数据。
    8. 【客户端】释放资源,断开连接。

  • 客户端向服务器发送数据

    • 服务端代码

      public static void main(String[] args) throws IOException {
          System.out.println("启动服务端...");
          // 1. 创建ServerSocket对象
          ServerSocket server = new ServerSocket(6666);
          // 2. 接收客户端的socket对象
          Socket socket = server.accept();
          // 3. 创建字节输入流对象
          InputStream is = socket.getInputStream();
          // 4. 读取数据
          int len = 0;
          byte[]bts = new byte[1024];
          len = is.read(bts);
          System.out.println(new String(bts,0,len));
          // 关闭网络流
          is.close();
          // 关闭socket
          socket.close();
      
        }
    • 客户端代码

      public static void main(String[] args) throws IOException {
          System.out.println("启动客户端");
          // 1. 创建Socket对象
          Socket sk = new Socket("127.0.0.1",6666);
          // 2. 创建字节网络流输出对象
          OutputStream os = sk.getOutputStream();
          // 3. 输出内容
          os.write("你好哈,服务端大大".getBytes());
          // 关闭网络流
          os.close();
          // 关闭socket
          sk.close();
        }
  • 服务器向客户端回写数据

  • 服务端代码

    public static void main(String[] args) throws IOException {
        System.out.println("启动服务端...");
        // 1. 创建ServerSocket对象
        ServerSocket server = new ServerSocket(6666);
        // 2. 接收客户端的socket对象
        Socket socket = server.accept();
        // 3. 创建字节输入流对象
        InputStream is = socket.getInputStream();
        // 4. 读取数据
        int len = 0;
        byte[]bts = new byte[1024];
        len = is.read(bts);
        System.out.println(new String(bts,0,len));
        // 【-------服务端回写数据--------】
        // 1. 创建输出网络流
        OutputStream os = socket.getOutputStream();
        // 2. 写入内容
        os.write("我很好,谢谢,思密达".getBytes());
        // 关闭socket
        socket.close();
        server.close();
    
      }
  • 客户端代码

    public static void main(String[] args) throws IOException {
        System.out.println("启动客户端");
        // 1. 创建Socket对象
        Socket sk = new Socket("127.0.0.1",6666);
        // 2. 创建字节网络流输出对象
        OutputStream os = sk.getOutputStream();
        // 3. 输出内容
        os.write("你好哈,服务端大大".getBytes());
        // 【解析服务端的回应】
        // 1. 创建输入网络流
        InputStream is = sk.getInputStream();
        // 2. 读取数据
        int len = 0;
        byte[]bs = new byte[1024];
        len = is.read(bs);
        System.out.println(new String(bs,0,len));
        // 关闭socket
        sk.close();
      }

3. 文件上传

3.1 文件上传分析图

3.2 基本实现

  • 服务端代码

      public static void main(String[] args) throws IOException {
        System.out.println("启动服务端...");
        // 1. 创建SeverSocket对象,用来处理客户端请求
        ServerSocket sever = new ServerSocket(6666);
        // 2. 获取socket对象,用来处理请求
        Socket socket = sever.accept();
        // 3. 创建本地输入流对象,用来读取客户端发送的数据
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        // 4. 创建本地输出流对象,把客户端上传的文件写入本地
        String fileName = System.currentTimeMillis() + "" + (new Random().nextInt(99999)) + ".jpg";
        File file = new File("d:\\upload");
        if(!file.exists()){
          file.mkdirs();
        }
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file +"\\"+ fileName));
        // 5. 循环读取客户端数据
        int len = 0;
        byte[]bts = new byte[1024];
        while((len=bis.read(bts))!=-1){
          bos.write(bts,0,len);
        }
        // 关闭资源
        bis.close();
        bos.close();
        socket.close();
        sever.close();
    
      }
  • 客户端代码

      public static void main(String[] args) throws IOException {
        System.out.println("启动客户端...");
        // 1. 创建Socket对象,连接服务端
        Socket socket = new Socket("127.0.0.1",6666);
        // 2. 创建本地输入流对象,读取本地文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\Bruce\\Desktop\\logo\\logo01.png"));
        // 3. 创建网络输出流对象,向服务端传送数据
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        // 4. 定义字节数组,设置每次读取的字节
        int len = 0;
        byte[]bts = new byte[1024];
        // 5. 循环读取本地文件数据,并向服务端输出
        while ((len=bis.read(bts))!=-1){
          bos.write(bts,0,len);
        }
        System.out.println("客户端:文件上传完毕!");
        // 关闭资源
        bis.close();
        bos.close();
        socket.close();
      }

3.3 优化分析

  1. 循环接收问题

    • 问题:服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断 的接收不同用户的文件

    • 代码:

      while(true){
         Socket accept = serverSocket.accept();
         ......
      }
      
  2. 效率问题

    • 问题:服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优 化

    • 代码:

      while(true){
          Socket accept = serverSocket.accept();
          // accept 交给子线程处理.
          new Thread(() ‐> {
              ......
              InputStream bis = accept.getInputStream();
              ......
          }).start();
      }
  3. 代码

    public static void main(String[] args) throws IOException {
        System.out.println("启动服务端...");
        // 1. 创建SeverSocket对象,用来处理客户端请求
        ServerSocket sever = new ServerSocket(6666);
        while (true){
          new Thread(new Runnable() {
            @Override
            public void run() {
              try {
                // 2. 获取socket对象,用来处理请求
                Socket socket = sever.accept();
                // 3. 创建本地输入流对象,用来读取客户端发送的数据
                BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
                // 4. 创建本地输出流对象,把客户端上传的文件写入本地
                String fileName = System.currentTimeMillis() + "" + (new Random().nextInt(99999)) + ".jpg";
                File file = new File("d:\\upload");
                if(!file.exists()){
                  file.mkdirs();
                }
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file +"\\"+ fileName));
                // 5. 循环读取客户端数据
                int len = 0;
                byte[]bts = new byte[1024];
                while((len=bis.read(bts))!=-1){
                  bos.write(bts,0,len);
                }
                // 关闭资源
                bis.close();
                bos.close();
                socket.close();
                System.out.println(Thread.currentThread().getName()+":已经保存到本地!");
              }catch (IOException e) {
                e.printStackTrace();
              }
            }
          }).start();
        }
    
      }

3.4 回写实现

  • 图解

  • 代码

    • 服务端

        public static void main(String[] args) throws IOException {
          System.out.println("启动服务端...");
          // 1. 创建SeverSocket对象,用来处理客户端请求
          ServerSocket sever = new ServerSocket(6666);
          while (true){
            new Thread(new Runnable() {
              @Override
              public void run() {
                try {
                  // 2. 获取socket对象,用来处理请求
                  Socket socket = sever.accept();
                  // 3. 创建本地输入流对象,用来读取客户端发送的数据
                  BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
                  // 4. 创建本地输出流对象,把客户端上传的文件写入本地
                  String fileName = System.currentTimeMillis() + "" + (new Random().nextInt(99999)) + ".jpg";
                  File file = new File("d:\\upload");
                  if(!file.exists()){
                    file.mkdirs();
                  }
                  BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file +"\\"+ fileName));
                  // 5. 循环读取客户端数据
                  int len = 0;
                  byte[]bts = new byte[1024];
                  while((len=bis.read(bts))!=-1){
                    bos.write(bts,0,len);
                  }
                  // 回写客户端----------------
                  OutputStream os = socket.getOutputStream();
                  os.write("服务器端:已经保存在服务端本地".getBytes());
                  // 关闭资源
                  bis.close();
                  bos.close();
                  os.close();
                  socket.close();
                  System.out.println(Thread.currentThread().getName()+":已经保存到本地!");
                }catch (IOException e) {
                  e.printStackTrace();
                }
              }
            }).start();
          }
      
        }
    • 客户端

        public static void main(String[] args) throws IOException {
          System.out.println("启动客户端...");
          // 1. 创建Socket对象,连接服务端
          Socket socket = new Socket("127.0.0.1",6666);
          // 2. 创建本地输入流对象,读取本地文件
          BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\Bruce\\Desktop\\logo\\logo01.png"));
          // 3. 创建网络输出流对象,向服务端传送数据
          BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
          // 4. 定义字节数组,设置每次读取的字节
          int len = 0;
          byte[]bts = new byte[1024];
          // 5. 循环读取本地文件数据,并向服务端输出
          while ((len=bis.read(bts))!=-1){
            bos.write(bts,0,len);
          }
          bos.flush();
          // 关闭输出流,通知服务端,写出数据完毕
          socket.shutdownOutput();
          System.out.println("客户端:文件上传完毕!");
          // 接收服务返回的信息
          InputStream is = socket.getInputStream();
          while ((len=is.read(bts))!=-1){
            System.out.print(new String(bts,0,len));
          }
          // 关闭资源
          is.close();
          bis.close();
          socket.close();
        }

4. 模拟B/S服务器

  • 项目中有一个web项目,我们写服务端代码,然后通过浏览器输入地址127.0.0.1:8888/web/index.html

    访问网页。

  • 代码:

    public class WebSever {
      public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象
       ServerSocket server = new ServerSocket(8888);
       while (true){
         new Thread(new Runnable() {
           @Override
           public void run() {
            try{
              // 获取socket对象
              Socket socket = server.accept();
              // 读取接收的内容
              BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
              String line = reader.readLine(); // GET /web/index.html HTTP/1.1
              String path = line.split(" ")[1].substring(1);
              // 创建本地字节输入流
              FileInputStream fis = new FileInputStream("day09_Socket\\" +path);
              // 创建网络输出流
              OutputStream os = socket.getOutputStream();
              // 写入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());
              int len = 0;
              byte[]bts = new byte[1024];
    
              while((len=fis.read(bts))!=-1){
                os.write(bts,0,len);
              }
              // 关闭资源
              os.close();
              fis.close();
              reader.close();
              socket.close();
            } catch (IOException e){
              e.printStackTrace();
            }
           }
         }).start();
       }
      }
    }
  • 页面效果

Guess you like

Origin www.cnblogs.com/bruce1993/p/11876555.html