Java-EE 网络编程(1)

news2024/12/24 10:30:13

目录

一、网络初识

二、协议

协议的定义

协议分层

协议分层的好处

TCP/IP五层模型

五层协议

协议之间如何配合工作的

三、网络编程套接字

TCP/UDP特点

UDP回显服务器

UDP的socket api

回显服务器

回显客户端


一、网络初识

先了解一些核心概念:

局域网:把若干个电脑通过路由器连接在一起。

广域网:把若干个局域网进一步连接在一起,构成更复杂的网络体系。

路由器/交换机:组件网络的基础设备。

IP地址:区分主机。

端口号:区分主机上的不同程序。

二、协议

协议的定义

协议可以理解为是一种约定,是通信双方对于通信规则的约定。听起来和标准的意思相近,但不同的是,标准是认可面很广泛的,而协议可以是认可面非常广也可以是认可面不广只有通信双方认可的。

通信双方都认可的规则称之为——协议。

进行网络通信时一定要有通信协议,因为两个用来通信的主机设备往往有不同的硬件不同的操作系统等等,为了保证即使上述不同仍然能进行正确通信。

进行网络通信时,通信协议是非常关键的环节。

协议分层

网络通信是一个非常复杂的事情,这个过程中涉及很多细节问题,如果使用一个协议约定上述所有内容,那这个协议就会变的非常庞大而且复杂,所以就要把它进行拆分,也就是协议分层。这样就可以把复杂的协议分成功能单一的协议,但拆分出的协议因为数量变多了,所以就要对这些协议进行分类,也就是协议分层举个栗子理解一下:

在公司中会进行分组,每个组都有组长,组员把工作汇报给组长,组长再汇报给老板,这样分组的组织结构虽然整体的人很多,但分成组后就不会乱。这样分层的前提是不能越级汇报工作。

协议分层就是上述这样的功能,将协议按照功能分成不同的层级,每个层级有自己的任务,上层协议调用下层协议的功能,下层协议会给上层协议提供服务。

协议分层的好处

好处一:封装的效果,不必知道其他协议的细节,降低学习的成本

比如说两个人在进行打电话时,只需要拨号然后在接通后进行交流即可,通话的两个人不需要理解电话的原理,只要会说汉语就能打电话,

好处二:任意层次的协议都可以灵活替换

意思是电话机和电话机之间有电话机协议,而如果使用无线电的话就会有无线电协议,但使用者不必在意协议的变化,只需要进行通话即可。同理,对于电话机协议,使用者进行汉语交流时使用的汉语协议与使用英语交流时的英语协议没有区别,都是一样进行通信即可。

TCP/IP五层模型

实际开发时采用的网络分层模型。(目前用电脑上网大多数接触到的都是这个网络模型)

五层协议

1、物理层:硬件层面的相关约定,比如网卡,接口之类的

2、数据链路层:关注的是通信时两个相邻节点之间的通信

3、网络层:关注的是通信时对于通信路线的规划,规划出的路径决定了要经过哪些节点,“点到点的传输”

4、传输层:关注的是通信双方的起点与终点,“端到端的传输”

5、应用层:和具体的应用程序相关,传输的数据是做什么的,有什么意义。

协议之间如何配合工作的

首先需要知道:协议的层与层之间,上层协议调用下层协议的功能,下层协议给上层协议提供服务。通过一个栗子来理解层与层之间的配合:

假设A通过vx给B发送一句hello

第一层:A在聊天框输入hello,点击发送,应用程序就要执行上述逻辑,信息是通过网络来执行,首先应用程序将上述要传递的内容组织成应用层数据包,应用程序会有应用层网络协议,协议中约定了数据按照什么样的形式来组织。

网络上传输的数据本质上二进制的字符串,因此要传输的接收人/发送人/发送时间/消息内容等等就要组织到字符串中,组织的时候就要按照一定的格式来组织。

不同的协议组织方式是不同的。

假设应用程序的应用层网络协议约定数据的组织格式是这样:

发送人的vx号/接收人的vx号/发送时间/消息正文,具体的栗子就长这样:

