概述

常见计算机网络

  • 互联网:(Internet)点与点相连。
  • 万维网:(WWW – World Wide Web)端与端相连。
  • 物联网:( IoT - Internet of things) 物与物相连。
  • 网络编程:让计算机与计算机之间建立连接、进行通信。

    网络通信协议

    通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。

    网络模型【重点

    OSI参考模型

    OSI(Open System Interconnect),即开放式系统互联。

  • 是ISO组织在1985年研究的网络互联模型。

  • 该体系结构标准定义了网络互联的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层和应用层)。

第13章 网络编程 - 图1

每层功能:

  • 第七层:应用层负责文件访问和管理、可靠运输服务、远程操作服务。(HTTP、FTP、SMTP)。
  • 第六层:表示层负责定义转换数据格式及加密,允许选择以二进制或ASCII格式传输。
  • 第五层:会话层负责使应用建立和维持会话,使通信在失效时继续恢复通信。(断点续传)。
  • 第四层:传输层负责是否选择差错恢复协议、数据流重用、错误顺序重排。(TCP、UDP)。
  • 第三层:网络层负责定义了能够标识所有网络节点的逻辑地址。(IP地址)。
  • 第二层:链路层在物理层上,通过规程或协议(差错控制)来控制传输数据的正确性。(MAC)。
  • 第一层:物理层为设备之间的数据通信提供传输信号和物理介质。(双绞线、光导纤维)。

    TCP/IP模型

  • TCP/IP模型是因特网使用的参考模型,基于TCP/IP的参考模型将协议分成四个层次。

  • 该模型中最重要的两个协议是TCP和IP协议。

第13章 网络编程 - 图2第13章 网络编程 - 图3

上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能。
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层(高速公路):网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
运输层(卡车):主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层(货物):主要负责应用程序的协议,例如HTTP协议、FTP协议等。

网络编程

UDP和TCP

通信的协议还是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net 包中提供了两种常见的网络协议的支持

UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
image.png
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

  • 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
  • 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
  • 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。

第13章 网络编程 - 图5
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
TCP/UDP区别
TCP(传输控制协议,Transmission Control Protocol):(类似打电话)面向连接、传输可靠(保证数据正确性)、有序(保证数据顺序)、传输大量数据(流模式)、速度慢、对系统资源的要求多,程序结构较复杂,每一条TCP连接只能是点到点的,TCP首部开销20字节。
UDP(用户数据报协议,User Data Protocol):(类似发短信)面向非连接 、传输不可靠(可能丢包)、无序、传输少量数据(数据报模式)、速度快,对系统资源的要求少,程序结构较简单 ,UDP支持一对一,一对多,多对一和多对多的交互通信,UDP的首部开销小,只有8个字节。

网络编程三要素

协议

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

    IP地址

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


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

    • 本机IP地址:127.0.0.1localhost

      端口号

      网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分这些进程呢?
      如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。
  • 端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。

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

TCP编程

概述

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

  1. 服务端程序,需要事先启动,等待客户端的连接。
  2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。

在Java中,提供了两个类用于实现TCP通信程序:

  1. 客户端:java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
  2. 服务端:java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。

