Skip to content

网络编程

  • 网络编程
    • 网络编程三要素 🍐 ❤️
    • UDP通讯 🚀 🍐
    • TCP通讯 🍐 ✏️完成案例即可
    • TCP和UDP的区别 🍐 ❤️

前置知识

  1. 完成过字节流复制文件的案例
  2. 理解字节流复制文件的传输过程
  3. 理解文件是由字节组成的
  4. 曾经有过发送图片给朋友,却因为网络问题,失败的经历(如图👉)

一、网络编程概述 🚩

网络编程:编写的应用程序可以与网络上其他设备中的应用程序进行数据交互

网络编程的作用:

  1. 如我们经常用的微信收发消息就需要用到网络通信的技术
  2. 如我们打开浏览器可以浏览各种网络、视频等也需要用到网络编程的技术

Java提供的网络编程的解决方案:

  1. 在java.net包下提供了诸多类,实现网络编程

网络通信的基本架构:

  1. CS架构(Client 客户端/Server服务端)
  2. BS架构(Brower 浏览器/Server服务端)
  • CS架构的特点: CS架构需要用户在自己的电脑或者手机上安装客户端软件,然后由客户端软件通过网络连接服务器程序,由服务器把数据发给客户端,客户端就可以在页面上看到各种数据了。
  • BS架构的特点: BS架构不需要开发客户端软件,用户只需要通过浏览器输入网址就可以直接从服务器获取数据,并由服务器将数据返回给浏览器,用户在页面上就可以看到各种数据了。

不管是CS、还是BS都是需要用到网络编程的相关技术

二、网络编程三要素 🚩 🍐 ❤️

网络编程三要素:IP地址、端口号、通信协议

如下图所示:假设现在要从一台电脑中的微信上,发一句“你愁啥?”到其他电脑的微信上,流程如下

java
1.先通过ip地址找到对方的电脑
2.再通过端口号找到对方的电脑上的应用程序
3.按照双方约定好的规则发送、接收数据

1️⃣ 2.1 IP地址

IP(Ineternet Protocol)全称互联网协议地址,是分配给网络设备的唯一表示。

IP地址分为:IPV4地址、IPV6地址

IPV4地址由32个比特位(4个字节)组成,如果下图所示,但是由于采用二进制太不容易阅读了,于是就将每8位看成一组,把每一组用十进制表示(叫做点分十进制表示法)。所以就有了我们经常看到的IP地址形式,如:192.168.1.66

如果想查看本机的IP地址,可以在命令行窗口(win+R cmd),输入ipconfig命令查看,如下图所示

经过不断的发展,现在越来越多的设备需要联网,IPV4地址已经不够用了(255 * 255 * 255 * 255=约42亿)

IPV6采用128位二进制数据来表示(16个字节),号称可以为地球上的每一粒沙子编一个IP地址

IPV6比较长,为了方便阅读,每16位编成一组,每组采用十六进制数据表示,然后用冒号隔开(称为冒分十六进制表示法),如下图所示

现在的网络设备,一般IPV4和IPV6地址都是支持的。

域名

域名是一个IP地址上的面具 。一个域名的目的是便于记忆和沟通的一组服务器的地址

如:百度的域名:https://www.baidu.com/open in new window 对应的ip地址是:14.119.104.189

域名如何找到匹配IP?

域名和IP其实是一一对应的,由运营商来管理域名和IP的对应关系。我们在浏览器上敲一个域名时,首先由运营商的域名解析服务,把域名转换为ip地址,再通过IP地址去访问对应的服务器设备。

本地回送地址127.0.0.1,方便测试使用(因为电脑没插网线或者没连接Wifi的时候没有分配ip地址)

点击查看常见的几个关于控制台命令

java
ipconfig: 查看本机的ip地址
ping 域名/ip  检测当前电脑与指定的ip是否连通

ping命令出现以下的提示,说明网络是通过的

使用Java代码获得本机的IP地址

Java中也有一个类用来表IP地址,这个类是InetAddress类

