【Java EE初阶十四】网络编程TCP/IP协议(一)

news2025/1/18 17:05:58

 1. 网络编程

        通过网络,让两个主机之间能够进行通信->就这样的通信来完成一定的功能,进行网络编程的时候,需要操作系统给咱们提供一组API,通过这些API来完成编程;API可以认为是应用层和传输层之间交互的路径,其中Socket Api(可以认为是插座)通过这个一套Socket Api可以完成不同主机之间,不同系统之间的网络通信;   

        传输层提供的网络协议主要有两个tcp、udp:这两个协议的特性(工作原理差异很大,就会导致使用这两种协议进行网络编程,也会存在一定的差异)

1.1 TCP和UDP的区别

        1、TCP是有连接的,UDP是无连接的;

        这里的连接是抽象的概念,计算机中,这种抽象的连接是很常见的,此处的连接本质上就是建立连接的双方,各自保存对方的信息,即两台计算机建立连接,就是双方彼此保存了对方的关键信息;

        TCP想要通信,就需要先建立连接(即计算机保存对方的信息),做完之后,才能后续通信(如果A和B想要建立连接,但是B拒绝了,通信就无法完成建立)

        UDP想要通信,就直接发送数据报即可,不需要征得对方的同意,UDP自身也不会保存对方的信息(虽然UDP不知道,但是写程序的人得知道,UDP自己不保存对方的信息,但是调用UDP的socket api的时候就要把对方的位置信息等都传输过去)

        2、TCP是可靠传输的,UDP是不可靠传输的;

        网络上进行的通信,A->B发送一个信息,首先送到B的信息不可能做到100%送达,

所谓的可靠传输,就是退而求其次,当A->B发送一个消息,但是消息是不是达到B这一方,A这边自己是能感知到的,所以A在消息发送失败的时候就可以采取一定的措施(尝试重传之类的措施)

        TCP就内置了可靠的传输机制,但是UDP就没有内置可靠传输;

        可靠传输的不足:1、可靠传输所使用的机制更加复杂;2、可靠传输会导致消息的传输效率更加低;

        综上所述:

        TCP协议:发送方知道传输的数据是否成功的传输给接收方

        UDP协议:消息发出去,就不管了,不再考虑消息是否成功发送给接收方;

        3、TCP是面向字节流的,UDP是面向数据报的

        TCP也是和文件操作一样,以字节为单位来进行传输;UDP则是按照数据报(UDP有着自己严格的数据报格式)为单位来进行传输的

        4、TCP和UDP都是全双工的;

        一个信道,允许双向通信,就是全双工;一个信道,只能单向通信,就是半双工;

代码中使用一个socket对象,就可以发送数据也可以接收数据;

2. 关于UDP

2.1 UDP的socket api

        Socket其实是操作系统中的一个概念,本质上是一种特殊的文件;Socket就属于是把“网卡”这个设备给抽象成了文件了,往Socket文件里面写数据,就相当于通过网卡发送数据,从Socket文件里面读数据,就相当于通过网卡接收数据,(如此把网络通信和文件操作给统一了)

        DatagramSocket :

       Java中,就是用DatagramSocket这个类,来表示系统内部的socket文件;

        DatagramPacket: 使用这个类,来表示一个UDP数据报;UDP是面向数据报的,每一次传输都是以UDP数据报为基本单位的;

 2.2 基于UDP实现通信

        写一个简单的UDP的客户端/服务器通信的程序

        要求我们写的程序没有具体的业务逻辑,只是调用单纯的socket api,让客户端给服务器发送一个请求,且该请求是一个从控制台输入的字符串,服务器收到字符串之后,就会把这个字符串原封不动的返回给客户端,客户端在显示出来(即该服务器是回显服务器echo server)

1、读取请求并解析

        服务器和客户端都需要创建socket对象,但是服务器的socket一般要显示的指定一个端口号,而客户端的socket一般不能显式指定(不显式指定,此时系统就会自动分配一个随机的端口),客户端的端口号是不需要确定的,交给系统进行随机分配即可,如果我们手动指定确定的端口,就可以和别人的程序的端口号冲突;

Q:服务器这边手动指定端口号,难道就不会出现端口号冲突吗?为啥客户端在意这个冲突,而服务器不在意这个冲突?