006.png
开发步骤:

  • 建立通信连接(会话):
    • 创建ServerSocket,指定端口号。
    • 调用accept等待客户端接入。
  • 客户端请求服务器:
    • 创建Socket,指定服务器IP + 端口号。
    • 使用输出流,发送请求数据给服务器。
  • 使用输入流,接收响应数据到客户端(等待)
    • 服务器响应客户端。
    • 使用输入流,接收请求数据到服务器(等待)。
    • 使用输出流,发送响应数据给客户端。

      客户端向服务器发送数据

      示例1:

      服务端实现: ```java package com.alpaak.part13.demo1;

import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket;

/**

  • 服务器端-ServerSocket */ public class ServerTCP { public static void main(String[] args) throws IOException {
    1. System.out.println("服务端启动 , 等待连接 .... ");
    2. // 1.创建 ServerSocket对象,绑定端口,开始等待连接
    3. ServerSocket ss = new ServerSocket(6666);
    4. // 2.接收连接 accept 方法, 返回 socket 对象.
    5. Socket server = ss.accept();
    6. // 3.通过socket 获取输入流
    7. InputStream is = server.getInputStream();
    8. // 4.一次性读取数据
    9. // 4.1 创建字节数组
    10. byte[] b = new byte[1024];
    11. // 4.2 据读取到字节数组中.
    12. int len = is.read(b);
    13. // 4.3 解析数组,打印字符串信息
    14. String msg = new String(b, 0, len);
    15. System.out.println(msg);
    16. //5.关闭资源.
    17. is.close();
    18. server.close();
    } } **客户端实现:**java package com.alpaak.part13.demo1;

import java.io.OutputStream; import java.net.Socket;

/**

  • 客户端-Socket */ public class ClientTCP { public static void main(String[] args) throws Exception {
     System.out.println("客户端 发送数据");
     // 1.创建 Socket ( ip , port ) , 确定连接到哪里.
     Socket client = new Socket("localhost", 6666);
     // 2.获取流对象 . 输出流
     OutputStream os = client.getOutputStream();
     // 3.写出数据.
     os.write("你好么? tcp ,我来了4".getBytes());
     // 4. 关闭资源 .
     os.close();
     client.close();
    
    } } ```

    服务器向客户端回写数据

    示例2:

    服务端实现: ```java package com.alpaak.part13.demo2;

import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;

/**

  • 服务器向客户端回写数据
  • 服务端实现: */ public class ServerTCP { public static void main(String[] args) throws IOException {
     System.out.println("服务端启动 , 等待连接 .... ");
     // 1.创建 ServerSocket对象,绑定端口,开始等待连接
     ServerSocket ss = new ServerSocket(6666);
     // 2.接收连接 accept 方法, 返回 socket 对象.
     Socket server = ss.accept();
     // 3.通过socket 获取输入流
     InputStream is = server.getInputStream();
     // 4.一次性读取数据
       // 4.1 创建字节数组
     byte[] b = new byte[1024];
       // 4.2 据读取到字节数组中.
     int len = is.read(b);
     // 4.3 解析数组,打印字符串信息
     String msg = new String(b, 0, len);
     System.out.println(msg);
       // =================回写数据=======================
       // 5. 通过 socket 获取输出流
        OutputStream out = server.getOutputStream();
       // 6. 回写数据
        out.write("我很好,谢谢你".getBytes());
       // 7.关闭资源.
       out.close();
     is.close();
     server.close();
    
    } } **客户端实现:**java package com.alpaak.part13.demo2;

import java.io.InputStream; import java.io.OutputStream; import java.net.Socket;

/**

  • 服务器向客户端回写数据
  • 客户端实现: */ public class ClientTCP { public static void main(String[] args) throws Exception {
     System.out.println("客户端 发送数据");
     // 1.创建 Socket ( ip , port ) , 确定连接到哪里.
     Socket client = new Socket("localhost", 6666);
     // 2.通过Scoket,获取输出流对象 
     OutputStream os = client.getOutputStream();
     // 3.写出数据.
     os.write("你好么? tcp ,我来了".getBytes());
       // ==============解析回写=========================
       // 4. 通过Scoket,获取 输入流对象
       InputStream in = client.getInputStream();
       // 5. 读取数据数据
       byte[] b = new byte[100];
       int len = in.read(b);
       System.out.println(new String(b, 0, len));
     // 6. 关闭资源 .
       in.close();
     os.close();
     client.close();
    
    } } ```

    案例:简易聊天

    示例3

    服务端 ```java package com.alpaak.part13.demo3;

import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner;

/**

  • 简易聊天 - 服务器端 */ public class TCPServer_IM {

    public static void main(String[] args){

     System.out.println("服务端启动。。。");
     Socket server = null;
     InputStream is = null;
     OutputStream os = null;
     //1,创建SocketServer对象,绑定端口,等待连接
     try {
         ServerSocket serverSocket = new ServerSocket(8888);
         Scanner sc  =new Scanner(System.in);
         //2,
         server = serverSocket.accept();
    
         while(true){
    
             //3,通过Socket获取输入流
             is = server.getInputStream();
             //4,读取数据
             byte[] buff = new byte[1024];
             int len = is.read(buff);
             String msg = new String(buff, 0, len);
             System.out.println("客户端:"+msg);
    
             //5,服务端回写数据
             os = server.getOutputStream();
             //System.out.println("请输入服务端信息:");
             String serverMsg = sc.nextLine();
             os.write(serverMsg.getBytes());
    
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         try {
             if(os!=null){
                 os.close();
             }
             if(is!=null){
                 is.close();
             }
             if(server!=null){
                 server.close();
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    

    } }

**客户端:**
```java
package com.alpaak.part13.demo3;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * 简易聊天 - 客户端
 */
