JAVA高级进阶12网络通信

第十二天、网络通信

网络通信介绍

什么是网络通信?

  • 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)

  • 比如:微信聊天、LOL游戏、京东、淘宝网站的访问

基本的通信架构

  • 基本的通信架构有2种形式:CS架构( Client客户端/Server服务端 ) 、 BS架构(Browser浏览器/Server服务端)

网络通信三要素

网络通信的关键三要素

IP地址

IP地址

  • IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志

  • IP地址有两种形式:IPv4、IPv6

IPv4

IPv6地址

  • IPv6:共128位,号称可以为地球每一粒沙子编号。

  • IPv6分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开。

公网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地址:检查网络是否连通

IP域名

  • (或更常被称为“域名”)是用于标识和定位互联网上的资源,如网站或服务器的地址。虽然IP地址(如192.168.1.1104.236.166.99)是计算机在网络中相互通信的基础,但域名提供了一种更易于记忆和使用的替代方式。

以下是关于IP域名的一些关键点:

  • *易于记忆**:与IP地址相比,域名更容易记忆。例如,www.baidu.com104.236.166.99更容易记住。

  • 层次结构:域名具有层次结构,通常包括顶级域名(TLD,如.com.net.org等)、二级域名(如example在上述示例中)、三级域名(如果存在)等。

  • DNS(域名系统):DNS是一个分布式数据库,用于将域名转换为相应的IP地址。当你尝试访问一个网站时,你的浏览器首先会查询DNS来找出该网站IP地址,然后使用该地址来连接网站。

  • 注册和维护:域名通常由域名注册机构(如GoDaddy、Namecheap等)出售和管理。你需要选择一个合适的域名注册机构来购买和管理你的域名。

  • SSL/TLS证书:许多现代网站都使用SSL/TLS证书来加密其与用户的通信,并提供安全的HTTPS连接。这通常要求你为域名购买一个SSL/TLS证书。

InetAddress

  • 代表IP地址

常用方法

  •  public static void main(String[] args) throws Exception {
            //1.获取本机地址
            InetAddress byAddress = InetAddress.getLocalHost();
            System.out.println(byAddress);
            //2.获取百度的IP
            InetAddress byName = InetAddress.getByName("www.baidu.com");
            System.out.println(byName);
            //3.获取百度主机名
            String hostName = byName.getHostName();
            System.out.println(hostName);
            //4.获取百度ip
            String hostAddress = byName.getHostAddress();
            System.out.println(hostAddress);
            //5.查看在10毫秒是否能够和百度联通
            boolean reachable = byName.isReachable(100);
            System.out.println(reachable);
        }

端口号

端口

  • 标记正在计算机设备上运行的应用程序的一个数字,范围是 0~65535。

分类

  • 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)

  • 注册端口:1024~49151,分配给用户进程或某些应用程序

  • 动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配

  • 注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。

协议

通信协议

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

开放式网络互联标准:OSI网络参考模型

  • OSI网络参考模型:全球网络互联标准

  • TCP/IP网络模型:事实上的国际标准

传输层的2个通信协议

  • UDP(User Datagram Protocol):用户数据报协议

  • TCP(Transmission Control Protocol) :传输控制协议

UDP协议

  • 特点:无连接、不可靠通信

  • 不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等

  • 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的

TCP协议

  • 特点:面向连接、可靠通信

  • TCP的最终目的:要保证在不可靠的信道上实现可靠的传输

  • TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接

TCP协议:三次握手建立可靠连接

  • 可靠连接:确定通信双方,收发消息都是正常无问题的!

TCP协议:四次挥手断开连接

  • 目的:确保双方数据的收发都已经完成!

UDP通信

快速入门

UDP通信

  • 特点:无连接、不可靠通信

  • 不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了

  • Java提供了一个java.net.DatagramSocket类来实现UDP通信

DatagramSocket: 用于创建客户端、服务端

DatagramPacket:创建数据包