A:首先服务器是在程序员手里面的,一个服务器上有哪些程序,这些程序都使用哪些端口,程序员都是可控的,且程序员在写代码的时候,就可以指定一个空闲的端口,给当前的服务器使用即可;其次客户端的情况就不一样了,因为客户端是在用户的电脑上的,一方面,用户有成千上万,每一个用户电脑上所装的程序都不一样,占用的短端口也不一样,林外一方面,用户这边如果出现了端口号冲突,用户这方面自己本身不知道是什么原因

        所以,客户端的端口号还是交给系统来进行分配,因为系统能保证肯定分配一个空闲的端口号;

        服务器一旦启动,就会立即执行到这里的receive这里的方法,此时客户端的请求可能还没有过来,此时遇到这种情况,receive就会直接阻塞,一直阻塞到客户端把请求发送过来为止;

2、根据请求计算响应(一般的服务器都会经历的过程)

        这个步骤是一个服务器程序最核心的步骤,我们当先的echo server不涉及到这些流程,只要求当请求过来就把请求当作响应,因为UDP是无连接的,所以udp自身不会保存数据要发送的对象信息,就需要每一次发送的时候,重新指定数据要发送到哪儿去;

 
 

3、把响应显示到客户端

4、打印一个日志,将我们进行的数据交互都打印出来

Q:为啥上述所写的代码中,没有写close,因为socket也是文件,不关闭的话就会出现之前我们所讲的文件资源泄露问题嘛?

A:        首先socket是文件描述符表中的一个表项,每一次打开一个文件,就会占用一个位置;文件描述符是在pcb上的(跟随的是进程)

        其次这个socket在整个程序的运行中都是需要使用的(不能提前关闭),当socket不需要使用的时候,意味着程序就要结束了,进程结束,此时随之的文件描述符表就会销毁了(pcb也会销毁),被销毁后资源就会被系统进行自动回收;

        最后,所谓出现泄露,是指代码中频繁打开文件,但是不会关闭,在一个进程的运行过程中,不断的打开文件,并逐渐消耗掉文件描述符表里面的内容,最终该资源就会被消耗殆尽了;但是,如果进程的生命周期很短,打开一下没多久就关闭了,谈不上所谓的资源泄露;

       综上所述,文件资源泄露这样的问题,在服务器这边是比较严重的,在客户端这边其实影响不大;

2.3 代码实现

2.3.1 服务器代码

package network;

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

public class UdpEchoServer {
    // 创建一个 DatagramSocket 对象. 后续操作网卡的基础.
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        // 这么写就是手动指定端口
        socket = new DatagramSocket(port);
        // 这么写就是让系统自动分配端口
        // socket = new DatagramSocket();
    }

    public void start() throws IOException {
        // 通过这个方法来启动服务器.
        System.out.println("服务器启动!");
        // 一个服务器程序中, 经常能看到 while true 这样的代码.
        while (true) {
            // 1. 读取请求并解析.
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            // 当前完成 receive 之后, 数据是以 二进制 的形式存储到 DatagramPacket 中了.
            // 要想能够把这里的数据给显示出来, 还需要把这个二进制数据给转成字符串.
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 2. 根据请求计算响应(一般的服务器都会经历的过程)
            //    由于此处是回显服务器, 请求是啥样, 响应就是啥样.
            String response = process(request);
            // 3. 把响应写回到客户端.
            //    搞一个响应对象, DatagramPacket
            //    往 DatagramPacket 里构造刚才的数据, 再通过 send 返回.
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            // 4. 打印一个日志, 把这次数据交互的详情打印出来.
            System.out.printf("[%s:%d] req=%s, resp=%s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}

2.3.2 客户端代码

package network;

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

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp = "";
    private int serverPort = 0;

    public UdpEchoClient(String ip, int port) throws SocketException {
        // 创建这个对象, 不能手动指定端口.
        socket = new DatagramSocket();
        // 由于 UDP 自身不会持有对端的信息. 就需要在应用程序里, 把对端的情况给记录下来.
        // 这里咱们主要记录对端的 ip 和 端口 .
        serverIp = ip;
        serverPort = port;
    }

    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 1. 从控制台读取数据, 作为请求
            System.out.print("-> ");
            String request = scanner.next();
            // 2. 把请求内容构造成 DatagramPacket 对象, 发给服务器.
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);
            // 3. 尝试读取服务器返回的响应了.
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            // 4. 把响应, 转换成字符串, 并显示出来.
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        //UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
        UdpEchoClient client = new UdpEchoClient("42.192.83.143", 9090);
        client.start();
    }
}