这样组织后的数据包是一个结构化的数据,包含了很多字段/属性,要转化成实际的字符串,将结构化数据转化为二进制字符串的操作——序列化,反过来将二进制字符串转化为结构化数据的操作为反序列化。

第二步:通过第一层的应用层网络协议得到了应用层数据包,现在应用程序就要调用系统api进行传输,应用层接下来把数据交给传输层,怎么交给传输层?

传输层提供了api(socket)交给应用程序去调用,调用这样的api就会把数据从应用层交给传输层,进入传输层意味着进入了操作系统内核。

传输层拿到了应用层数据包后会进行进一步分装,构造成传输层数据包,在传输层中有两个典型的协议:UDP/TCP,假设此处使用UDP进行传输协议。

将应用层数据包进一步封装,加上该数据包上加上了UDP报头,报头里放的是一些UDP相关属性(比如发送人和收件人的端口号),原来的应用层数据包为UDP数据包的载荷

传输层构造好传输层数据包后,会把数据包交给网络层,同样的也是传输层调用网络层提供的api

第三层:数据包到网络层后,网络层典型的协议是IP协议,同样的在数据包上加上IP报头,UDP数据包就作为IP数据包的载荷

IP报头里会包括发件人的IP地址和收件人的IP地址。

同样的,加下来网络层调用数据链路层的api(往往是网卡的驱动程序提供),将上述IP数据包交给数据链路层协议

第四层:数据链路层,典型的协议:以太网

以太网拿到iP数据包后进一步封装

上述数据就进入到了网卡驱动中,准备发送了

第五层:物理层。上述的以太网数据帧本质上还是二进制数据,硬件设备要把上述二进制数据转化为光信号/电信号才会真正的发射。

上述层层的包装数据,不停的加数据报头的过程称为封装。

A这边发件人的五层协议执行完,要传输的数据包发送到了B的交换机,数据经过交换机/路由器的一系列转化后最终到达B,

数据到了B后,B执行上述过程的逆过程,B这边:

第一层:物理层,收到光电信号转化为二进制数据,交给数据链路层

第二层数据链路层,按照以太网协议对数据进行解析,解析报头信息,拿到载荷数据交给下一层网络层

第三层网络层,拿到IP数据包后进一步解析,解析报头中的关键信息取出载荷,将载荷传递给传输层

第四层传输层,拿着UDP数据包进一步解析,解析出报头中的关键信息(交给哪个端口号对应的进程)取出载荷交给应用层

第五层应用层,应用程序按照自己的协议解析应用层数据包,得到结构化数据,进行反序列化,将信息显示到界面。

中间的路由器/交换机也要封装分用,也是上述过程,交换机封装分用到数据链路层即可知道下一步转发(工作在数据链路层),路由器分装分用到网络层即可知道下一步转发(工作在网络层)

三、网络编程套接字

操作系统提供的网络编程api为socket api,操作系统提供的socketapi不止一套,包含:(1)流式套接字->给TCP使用 (2)数据包套接字->给UDP使用 (3)Unix域套接字->不能跨主机使用

TCP/UDP都是传输层协议,给应用层提供服务,由于这两个协议的特点/差异非常大,所以我们就需要两套api分别表示。

TCP/UDP特点

TCP:有连接,可靠传输,面向字节流,全双工

UDP:无连接,不可靠传输,面向数据报,全双工

有连接vs无连接:有连接好比打电话,接通了才能说话,通信双方保存对方的信息;无连接就像发短信,不需要接通可以直接发,通信双方不需要保存对方的信息。

可靠传输vs不可靠传输

首先要区分可靠与安全的区别,安全是指你传输的数据是否容易被黑客截获,一旦被截获是否会造成严重的影响;可靠是指要传输的数据尽可能的传给对方;UDP是不可靠传输,也就是传输的时候不在意对方是否会收到,只管发。

面向字节流vs面向数据报

文件操作就是字节流的,读写操作很灵活,TCP和文件操作具有相同的特点;而面向数据报传输数据时的基本单位只能是一个一个的UDP数据报,一次读写只能读一个完整的数据报。

全双工vs半双工