初步了解客户端与服务端的联系

  • 客户端:
            // 创建一个UDP套接字,用于发送数据包
            DatagramSocket socket = new DatagramSocket();
            
            // 定义要发送的消息
            String msg = "嗨嗨嗨";
            
            // 将消息转换为字节数组,这是UDP数据包传输的数据形式
            byte[] bytes = msg.getBytes();
            
            // 获取本地主机的IP地址,用于构建数据包的目标地址
            InetAddress host = InetAddress.getLocalHost();
            
            // 创建一个DatagramPacket,包含要发送的数据、数据长度、目标地址和端口
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, host, 8943);//8943为自定义4位数端口
            
            // 发送数据包
            socket.send(packet);
            
            // 关闭套接字,因为不再需要它
            socket.close();
        }
    ​
        
    服务端:
        public static void main(String[] args) throws Exception {
            // 创建一个UDP套接字并绑定到8943端口,用于接收数据。
            DatagramSocket socket = new DatagramSocket(8943);//8943为自定义4位数端口
            
            // 创建一个足够大的字节数组,用于存储接收的数据。
            byte[] bytes = new byte[64 * 1024];
            
            // 根据字节数组长度创建一个数据报包,用于接收数据。
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
            
            // 接收数据,此操作会阻塞直到有数据到达。
            socket.receive(packet);
            
            // 从接收到的字节数组中构造一个字符串,只包含实际接收到的数据部分。
            String s = new String(bytes, 0, packet.getLength());
            
            // 打印发送数据的IP地址和接收到的数据内容。
            System.out.println(packet.getAddress().getHostAddress()+":"+s);
            
            // 关闭UDP套接字。
            socket.close();
        }

多发多收

多发多收

  • 其定义就是客户端可以反复发送数据,服务端可以反复接收数据

UDP多发多收代码运用

  • 客户端:
        public static void main(String[] args) throws Exception {
            // 创建一个DatagramSocket实例,用于发送和接收数据报。
            DatagramSocket socket = new DatagramSocket();
            // 使用Scanner从控制台读取输入。
            Scanner scanner = new Scanner(System.in);
            while (true) {
                // 提示用户输入数据。
                System.out.println("请输入数据");
                // 读取用户输入的字符串。
                String msg = scanner.next();
                // 如果输入为"exit",则退出循环。
                if ("exit".equals(msg)){
                    break;
                }
                // 将字符串转换为字节数组。
                byte[] bytes = msg.getBytes();
                // 获取本地主机的IP地址。
                InetAddress host = InetAddress.getLocalHost();
                // 创建一个DatagramPacket,包含要发送的数据、数据长度、目标主机和端口。
                DatagramPacket packet = new DatagramPacket(bytes, bytes.length, host, 8943);//8943为自定义4位数端口
                // 发送数据报。
                socket.send(packet);
            }
            // 关闭DatagramSocket。
            socket.close();
        }
    服务端:
        public static void main(String[] args) throws Exception {
            // 在8943端口打开一个UDP套接字。
            DatagramSocket socket = new DatagramSocket(8943);//8943为自定义4位数端口
            // 创建一个足够大的字节数组,用于接收来自客户端的数据包。
            byte[] bytes = new byte[64 * 1024];
            // 无限循环,以持续监听和接收数据包。
            while (true) {
                // 创建一个数据包对象,用于接收数据。
                DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
                // 接收来自客户端的数据包。
                socket.receive(packet);
                // 将接收到的字节数据转换为字符串。
                String s = new String(bytes, 0, packet.getLength());
                // 打印发送数据的客户端地址、端口和数据内容。
                System.out.println(packet.getAddress().getHostAddress() + ":" + packet.getPort() + ":" + s);
         
                // 注释掉的socket.close()如果被取消注释,将导致服务器在接收一个数据包后关闭,因此不适合此处。
                //socket.close();
            }
        }

TCP通信

快速入门

TCP通信

  • 特点:面向连接、可靠通

  • 通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端

  • Java提供了一个java.net.Socket类来实现TCP通信

TCP通信之-客户端开发

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

TCP通信-服务端程序的开发

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

ServerSocket

TCP通信客户端与服务端的联系

  • 客户端:
        public static void main(String[] args) throws Exception {
            //创建客户端的Socket对象,请求与服务端的链接
            Socket socket = new Socket("127.0.0.1",8848);//127.为本机ip,8848为自定义4位数端口
            //使用socket对象调用getOutputStream()方法得到字节输出流
            OutputStream os = socket.getOutputStream();
            //使用字节输出流完成数据的发送
            ObjectOutputStream oos = new ObjectOutputStream(os);
            oos.writeUTF("哈哈,在一起?");
            //释放资源:关闭socke管道
            oos.close();
            socket.close();
        }
    服务端:
        public static void main(String[] args) throws Exception {
            //创建ServerSocket对象,注册服务端端口
            ServerSocket serverSocket = new ServerSocket(8848);//8848为自定义4位数端口
            //调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
            Socket socket = serverSocket.accept();
            //通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
            InputStream inputStream = socket.getInputStream();
            ObjectInputStream ois = new ObjectInputStream(inputStream);
            //获取客户端访问者的ip地址对象
            InetAddress inetAddress = socket.getInetAddress();
            //获取端口
            int port = socket.getPort();
            String msg = ois.readUTF();
            System.out.println(inetAddress.getHostAddress() + ":" + port + "--->" + msg);
            //释放资源:关闭管道
            ois.close();
            socket.close();
        }