public class TCPClient_IM {
    public static void main(String[] args) {
        System.out.println("客户端启动。。。。");
        Scanner sc = new Scanner(System.in);
        Socket client = null;
        OutputStream os = null;
        InputStream is = null;
        try {
            //1,创建Socket对象,连接到指定的服务端
            client = new Socket("localhost",8888);
           while(true){
               //2,通过socket对象获取输出流,给服务端发送消息
               os = client.getOutputStream();
               //System.out.println("请录入客户端的消息:");
               String input = sc.nextLine();
               os.write((input).getBytes());

               //3,获取服务器发送的消息
               is = client.getInputStream();
               //4,读取数据
               byte[] buff = new byte[1024];
               int len = is.read(buff);
               String msg = new String(buff, 0, len);
               System.out.println("服务端:"+msg);
           }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(is!=null){
                    is.close();
                }
                if(os!=null){
                    os.close();
                }
                if(client!=null){
                    client.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

UDP 编程

主要用到两个类DatagramPacket和DatagramSocket,下面分别介绍。

DatagramSocket

此类表示用来发送和接收数据报包的套接字。
构造方法:
DatagramSocket(int port) ~创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr) ~创建数据报套接字,将其绑定到指定的本地地址。

DatagramPacket

此类表示数据报包。
两个都要先构造好相应的数据包(java.net.DatagramPacket)。
在DatagramPacket包中的函数 int getLength()返回实际接受的字节数,
DatagramPacket packet = new DatagramPacket(data,data.length);
要想接受端给发送端回信息,就需要知道发送端的IP地址InetAddress getAddress()和发送端进程所绑定的端口号int getPort()。
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
image.png

示例4:

客户端1:

package com.alpaak.part13.demo4;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


public class Client1 {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端1启动。。。。");

        /**
         * 接收信息
         */
        //1,创建一个指定端口的DatagramSocket
        DatagramSocket socket = new DatagramSocket(8888);
        //2,创建一个字节数组
        byte[] data = new byte[1024];
        //3,创建DatagramPacket接收客户端的信息
        DatagramPacket packet = new DatagramPacket(data,data.length);
        //4,接收消息
        socket.receive(packet);

        //读取数据
        String msg = new String(data, 0, packet.getLength());
        System.out.println("客户端2:"+msg);


        /**
         * 发送消息
         */
        //5,指定地址,端口
        byte[] data2 = "你好,我是客户端1".getBytes();
        InetAddress localhost = InetAddress.getByName("localhost");
        int port = 9999;
        //6,创建消息
        DatagramPacket packet2 = new DatagramPacket(data2,data2.length,localhost,port);
        //7,发送消息
        socket.send(packet2);

        //8
        socket.close();

    }
}

客户端2:

package com.alpaak.part13.demo4;

import java.io.IOException;
import java.net.*;

public class Client2 {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端2启动。。。。。");
        /**
         * 发送消息
         */
        //1,创建DatagramSocket,指定端口
        DatagramSocket socket = new DatagramSocket(9999);
        //2,指定地址端口
        InetAddress localhost = InetAddress.getByName("localhost");
        int port = 8888;
        //3,要发送的消息
        byte[] data = "你好,客户端1".getBytes();
        //4,创建
        DatagramPacket packet = new DatagramPacket(data,data.length,localhost,port);
        //5,发送
        socket.send(packet);

        /**
         * 接收消息
         */

        //1,创建一个数组
        byte[] data2 = new byte[1024];
        DatagramPacket packet2 = new DatagramPacket(data2,data2.length);
        //2,
        socket.receive(packet2);
        //3,解析发送过来的消息
        String msg = new String(data2, 0, packet2.getLength());
        System.out.println("客户端1:"+msg);


        socket.close();

    }
}