2.3.3 客户端和服务器交互逻辑

         分析如下:

1、服务期先启动,启动之后就开始进行循环,执行到receive这里并阻塞(此时还没有客户端过来)

2、客户端开始启动,也会先进入while循环,执行scanner.next,并且在这里进行阻塞,当用户在控制台输入字符串后,next就会返回,并且构造请求数据病发出来

3、客户端发出数据之后:

        服务器:就会从rfeceive中返回,进一步的执行解析请求为字符串,执行process操作,执行send操作

        客户端:继续往下执行,执行到receive服务器的响应;

4、客户端收到从服务器返回的数据之后,就会从receive中返回,执行这里的打印操作,也就把响应给显示出来了;

5、服务器这边完成过一次循环之后,又执行到receive这里;客户端这边完成一次循环之后,又执行到scanner.next这里,双双进入阻塞;

ps:本篇内容主要讲解关于UDP时间简单通信的过程,如果大家感兴趣的话就请一键三连哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1455561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode刷题笔记题解(C++):120. 三角形最小路径和

思路:动态规划,去生成一个对应的当前节点的最小路径值,对应的关系如下所示 dp[0][0] triangle[0][0] dp[i][0] triangle[i][0]dp[i-1][0] dp[i][i] triangle[i][i]dp[i-1][i] dp[i][j] triangle[i][j]min(dp[i-1][j-1],dp[i-1][j]) …

扫描电子显微镜(SEM)样品制备要求与方法解析

扫描电子显微镜(Scanning Electron Microscope,简称SEM)是一种强大的分析工具,广泛应用于材料科学、生物学、医学、半导体材料和化学化工等领域。SEM能够提供高分辨率的表面形貌图像,因此样品制备成为获取准确、清晰图…

在 MyBatis 中,可以使用相同的 SQL 映射语句进行批量删除和单个删除。

目录 前端代码: 后端代码: controller service层接口 service接口的实现 mapper层接口 xml sql 效果:(点击操作列的删除,可删除一行数据。勾选多个多选框再点击批量删除,可删除多个) …

Postman路径修改

默认安装好Postman之后,默认路径在:C:\Users\用户名\AppData\Local\Postman。 修改路径只需要将整个文件夹拷贝到需要移动的位置即可,然后重新创建一个快捷方式。再删除原来路径的文件夹。

访问学者感谢信|人文社科工作者赴北欧访学

编者按:这位访问学者从委托我们申请到获得邀请函只用了一个月时间。为了表达感激之情,其当时就写了这封感谢信,但依据我们的惯例,一般是待申请者出国,一切安排妥当后再发成功案例。所以时至今日,才将该申请…

2942. 查找包含给定字符的单词【简单】

2942. 查找包含给定字符的单词【简单】 题目描述: 给你一个下标从 0 开始的字符串数组 words 和一个字符 x 。 请你返回一个 下标数组 ,表示下标在数组中对应的单词包含字符 x 。 注意:返回的数组可以是 任意 顺序 示例 1: 输…

海外大带宽服务器连接失败:原因与解决策略

​随着全球互联网的发展,越来越多的企业和个人选择使用海外大带宽服务器来满足数据传输和业务需求。然而,在实际使用中,有时会出现服务器连接失败的问题。本文将为您分析原因并提供相应的解决策略。 一、海外大带宽服务器连接失败的原因 网络…

Firefox火狐浏览器/Google谷歌浏览器安装免费好用的翻译插件,亲测好用舒服了(附上安装包)

文章目录 1. 为什么选择它?2. 下载安装并体验插件2.1 下载安装包2.2 安装插件2.3 翻译体验 1. 为什么选择它? 最近维基百科项目,由于是国外的网站全是英文,我英语又不好,试了好几个英文翻译插件都没法使用&#xff0c…

elementui 中el-date-picker 选择年后输出的是Wed Jan 01 2025 00:00:00 GMT+0800 (中国标准时间)