全双工:一条链路双向通信(TCP/UDP都是,后续代码中创建的socket对象既可以读也可以写)

半双工:一条链路单向通信

UDP回显服务器

UDP的socket api

UDP的socket api重点是两个类:

(1)DatagramSocket系统中本来就有socket的概念,Datagram是对于操作系统的socket概念的封装;系统中的socket可以理解为是“文件”,socket文件可以视为是网卡这种硬件设备的抽象表示形式,针对socket文件的读写操作可以理解为是针对网卡这种硬件设备进行读写。之前学过的普通文件相当于是针对硬盘硬件的设备的抽象,借助文件这个概念(遥控器)来操作硬盘。

计算机中将遥控器这样的概念称为——句柄(handle)

此处Datagram就是用来操作网卡的遥控器,针对这个对象的读写操作就是相当于针对网卡进行读写操作。

(2)DatagramPacket:针对UDP数据报的抽象表达,一个DatagramPacket对象就相当于一个UDP数据报,一次发送/接收就是传输了一个DatagramPacket对象。

回显服务器

Echo称为回显,正常服务器发送请求时会返回不同的响应,此处回显服务器就是请求发了什么响应就是什么,这个过程没有计算也没有逻辑,是最简单的客户端服务器,目的是为了学习socket api的用法

服务器程序一启动就要确认好端口号,客户端是主动的一方,服务器是被动的一方,客户端要知道服务器的端口号才能开始主动

(ip地址:服务器所在的主机ip;port:一个主机上有很多应用程序都要网络通信,就要把哪个程序用哪个端口确认下来,而且还要确保一个端口不能同时被两个或多个进程进程关联,也就是不能重复。实际开发中端口号应该设为多少?在保证端口号合法的前提下,程序员先写个端口号试试,不行再换就可以了)。

也就是要在服务器构造方法中加上参数port,记得抛出异常

public class UdpEchoServer {
    private DatagramSocket socket = null;
    
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    
}

服务器需要不停的处理客户端的请求不停的返回响应,所以在服务器类的start方法中要写一个while循环不停的处理请求返回响应

在处理一个请求时,首先读取指令并解析,需要使用socket接受请求,而接受的请求是一个UDP数据报所以要创建一个datagramPocket对象来引用接收的请求

需要注意参数列表,数据报本质上也是字节,所以创建字节数组来接收,接收的容量假设是数组的最大容量。

为了方便在java代码中处理,可以把上述数据报中的二进制数据转化为字符串,构造成String

DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(requestPacket);
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());

第二步根据请求返回响应,因为是回响服务器,所以在执行请求时直接返回就好,返回的响应使用String进行接收

String reponse = this.possess(request);
 public String possess(String reponse){
        return reponse;
    }

第三步将响应返回客户端接收的时候构造一个空的DatapramPacket即可,而发出响应的时候不光要有带数据的对象,还要有数据要发给谁,这里要发送的对象就是谁给你发出请求就是要返回响应的对象,UDPsocket本身没有记录对方的ip端口号等信息,但是datagramPacket里有记录,我们一开始创建出接收客户端请求的对象里会记录发出请求客户端的信息,用这个对象调用getSocketAddress方法可以得到信息

DatagramPacket reponsePacket = new DatagramPacket(reponse.getBytes(),0,reponse.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(reponsePacket);

这样一个回响服务器就创建好了,在主函数中调用一下即可,完整服务器代码:

public class UdpEchoServer {
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(requestPacket);
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            String reponse = this.possess(request);
            DatagramPacket reponsePacket = new DatagramPacket(reponse.getBytes(),0,reponse.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(reponsePacket);
        }
    }
    public String possess(String reponse){
        return reponse;
    }

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

}

回显客户端

首先在客户端类创建DatagramSocket对象,参数列表要有ip地址和端口号,ip地址如果服务器和客户端在一台主机上此时就固定写为127.0.0.1,端口号是服务器在创建socket对象时指定的端口号。

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverPort;
    private String serverIP;
    public UdpEchoClient(String serverIP,String serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
}

创建socket对象时没有指定端口号,是因为操作系统会自动分配一个空闲的端口号,这个自动的端口号会因为每次重启都会不一样。