java
public class InetAddressTest {
    public static void main(String[] args) throws Exception {
        // 1、获取本机IP地址对象的
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());

        // 2、获取指定IP或者域名的IP地址对象。
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress());

        // ping www.baidu.com
        System.out.println(ip2.isReachable(6000));
    }
}

作业

🚩

  1. 使用win+R 输入cmd 打开控制台,输入ipconfig 查看本机的ip地址
  2. 或者打开网络适配器,查看网络详情,查看本机地址。或者打开飞秋查看你同学的网络地址
  3. 在Java类中使用InetAddress的api 获得本机地址(方法:getHostAddress)

2️⃣ 2.2 端口号

端口号

指的是计算机设备上运行的应用程序的标识,被规定为一个16位的二进制数据,范围(0~65535

端口号分类:了解

  • 周知端口:0~1023,被预先定义的知名应用程序占用(如:HTTP占用80,FTP占用21)
  • 注册端口:1024~49151,分配给用户经常或者某些应用程序
  • 动态端口:49152~65536,之所以称为动态端口,是因为它一般不固定分配给某进程,而是动态分配的。

常见的端口: 开发常见 👈

  1. 80端口 是http协议的端口
  2. 443端口 是https协议的端口
  3. 3306端口 是mysql数据库的端口
  4. 22端口 是文件上传的端口

注意:同一个计算机设备中,不能出现两个应用程序,用同一个端口号

3️⃣ 2.4 协议

协议

网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。

作用:

**协议的种类:**了解传输层协议

只要按照OSI网络参考模型制造的设备,就可以在国际互联网上互联互通

  • UDP协议特点
  • TPC协议特点

三次握手如下图所示 :目的是确认通信双方,手法消息都是正常没问题的

四次挥手如下图所示:目的是确保双方数据的收发已经完成,没有数据丢失

三、UDP通信代码(案例) 🚩

学习基于UDP协议通信的程序

UDP入门案例前置知识:

  1. 了解 UDP是面向无连接的、不需要确认双方是否存在,是不可靠的协议
  2. Java提供了一个类叫DatagramSocket来完成基于UDP协议的收发数据
  3. 使用DatagramSocket收发数据时,数据要以数据包的形式体现,一个数据包限制在64KB以内大小有限制
  4. 有发送端和接收端2个程序

1️⃣ 3.1 入门程序 ✏️

需求:客户端程序发一个字符串数据给服务端,服务端程序接收数据并打印。

java
public class Client {
    public static void main(String[] args) throws Exception {
//        创建数据报套接字并将其绑定到本机地址上的任何可用端口
        // 1、创建客户端对象(发韭菜出去的人)
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据(创建一个韭菜盘子)
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */
        byte[] bytes = "发送端:我是老王,你在吗".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                , InetAddress.getLocalHost(),  22222);

        // 3、开始正式发送这个数据包的数据出去了
        socket.send(packet);

        System.out.println("客户端数据发送完毕~~~");
        socket.close(); // 释放资源!
    }
}
java
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象(创建一个接韭菜的人) 注册端口
        DatagramSocket socket = new DatagramSocket(22222);

        // 2、创建一个数据包对象,用于接收数据的(创建一个韭菜盘子)
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        // 3、开始正式使用数据包来接收客户端发来的数据
        socket.receive(packet);

        // 4、从字节数组中,把接收到的数据直接打印出来
        // 接收多少就倒出多少
        // 获取本次数据包接收了多少数据。
        int len = packet.getLength();

        String rs = new String(buffer, 0 , len);
        System.out.println(rs);



        socket.close(); // 释放资源
    }
}

2️⃣ 3.2 进阶程序 ✏️ 增加一个while循环

需求:实现客户端不断的发数据,而服务端能不断的接收数据,客户端发送exit时客户端程序退出。

