• 26-网络通信


    网络通信

    什么是网络编程
    可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。
    java.net.包下提供了网络编程的解决方案!
    基本的通信架构有2种形式:CS架构( Client客户端/Server服务端 ) 、 BS架构(Browser浏览器/Server服务端)。

    image-20230811093617412

    image-20230811093708272

    我们java的重点是开发BS架构
    无论是CS架构,还是BS架构的软件都必须依赖网络编程

    网络通信的三要素

    1. IP地址 设备在网络中的地址,是唯一的标识。
    2. 端口 应用程序在设备中唯一的标识
    3. 协议 连接和数据在网络中传输的规则。

    IP地址

    IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志。
    IP地址有两种形式:IPv4、IPv6

    IPV4

    image-20230811094428007

    IPV6

    32为的IP地址根本不够为我们如此多的IP的地址进行编号,所以演变出了IPV6地址
    IPv6:共128位,号称可以为地球每一粒沙子编号。
    IPv6分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开。
    image-20230811095044814

    IP域名

    域名就是最后也要转化为IP的
    image-20230811095232838

    特殊IP

    公网IP,内网IP
    公网IP:是可以连接互联网的IP地址;内网IP:也叫局域网IP,只能组织机构内部使用。
    192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用。
    特殊IP地址:非常重要
    127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机。
    IP常用命令:
    ipconfig:查看本机IP地址。
    ping IP地址:检查网络是否连通。

    InerAddress命令
    名称说明
    public static InetAddress getLocalHost()获取本机IP,会以一个inetAddress的对象返回
    public static InetAddress getByName(String host)根据ip地址或者域名,返回一个inetAdress对象
    public String getHostName()获取该ip地址对象对应的主机名。
    public String getHostAddress()获取该ip地址对象中的ip地址信息。
    public boolean isReachable(int timeout)在指定毫秒内,判断主机与该ip对应的主机是否能连通
    举例
        public static void main(String[] args) {
            try {
                InetAddress ia = InetAddress.getLocalHost();  //获取本机IP
                InetAddress bd = InetAddress.getByName("www.baidu.com");  //获取这个网址的ip
                System.out.println(ia);  //输出这个本机ip的主机
                System.out.println(ia.getHostName());  //输出这个本机ip的名字
                System.out.println(ia.getHostAddress()); //输出这个本机ip的地址
                System.out.println(bd.isReachable(1000)); //判断baidu的主机和本机是否能够在1000ms内进行连接成功
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    输出结果
    DESKTOP-ROKA7MJ/111.111.111.1 (这个本机ip我已经修改了,防止自己的ip泄露,这里随便编了一个,为了能够更好的展示结果)
    DESKTOP-ROKA7MJ
    111.111.111.1
    www.baidu.com/180.101.50.188
    true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    端口号

    端口
    标记正在计算机设备上运行的应用程序的,被规定为一个 16 位的二进制,范围是 0~65535。
    分类
    周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
    注册端口:1024~49151,分配给用户进程或某些应用程序。
    动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。
    注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。

    协议

    网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议
    开放式网络互联标准:OSI网络参考模型
    OSI网络参考模型:全球网络互联标准。
    TCP/IP 网络模型:事实上的国际标准。(大四的课程, 进行网络通信)

    OSI网络参考模型TCP/IP网络模型各层对应面向操作
    应用层应用层HTTP、FTP、SMTP…应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发
    表示层应用层HTTP、FTP、SMTP…应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发
    会话层应用层HTTP、FTP、SMTP…应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发
    传输层传输层UDP、TCP…选择使用的TCP , UDP协议
    网络层网络层IP…封装源和目标IP
    数据链路层数据链路层+ 物理比特流…物理设备中传输
    物理层数据链路层+ 物理比特流…比特流…

    UDP(User Datagram Protocol):用户数据报协议; TCP(Transmission Control Protocol) :传输控制协议。

    UDP协议

    特点:无连接、不可靠通信。
    不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。
    发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的 。
    语音童话,还有视频直播我们可以用这个UDP通信,因为数据的丢失对这个影响不大

    TCP协议

    特点:面向连接、可靠通信。
    TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
    TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。

    image-20230811105551312

    image-20230811105831833

    image-20230811105854535

    UDP通信的快速入门

    常用方法

    UDP通信
    特点:无连接、不可靠通信。
    不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了。
    Java提供了一个java.net.DatagramSocket类来实现UDP通信。
    DatagramSocket: 用于创建客户端、服务端

    构造器说明
    public DatagramSocket()创建客户端的Socket对象, 系统会随机分配一个端口号。
    public DatagramSocket(int port)创建服务端的Socket对象, 并指定端口号
    方法说明
    public void send(DatagramPacketdp)发送数据包
    public void receive(DatagramPacket p)使用数据包接收数据

    DatagramPacket:创建数据包

    构造器说明
    public DatagramPacket(byte[] buf, int length, InetAddress address, int port)创建发出去的数据包对象
    public DatagramPacket(byte[] buf, int length)创建用来接收数据的数据包
    方法说明
    public int getLength()获取数据包,实际接收到的字节个数

    一发一收

    创建一个客户端对象

        public static void main(String[] args) throws Exception {
            //1. 创建客户端对象,发数据的人
            DatagramSocket ds = new DatagramSocket();
    
            //2 创建数据包对象封装要发出去的数据(创建一个韭菜盒子)  
            //参数一:封装要发出去的数据
            //参数二,发出去的数据大小,字节个数
            //参数三:服务端的IP地址
            // 参数四: 服务端程序的端口
            byte[] bytes = "我爱你,你爱我,蜜雪冰城甜蜜蜜".getBytes();
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),6666);
            ds.send(dp);
            System.out.println("客户端数据发送完毕");
            ds.close(); //释放资源
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建一个服务端对象,注册端口

        public static void main(String[] args) throws Exception {
            //创建一个服务端对象,注册端口
            System.out.println("------------");
            DatagramSocket ds = new DatagramSocket(6666);
            //创建一个数据包对象,用于接受数据
            byte [] bytes = new byte[1024*64]; //一个数据包最大不能超过64kb,所以我们接收的时候最好用最大的,防止接收不完
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
            ds.receive(dp);
            //读取多少就倒出多少
            String s = new String(bytes,0,dp.getLength());
            System.out.println(s);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    多发多收

    客户端.可以多次运行

            Scanner input  = new Scanner(System.in);
            while (true) {
                System.out.println("请说");
                String msg = input.nextLine();
                //退出这个循环
                if(msg.equals("end")){
                    System.out.println("退出成功,欢迎下次使用");
                    ds.close(); //释放资源
                    break;
                }
                byte[] bytes = msg.getBytes();
                DatagramPacket dp = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),6666);
                ds.send(dp);
                System.out.println("客户端数据发送完毕");
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    服务端没有必要关闭

            DatagramSocket ds = new DatagramSocket(6666);
            byte [] bytes = new byte[1024*64]; //一个数据包最大不能超过64kb,所以我们接收的时候最好用最大的,防止接收不完
            while (true) {
                System.out.println("------------");
                DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
                ds.receive(dp);
                //读取多少就倒出多少
                String s = new String(bytes,0,dp.getLength());
                System.out.println(s);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    TCP通信

    一定要注意前后服务端和客户端要一一对应,否则就容易出错

    特点:面向连接、可靠通信。
    通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。
    Java提供了一个java.net.Socket类来实现TCP通信。

    image-20230811120040094

    TCP通信之-客户端开发

    客户端程序就是通过java.net包下的Socket类来实现的。

    常用方法

    构造器说明
    public Socket(String host , int port)根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
    方法说明
    public OutputStream getOutputStream()获得字节输出流对象
    public InputStream getInputStream()获得字节输入流对象

    TCP通信之-服务端开发

    服务端是通过java.net包下的ServerSocket类来实现的。

    构造器说明
    public ServerSocket(int port)为服务端程序注册端口
    方法说明
    public Socket accept()阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象。

    举例

    单发单收
    服务端, 从外读input

     //服务端的开发
            ServerSocket serverSocket = new ServerSocket(8888);
            //使用servesocket对象,调用accept[t方法,等待客户端的连接请求
            Socket accept = serverSocket.accept();
            //从socket通信管道中获取一个字节输入流
            InputStream is = accept.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            //读取数据
            System.out.println(dis.readUTF());
            dis.close();
            serverSocket.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    客户端,往外写 output

    //创建socker对象,并同时请求与服务器程序的连接
            Socket socket = new Socket(InetAddress.getLocalHost(),8888);
    
            //从cocket通信管道中得到一个字节输出流,用来发数据给服务端程序
            OutputStream os = socket.getOutputStream();
            //BufferedOutputStream bos = new BufferedOutputStream(os);
            //把低级的字节输出流,包装成数据输出流
            DataOutputStream dos = new DataOutputStream(os);
    
            dos.writeUTF("我喜欢你");
            dos.close();
            socket.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    多发多收
    服务端, 从外读 input

    //服务端的开发
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket accept = serverSocket.accept();
            //使用servesocket对象,调用accept[t方法,等待客户端的连接请求
            //从socket通信管道中的大奥一个字节输入流
            InputStream is = accept.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true) {
                System.out.println("------------");
                //读取数据
                System.out.println(dis.readUTF());
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    客户端, 往外写output

            //创建socker对象,并同时请求与服务器程序的连接
            Socket socket = new Socket(InetAddress.getLocalHost(),8888);
            Scanner input = new Scanner(System.in);
            OutputStream os = socket.getOutputStream();
            //BufferedOutputStream bos = new BufferedOutputStream(os);
            //把低级的字节输出流,包装成数据输出流
            DataOutputStream dos = new DataOutputStream(os);
            while (true) {
                //从cocket通信管道中得到一个字节输出流,用来发数据给服务端程序
                String msg = input.nextLine();
                if(msg.equals("end")){
                    System.out.println("退出客户端");
                    dos.close();
                    socket.close();
                    break;
                }
                dos.writeUTF(msg);
                dos.flush();
                System.out.println("写入成功");
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    如果我们这个客户端挂了,那么服务端也会挂

    TCP通信,支持与多个客户端同时通信

    目前我们开发的服务端程序,是否可以支持与多个客户端同时通信 ?
    不可以的。
    因为服务端现在只有一个主线程,只能处理一个客户端的消息。
    UDP不需要进行连接,只需要进行发送数据包就好了
    image-20230811124405412
    所以我们要又多线程来实现多个客户端进行通信
    服务端,创建多线程

            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("服务器启动");
            while (true) {
                Socket accept = serverSocket.accept();
                System.out.println("有人上线了,地址是"+accept.getRemoteSocketAddress() );
                new TCPThread(accept).start();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    线程对象

    public class TCPThread extends Thread{
        private Socket socket;
        public TCPThread(Socket socket){
            this.socket=socket;
        }
        @Override
        public void run(){
            try {
                InputStream is = socket.getInputStream();
                DataInputStream dis = new DataInputStream(is);
                while (true) {
                    //读取数据,防止服务端关闭这里崩溃
                    try {
                        System.out.println(dis.readUTF());
                        System.out.println(socket.getRemoteSocketAddress()+"输入了数据:");
                    } catch (Exception e) {
                        System.out.println(socket.getRemoteSocketAddress()+"关闭");
                        socket.close();
                        dis.close();
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    客户端

            //创建socker对象,并同时请求与服务器程序的连接
            Socket socket = new Socket(InetAddress.getLocalHost(),8888);
            Scanner input = new Scanner(System.in);
            OutputStream os = socket.getOutputStream();
            //BufferedOutputStream bos = new BufferedOutputStream(os);
            //把低级的字节输出流,包装成数据输出流
            DataOutputStream dos = new DataOutputStream(os);
            while (true) {
                //从cocket通信管道中得到一个字节输出流,用来发数据给服务端程序
                String msg = input.nextLine();
                if(msg.equals("end")){
                    System.out.println("退出客户端");
                    dos.close();
                    socket.close();
                    break;
                }
                dos.writeUTF(msg);
                dos.flush();
                System.out.println("写入成功");
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    gpnmb+ gpnmb-AT2 cell空转映射 上皮细胞的空转映射
    什么是用户体验测试? 为什么很重要?
    使用 AutoGPTQ 和 transformers 让大语言模型更轻量化
    【JavaScript原型链prototype详解】
    第三篇,芯片启动和时钟系统
    每日三题 9.28
    同样做软件测试,为什么有人月入3k-5k,有人能拿到17-20k?
    CentOS 软件包 rpm 管理学习笔记
    SAP UI5 的规则构建器控件介绍
    通信原理学习笔记6-2:数字解调——抽样和符号同步
  • 原文地址:https://blog.csdn.net/everything_study/article/details/133501119