为什么服务器要有固定的端口号而客户端要让系统自动分配?

(1)服务器要有固定端口号是因为客户端需要主动给服务器发出请求,如果服务器的端口号不是固定的话就需要每次调整。

(2)如果给客户端固定的端口号可能会跟客户端上的应用程序冲突,一旦端口冲突会导致程序不能进行。

在客户端内部的while循环思路与服务器的别无二致,首先向服务器发出请求,然后接收响应,完整的代码为:

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private int  serverPort;
    private String serverIP;
    public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);
        while(true){
            System.out.println("请输入请求: ");
            String request = scanner.next();
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),0,request.getBytes().length
            , InetAddress.getByName(serverIP),serverPort);
            socket.send(requestPacket);
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127。0.0.1",9090);
        udpEchoClient.start();
    }
}

UDP模型到这就创建完了。

感谢观看

道阻且长,行则将至。

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

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

相关文章

CsvExport:一个.Net高性能、低内存的CSV导出开源库

在我们项目开发中,导出CSV数据功能是非常常见的。 今天推荐一个高性能、低内存的CSV导出开源库。 01 项目简介 CsvExport是一个基于C#非常简单和快速的CSV导出开源库。 该开源库的核心特点: 导出功能兼容性高(自动检测分隔符,…

CSC7720 可正、负应用5V2.1A同步整流

CSC7720是一款同步整流芯片,可以代替肖特基二极管提高反激变换器的效率。CSC7720 支持工作在非连续模式(DCM)的反激变换器中,其内部集成了低 RDS(ON )的 N 沟道功率MOSFET,外围应用简单&#xf…

STM32—SPI通信

1.SPI简介 四根通信线:SCK(Serial Clock)(时钟线)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select) 同步、全双工 支持总线挂载多设备(一主多从) 所有SPI设备的SCK、MOSI、MISO分别连在一起 主机…

【Python学习-UI界面】PyQt5 小部件13-Slider 拖动条

高级布局管理器,允许通过拖动边界来动态改变子小部件的大小。 Splitter控件提供一个手柄,可以拖动以调整控件的大小 样式如下: 常用方法如下: 序号方法描述1addWidget将小部件添加到拆分器的布局中2indexOf返回布局中小部件的索引3insetW…

炖羊肉

炖羊肉必备的两种香辛料,白胡椒、白芷,让你炖出的羊肉软嫩,汤汁鲜美 在烹饪艺术的广阔领域中,炖羊肉作为一道经典佳肴,其风味的层次与深度往往取决于所选香辛料的精妙搭配。其中,白胡椒与白芷作为炖羊肉时…

fun状态上传,并可手动控制

文章目录 引言上传原因:矛盾点:基础工程源码: 代码实操fun状态上传fun状态下发控制 引言 上传原因: 续上一节, 我们把fun像小灯一样, 加入了预警工程, 但是我们fun其实还有其他用处, 比如我们人工手动开风扇, 排风, 所以我们需要把fun的状态上传, 然后也可以通过服务器手动控制…

EmguCV学习笔记 VB.Net 2.3 Mat类

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 EmguCV学习笔记目录 Vb.net EmguCV学习笔记目录 C# 笔者的博客网址:VB.Net-CSDN博客 教程相关说明以及如何获得pdf教…

StackStorm自动化平台

1. StackStorm概述 1.1 StackStorm介绍 StackStorm是一个开源的事件驱动自动化平台,它允许开发者和系统管理员自动化IT和网络操作。StackStorm结合了IT运维、DevOps和网络安全团队的需求,提供了一个集中式的工作流自动化解决方案,包括事件响…

Windows10、ARM开发板、虚拟机Ubuntu可同时上网

一、Windows10端设置 1、打开网络配置 2、打开适配器 3、将window连接的wifi网卡设置为共享模式 4、查看本地连接的ip 到此,window10端设置完毕 二、设置虚拟机端(Ubuntu) 1、打开网络配置 2、打开适配器 3、查看本地连接的网卡名称 4、配置…

超越流水线,企业研发规范落地新思路