java
/**
 * 目标:完成UDP通信快速入门:实现客户端反复的发。
 */public class Client {
    public static void main(String[] args) throws Exception {
//        创建数据报套接字并将其绑定到本机地址上的任何可用端口
        // 1、创建客户端对象(发韭菜出去的人)
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据(创建一个韭菜盘子)
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */

         Scanner scanner = new Scanner(System.in);

        while (true) {
             String msg = scanner.next();
            // 一旦发现用户输入的exit命令,就退出客户端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临!退出成功!");
                socket.close(); // 释放资源
                break; // 跳出死循环
            }

            DatagramPacket packet = new DatagramPacket(msg.getBytes(), msg.getBytes().length
                    , InetAddress.getLocalHost(),  22222);
            // 3、开始正式发送这个数据包的数据出去了
            socket.send(packet);
        }


    }
}
java

/**
 *  目标:完成TCP通信快速入门-服务端开发:实现1发1收。
 *
 *  1、创建ServerSocket的对象,同时为服务端注册端口。 ServerSocket(port);
 *  2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
 *  3、从socket通信管道中得到一个字节输入流。
 *  4、使用一个数组接收到传过来的数据,然后通过转成字符串
 *  5、关流和释放资源
 *
 */

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象(创建一个接韭菜的人) 注册端口
        DatagramSocket socket = new DatagramSocket(22222);

        // 2、创建一个数据包对象,用于接收数据的(创建一个韭菜盘子)
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        while (true) {
            // 3、开始正式使用数据包来接收客户端发来的数据
            socket.receive(packet);

            // 4、从字节数组中,把接收到的数据直接打印出来
            // 接收多少就倒出多少
            // 获取本次数据包接收了多少数据。
            int len = packet.getLength();

            String rs = new String(buffer, 0 , len);
            System.out.println(packet.getAddress().getHostAddress()+":"+rs);
            System.out.println("--------------------------------------");
        }
    }
}

作业

🚩 1. 参考下列提示,完成UDP入门案例(1发1接)

  • 发送端
java
/**
 * 目标:完成UDP通信快速入门:实现1发1收。
 *
 *
 * 1. 创建客户端对象(发韭菜出去的人) DatagramSocket
 * 2. 创建数据包对象封装要发出去的数据(创建一个韭菜盘子) DatagramPacket
 * 3. 开始正式发送这个数据包的数据出去了 send
 * 4. 释放资源! close
 *
 */
public class Client {
    public static void main(String[] args) throws Exception {

// 书写代码


    }
}
  • 接收端
java
/**
 * 目标:完成UDP通信快速入门-服务端开发
 * 1.  创建一个服务端对象(创建一个接韭菜的人) 注册端口   DatagramSocket
 * 2. 创建一个数据包对象,用于接收数据的(创建一个韭菜盘子) DatagramPacket(数组,数组长度)
 * 3. 开始正式使用数据包来接收客户端发来的数据 receive(包裹)
 * 4. 从包裹中获得数据,并且解析成String(byte数组,0,包裹的长度)
 * 5. 释放资源
 */
public class Server {
    public static void main(String[] args) throws Exception {

// 书写代码


    }
}

四、TCP通信代码(案例) 🚩 ✏️

学习TCP通信的代码完成网络通讯

前置知识:

  1. socket协议是面向连接、通讯可靠的协议
  2. socket协议需要三次握手建立连接,断开需要4次

Socket完成TCP通信的流程:

  1. 当创建Socket对象时,就会在客户端和服务端创建一个数据通信的管道,在客户端和服务端两边都会有一个Socket对象访问这个通信管道
  2. 现在假设客户端要发送一个在一起给服务端,客户端这边先需要通过Socket对象获取到一个字节输出流,通过字节输出流写数据到服务端
  3. 服务端这边通过Socket对象可以获取字节输入流,通过字节输入流就可以读取客户端写过来的数据,并对数据进行处理。
  4. 服务端处理完数据之后,假设需要把没感觉发给客户端端,那么服务端这边再通过Socket获取到一个字节输出流将数据写给客户端
  5. 客户端这边再获取输入流,通过字节输入流来读取服务端写过来的数据。

注意:写代码之前一定要理解上图,不然输入和输出流,容易晕!