多发多收

多发多收

  • 其定义就是客户端可以反复发送数据,服务端可以反复接收数据

TCP多发多收代码运用

  • 客户端:
        public static void main(String[] args) throws Exception {
            //创建客户端的Socket对象,请求与服务端的链接
            Socket socket = new Socket("127.0.0.1", 8858);//127.为本机ip,8858为自定义4位数端口
            //使用socket对象调用getOutputStream()方法得到字节输出流
            OutputStream os = socket.getOutputStream();
            //使用字节输出流完成数据的发送
            ObjectOutputStream oos = new ObjectOutputStream(os);
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println("请输入内容:");
                String msg = scanner.next();
                if ("exit".equals(msg)) {
                    break;
                }
                oos.writeUTF(msg);
                oos.flush();
            }
            //释放资源:关闭socke管道
            oos.close();
            socket.close();
        }
    服务端:
        public static void main(String[] args) throws Exception {
            // 监听客户端连接的端口
            // 创建ServerSocket对象,注册服务端端口
            ServerSocket serverSocket = new ServerSocket(8858);//8858为自定义4位数端口
            
            // 等待并接受客户端的连接
            // 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
            Socket socket = serverSocket.accept();
            
            // 获取客户端发送的数据流
            // 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
            InputStream inputStream = socket.getInputStream();
            
            // 将输入流封装为对象输入流,以便读取序列化对象
            ObjectInputStream ois = new ObjectInputStream(inputStream);
            
            // 获取客户端的IP地址和端口
            // 获取客户端访问者的ip地址对象
            InetAddress inetAddress = socket.getInetAddress();
            // 获取端口
            int port = socket.getPort();
            
            // 无限循环,持续接收和处理客户端发送的消息
            while (true) {
                // 读取客户端发送的字符串消息
                String msg = ois.readUTF();
                // 打印消息,包括发送者的IP地址和端口
                System.out.println(inetAddress.getHostAddress() + ":" + port + "--->" + msg);
                // 注释掉的代码块原意是处理完消息后关闭流和socket,但在此场景下应保持连接以持续接收消息         
    //            ois.close();
    //            socket.close();
            }
        }

多客户端通信

目前我们开发的服务端程序,是否可以支持与多个客户端同时通信 ?

  • 不可以的

  • 因为服务端现在只有一个主线程,只能处理一个客户端的消息