作者:子丑 内容大纲: 1、研发规范≠流程约束 2、自动化工具→研发规范载体 3、研发规范在工具上的落地示例 4、研发规范的选型方法与常见实践 研发规范≠流程约束 这个故事特别适合研发规范的场景,我们要避免成为把猫绑在柱子上的信众…

[NOIP2007 普及组] 守望者的逃离 题解

题意 给定 M ( 0 ≤ M ≤ 1 0 3 ) , S ( 1 ≤ S ≤ 1 0 8 ) , T ( 1 ≤ T ≤ 3 1 0 5 ) M(0 \leq M \leq 10^3),S(1 \leq S \leq 10^8),T(1 \leq T \leq 3\times 10^5) M(0≤M≤103),S(1≤S≤108),T(1≤T≤3105),守望者开始在位置 0 0 0,对于每一秒&…

C++调用C#方法(附踩坑点)

C调用C#方法 写在前面效果思路步骤可能的问题 写在后面 写在前面 工作需要用C调用C#写到代码,看来网上写的方法,自己也踩了一些坑,这里总结一下,我只试了CLR的方法。 主要参考了下面几篇博客 C调用C#库简单例程(Lucky…

【BUU】[NewStarCTF 2023 公开赛道]Final -CP读取文件内容

漏洞检测 访问首页发现是ThinkPHP5 的站点 用工具扫描一下,发现存在ThinkPHP5.0.23 RCE漏洞 访问验证,写入shell 成功写入shell. 根目录发现flag,但是权限不足 提权获取flag 准备提权,这里一开始尝试了find,但是find权限不足 尝试采用cp命令,移动到web目录,发现访问还是…

MySQL InnoDB中一个update语句从执行到提交的全过程(3)

接上文MySQL InnoDB中一个update语句从执行到提交的全过程(2)-CSDN博客 目录 六、本地提交 怎样保证binlog和redo log的状态一致呢? MySQL 中的内部 XA 机制 宕机时不同状态的处理 物理落盘策略 七、主备复制 八、返回提交成功 总结一…

Windows 10/11和Linux双系统用户请勿安装最新更新 否则将无法启动

据蓝点网报道,Windows 10/11 最新累积更新存在已知问题,如果你同时安装了 Linux 双系统则会在更新后导致系统无法正常启动。 启动时会出现如下报错: Verifiying shim SBAT data failed: Security Policy Violation.Something has gone serio…

私域场景中的数字化营销秘诀

​在当今的商业世界,私域场景的营销变得愈发重要。今天咱们就来深入探讨一下私域场景中的几个关键营销手段。 一、会员管理与营销 企业一旦拥有完善的会员体系,数字化手段就能大放异彩。它可以助力企业对会员进行精细划分,深度了解会员的消费…

win11笔记本电脑 声音输出设置为扬声器 | 添加蓝牙设备 | win11环境变量设置

🥇 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 🎉 声明: 作为全网 AI 领域 干货最多的博主之一,❤️ 不负光阴不负卿 ❤️ 文章目录 win11笔记本电脑 声音输出设置为扬声器搜索设置,打开设置选择声音选…

AI安全-文生图

1 需求 2 接口 3 示例 大模型图像安全风险探析 - 先知社区 前言 文生图模型是一种新兴的人工智能技术,它通过对大规模文本数据的学习,能够生成逼真的图像。这种模型包含两个主要组件:一个文本编码器和一个图像生成器。 文本编码器接收文本输入,并将其转换为一种数字化的表示…

Idea开发代码注释规范

规范 类注释:每个类都应该有一个简短的注释,描述这个类的用途和主要功能。注释应该放在类的声明之前,使用JavaDoc格式。 /*** 这是一个示例类,用于演示如何编写类注释。*/ public class ExampleClass {// ... }方法注释&#xf…

【Oracle点滴积累】解决PrereqSession failed: RawInventory gets null OracleHomeInfo故障的方法

广告位招租! 知识无价,人有情,无偿分享知识,希望本条信息对你有用! 今天和大家分享在安装Oracle Critical Patch Update (Patch Number:33806138) 遇到PrereqSession failed: RawInventory gets null OracleHomeInfo故…