1️⃣ 4.1 TCP入门程序 说一句话

java
/**
 *  目标:完成TCP通信快速入门-客户端开发:实现1发1收。
 */public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。(ip和端口是服务端的)
        Socket socket = new Socket("127.0.0.1", 22222);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();

        // 4、开始写数据出去了
        os.write("在一起,好吗?".getBytes());
        os.close();

        socket.close(); // 释放连接资源
    }
}
java
/**
 *  目标:完成TCP通信快速入门-服务端开发:实现1发1收。
 */
public class Server {
    public static void main(String[] args) throws Exception {

        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(22222);

        // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();

//        4创建一个byte数组接收数据
        byte[]  buffer=new byte[1024];

    // 5、使用数据输入流读取客户端发送过来的消息
//        获得数据长度
         int msglen = is.read(buffer);
         String msg= new String(buffer, 0, msglen);

        System.out.println(socket.getRemoteSocketAddress()+":"+msg);

        System.out.println("---------------------------------");

        is.close();
        socket.close();
    }
}

2️⃣ 4.2 TCP入门程序 说多句话

实现客户端可以反复的发消息出去,实现服务端反复接收消息

  1. 反复发,反复接收消息,说明要用到循环----> 死循环
  2. 死循环说明程序无法停止,输入exit出发break 跳出循环,结束程序
java
/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。(ip和端口是服务端的)
        Socket socket = new Socket("127.0.0.1", 22222);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();

        Scanner sc = new Scanner(System.in);

        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                os.close();
                socket.close();
                break;
            }

            // 4、开始写数据出去了
            os.write(msg.getBytes());
            os.flush();
        }
    }
}
java
/**
 *  目标:完成TCP通信快速入门-服务端开发:实现服务端反复发消息
 */
public class Server {
    public static void main(String[] args) throws Exception {
       System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(22222);

        // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();


        while (true) {

            //        4创建一个byte数组接收数据
            byte[]  buffer=new byte[1024];

            try {
                // 5、使用输入流读取客户端发送过来的消息
                int msglen = is.read(buffer);
                String msg= new String(buffer, 0, msglen);

                System.out.println(socket.getRemoteSocketAddress()+":"+msg);

                System.out.println("---------------------------------");
            } catch (Exception e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线了!");
                is.close();
                socket.close();
                break;
            }
        }
    }
}

3️⃣ 4.3 TCP入门程序 线程池优化 🍐 ✏️

为了让服务端能够支持多个客户端通信,就需要用到多线程技术。具体的实现思路如下图所示:每当有一个客户端连接服务端,在服务端这边就为Socket开启一条线程取执行读取数据的操作,来多少个客户端,就有多少条线程。按照这样的设计,服务端就可以支持多个客户端连接了。

java
/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 *  1. 和上述案例一样
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。(ip和端口是服务端的)
        Socket socket = new Socket("127.0.0.1", 22222);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();

        Scanner sc = new Scanner(System.in);

        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                os.close();
                socket.close();
                break;
            }

            // 4、开始写数据出去了
            os.write(msg.getBytes());
            os.flush();
        }
    }
}
java

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 *  1. 在死循环内,使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
 *  2. 使用线程池处理多个人的消息(发送端不变)
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(22222);
         ExecutorService executorService = Executors.newFixedThreadPool(5);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            executorService.submit(new ServerReaderRunnable(socket));
        }
    }
}
java
/**
 * 读取消息的执行程序
 */
public class ServerReaderRunnable implements Runnable {
    private Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }


    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();

            byte[] bytes=new byte[1024];

            while (true) {
                // 使用数据输入流读取客户端发送过来的消息
                try {
                    int len = is.read(bytes); //传过来的数据长度
                    String s = new String(bytes, 0, len);
                    System.out.println(Thread.currentThread().getName()+"线程负责:"+socket.getRemoteSocketAddress()+""+s);//打印一下
                } catch (Exception e) {
                    System.out.println("bbq:");
                    break;
                }


            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

用心去做高质量的内容网站,欢迎 star ⭐ 让更多人发现