文章目录 问题分析 问题 在使用 el-date-picker 做只选择年份的控制器时,出现如下问题:el-date-picker选择年后输出的是Wed Jan 01 2025 00:00:00 GMT0800 (中国标准时间),输出了两次如下 分析 在 el-date-picker 中,我们使用…

小程序全局变量管理-mobx引入与使用

背景: 对于全局变量操作,如果不规范使用很容易产生意想不到的问题,使用mobx对全局变量进行管理,能够规范代码操作,使程序变得简单高效。 代码使用: 1)定义全局变量 import { action, observ…

[力扣 Hot100]Day29 删除链表的倒数第 N 个结点

题目描述 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 出处 思路 两个指针间隔n,一趟遍历解决。 代码 class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* phead;ListNode* …

使用Docker Compose搭建Redis哨兵架构

搭建Redis哨兵(sentinel) 之前我们通过深入理解REDIS哨兵原理了解了Redis哨兵(sentinel)的原理,今天我们手动部署一个哨兵架构。要在Docker中搭建Redis哨兵(sentinel)架构,需要Redis的主从实例以及哨兵实例。之前我们已经使用Docker Compose搭建Redis主…

MySQL数据库进阶第二篇(索引,SQL性能分析,使用规则)

文章目录 一、索引概述二、索引结构三、结构 - B-Tree四、结构 - BTree五、结构 - Hash六、索引分类七、索引语法1.案例代码 八、SQL性能分析1.查看SQl执行频率2.慢查询日志3.PROFILES详情4.EXPLAIN执行计划 九、 索引使用规则十、SQL 提示十一、覆盖索引十二、前缀索引十三、单…

Qt的基本操作

文章目录 1. Qt Hello World 程序1.1 通过图形化界面的方式1.2 通过代码的方式实现 2. Qt 的编码问题3. 使用输入框实现hello world4. 使用按钮实现hello world5. Qt 编程注意事项6. 查询文档的方式7. 认识Qt坐标系 1. Qt Hello World 程序 1.1 通过图形化界面的方式 我们先讲…

抖音关键词搜索爬虫,抖音API数据接口,抖音商品详情数据采集

抖音商品API接口抖音关键词搜索抖音直播间小黄车抖店商品数据采集 除了微博,小红书,抖音也是一个巨大的流量池。 除了评论,其实关键词搜索视频是更为常见的一个需求,于是上周末抽空开发了下,完成了 mvp。

鸿蒙新手入门-环境准备问题解析

Node.js版本与API配套关系 由于SDK的部分工具依赖Node.js运行时,推荐使用配套API版本的Node.js,保证工程的兼容性。 匹配关系见下表: API LevelNode.js支持范围API Level≤914.x(≥14.19.1)、16.xAPI Level>914.…

Graph + LLM图数据库技术如何助力行业大语言模型应用落地

随着 AI 人工智能技术的迅猛发展和自然语言处理领域的研究日益深入,如何构建强大的大语言模型对于企业来说愈发重要,尤其是在特定行业领域中。 图数据库作为处理复杂数据结构的有力工具,为企业构建行业大语言模型提供了强大的支持。本文将探…

AI 文生图提示词分类(合集 · 第一季)

一、时间和季节 Time and Season 1、时间描述 Time Description 比如,日出、黄昏、夜晚、清晨 / Sunrise, Sunset, Night, Early Morning 2、季节变化 Seasonal Changes 比如,春天、夏天、秋天、冬天 / Spring, Summer, Autumn, Winter 二、场景描述 Sce…

无人机技术,无人机动力系统知识,电机、电调、桨叶技术详解

无人机动力系统中的电机、电调和桨叶技术都是非常重要的部分,以下是对这些技术的详解: 无人机电机 在无人机动力系统中,电机是将电能转化为机械能的关键部件。其主要作用是产生旋转力矩,驱动螺旋桨的旋转,从而实现无…

【软考中级备考笔记】数据的表示和校验码

2024/2/18 – 数据的表示和校验码 天气:阴雨 春节假期结束后第一个工作日,开始备考中级软件工程师。 希望在今年5月底的软考中取得中级证书 视频地址:https://www.bilibili.com/video/BV1Qc411G7fB 1. 计算机的总体架构 从下图中可以看出&am…