多客户通信代码运用

  • 客户端:
        public static void main(String[] args) throws Exception {
            // 创建一个Socket对象,连接到本地服务器
            // 创建客户端的Socket对象,请求与服务端的链接
            Socket socket = new Socket("127.0.0.1", 8858);//127.为本机ip,8858为自定义4位数端口
            // 获取Socket的输出流,用于向服务器发送数据
            // 使用socket对象调用getOutputStream()方法得到字节输出流
            OutputStream os = socket.getOutputStream();
            // 将OutputStream封装成ObjectOutputStream,用于序列化对象发送给服务器
            // 使用字节输出流完成数据的发送
            ObjectOutputStream oos = new ObjectOutputStream(os);
            // 创建Scanner对象,从控制台读取用户输入
            Scanner scanner = new Scanner(System.in);
            // 不断循环,等待用户输入
            while (true) {
                // 提示用户输入消息
                System.out.println("请输入内容:");
                // 读取用户输入的消息
                String msg = scanner.next();
                // 如果用户输入了"exit",则退出循环
                if ("exit".equals(msg)) {
                    break;
                }
                // 将用户输入的消息写入到输出流中,发送给服务器
                oos.writeUTF(msg);
                // 刷新输出流,确保消息立即发送
                oos.flush();
            }
            // 关闭ObjectOutputStream和Socket,释放资源
            // 释放资源:关闭socke管道
            oos.close();
            socket.close();
        }
    服务端:
    public static void main(String[] args) throws Exception {
        // 创建ServerSocket对象并绑定到指定端口
        // 创建ServerSocket对象,注册服务端端口
        ServerSocket serverSocket = new ServerSocket(8858);
        // 不断等待并接受客户端的连接
        // 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
        while (true) {
            // 接受客户端连接,返回Socket对象
            Socket socket = serverSocket.accept();
            // 创建SocketRunnable对象并传入Socket,用于处理客户端连接
            SocketRunnable socketRunnable = new SocketRunnable(socket);
            // 创建新线程并启动,处理客户端通信
            new Thread(socketRunnable).start();
        }
    }
    ​
    /**
     * SocketRunnable类实现了Runnable接口,用于处理客户端的Socket连接。
     * 在run方法中,读取客户端发送的数据并打印。
     */
    class SocketRunnable implements Runnable {
        private Socket socket;
    ​
        /**
         * 构造函数,初始化SocketRunnable对象。
         * 
         * @param socket 客户端的Socket连接
         */
        public SocketRunnable(Socket socket) {
            this.socket = socket;
        }
    ​
        /**
         * run方法是线程执行的核心方法。
         * 在此方法中,通过输入流读取客户端发送的数据,并打印出来。
         */
        @Override
        public void run() {
            InputStream inputStream = null;
            ObjectInputStream ois = null;
            try {
                // 获取Socket的输入流
                inputStream = socket.getInputStream();
                // 将输入流封装为ObjectInputStream,用于读取序列化对象
                ois = new ObjectInputStream(inputStream);
                // 获取客户端的IP地址和端口
                InetAddress inetAddress = socket.getInetAddress();
                int port = socket.getPort();
                // 不断读取客户端发送的数据
                while (true) {
                    // 读取客户端发送的字符串
                    String msg = ois.readUTF();
                    // 打印客户端信息和发送的消息
                    System.out.println(inetAddress.getHostAddress() + ":" + port + "-->" + msg);
                }
            } catch (IOException e) {
                // 如果发生IO异常,打印提示信息
                System.out.println("叉出去!!!");
            } finally {
                // 关闭资源,确保输入流和Socket被正确关闭
                try {
                    if (ois != null) ois.close();
                    if (inputStream != null) inputStream.close();
                    if (socket != null) socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

bs程序开发

BS架构的基本原理

  • 服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据

  • HTTP协议规定:响应给浏览器的数据格式必须满足如下格式

BS架构代码基础应用

  •  public static void main(String[] args) throws Exception {
            // 创建ServerSocket对象,指定监听的端口号
            // 创建ServerSocket对象,注册服务端端口
            ServerSocket serverSocket = new ServerSocket(8668);
            // 不断循环,等待并处理客户端的连接请求
            // 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
            while (true) {
                // 接受客户端的连接,返回Socket对象
                Socket socket = serverSocket.accept();
                // 创建SocketRunnable对象,用于处理客户端请求
                SocketRunnable socketRunnable = new SocketRunnable(socket);
                // 创建线程并启动,处理客户端请求
                new Thread(socketRunnable).start();
            }
        }
    }
    ​
    ​
    /**
     * 实现Runnable接口的类,用于处理客户端的Socket连接。
     */
    class SocketRunnable implements Runnable {
        private Socket socket;
    ​
        /**
         * 构造函数,初始化Socket对象。
         * @param socket 客户端的Socket连接对象
         */
        public SocketRunnable(Socket socket) {
            this.socket = socket;
        }
    ​
        /**
         * Runnable接口的run方法,用于具体处理客户端请求。
         */
        @Override
        public void run() {
            PrintWriter writer = null;
            try {
                // 输出客户端连接的端口号
                System.out.println("浏览器访问了服务器:" + socket.getPort());
                // 创建PrintWriter对象,用于向客户端发送数据
                writer = new PrintWriter(socket.getOutputStream());
                // 发送HTTP响应头,模拟简单的HTTP服务器
                writer.println("HTTP/1.1   200   OK");
                writer.println("Content-Type : text/html;charset=UTF-8");
                writer.println();
                writer.println("<div style=color:green>白龙马</div>");
                // 确保数据被发送
                writer.flush();
            } catch (Exception e) {
                // 异常处理,此处省略
            } finally {
                try {
                    // 关闭PrintWriter和Socket,释放资源
                    if (writer != null) {
                        writer.close();
                    }
                    socket.close();
                } catch (Exception e) {
                    // 异常处理,此处省略
                }
            }
        }
    }
       
  • 42
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值