socket网络字节序以及大端序小端序

news2024/11/24 14:09:37

不同CPU中,4字节整数1在内存空间的存储方式是不同的。4字节整数1可用2进制表示如下:

00000000 00000000 00000000 00000001

有些CPU以上面的顺序存储到内存,另外一些CPU则以倒序存储,如下所示:

00000001 00000000 00000000 00000000

若不考虑这些就收发数据会发生问题,因为保存顺序的不同意味着对接收数据的解析顺序也不同。

大端序和小端序

CPU向内存保存数据的方式有两种:

  • 大端序(Big Endian):高位字节存放到低位地址(高位字节在前)。
  • 小端序(Little Endian):高位字节存放到高位地址(低位字节在前)。


仅凭描述很难解释清楚,不妨来看一个实例。假设在 0x20 号开始的地址中保存4字节 int 型数据 0x12345678,大端序CPU保存方式如下图所示:


图1:整数 0x12345678 的大端序字节表示


对于大端序,最高位字节 0x12 存放到低位地址,最低位字节 0x78 存放到高位地址。小端序的保存方式如下图所示:


图2:整数 0x12345678 的小端序字节表示


不同CPU保存和解析数据的方式不同(主流的Intel系列CPU为小端序),小端序系统和大端序系统通信时会发生数据解析错误。因此在发送数据前,要将数据转换为统一的格式——网络字节序(Network Byte Order)。网络字节序统一为大端序。

主机A先把数据转换成大端序再进行网络传输,主机B收到数据后先转换为自己的格式再解析。

网络字节序转换函数

在《使用bind()和connect()函数》一节中讲解了 sockaddr_in 结构体,其中就用到了网络字节序转换函数,如下所示:



  1. //创建sockaddr_in结构体变量
  2. struct sockaddr_in serv_addr;
  3. memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
  4. serv_addr.sin_family = AF_INET; //使用IPv4地址
  5. serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
  6. serv_addr.sin_port = htons(1234); //端口号

htons() 用来将当前主机字节序转换为网络字节序,其中h代表主机(host)字节序,n代表网络(network)字节序,s代表short,htons 是 h、to、n、s 的组合,可以理解为”将short型数据从当前主机字节序转换为网络字节序“。

常见的网络字节转换函数有:

  • htons():host to network short,将short类型数据从主机字节序转换为网络字节序。
  • ntohs():network to host short,将short类型数据从网络字节序转换为主机字节序。
  • htonl():host to network long,将long类型数据从主机字节序转换为网络字节序。
  • ntohl():network to host long,将long类型数据从网络字节序转换为主机字节序。


通常,以s为后缀的函数中,s代表2个字节short,因此用于端口号转换;以l为后缀的函数中,l代表4个字节的long,因此用于IP地址转换。

举例说明上述函数的调用过程:



  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <WinSock2.h>
  4. #pragma comment(lib, "ws2_32.lib")
  5. int main(){
  6. unsigned short host_port = 0x1234, net_port;
  7. unsigned long host_addr = 0x12345678, net_addr;
  8. net_port = htons(host_port);
  9. net_addr = htonl(host_addr);
  10. printf("Host ordered port: %#x\n", host_port);
  11. printf("Network ordered port: %#x\n", net_port);
  12. printf("Host ordered address: %#lx\n", host_addr);
  13. printf("Network ordered address: %#lx\n", net_addr);
  14. system("pause");
  15. return 0;
  16. }

运行结果:
Host ordered port: 0x1234
Network ordered port: 0x3412
Host ordered address: 0x12345678
Network ordered address: 0x78563412

另外需要说明的是,sockaddr_in 中保存IP地址的成员为32位整数,而我们熟悉的是点分十进制表示法,例如 127.0.0.1,它是一个字符串,因此为了分配IP地址,需要将字符串转换为4字节整数。

inet_addr() 函数可以完成这种转换。inet_addr() 除了将字符串转换为32位整数,同时还进行网络字节序转换。请看下面的代码:



  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <WinSock2.h>
  4. #pragma comment(lib, "ws2_32.lib")
  5. int main(){
  6. char *addr1 = "1.2.3.4";
  7. char *addr2 = "1.2.3.256";
  8. unsigned long conv_addr = inet_addr(addr1);
  9. if(conv_addr == INADDR_NONE){
  10. puts("Error occured!");
  11. }else{
  12. printf("Network ordered integer addr: %#lx\n", conv_addr);
  13. }
  14. conv_addr = inet_addr(addr2);
  15. if(conv_addr == INADDR_NONE){
  16. puts("Error occured!");
  17. }else{
  18. printf("Network ordered integer addr: %#lx\n", conv_addr);
  19. }
  20. system("pause");
  21. return 0;
  22. }

运行结果:
Network ordered integer addr: 0x4030201
Error occured!

从运行结果可以看出,inet_addr() 不仅可以把IP地址转换为32位整数,还可以检测无效IP地址。

注意:为 sockaddr_in 成员赋值时需要显式地将主机字节序转换为网络字节序,而通过 write()/send() 发送数据时TCP协议会自动转换为网络字节序,不需要再调用相应的函数。

在socket中使用域名

客户端中直接使用IP地址会有很大的弊端,一旦IP地址变化(IP地址会经常变动),客户端软件就会出现错误。

而使用域名会方便很多,注册后的域名只要每年续费就永远属于自己的,更换IP地址时修改域名解析即可,不会影响软件的正常使用。

关于域名注册、域名解析、host 文件、DNS 服务器等本节并未详细讲解,请读者自行脑补。本节重点讲解如何使用域名。

通过域名获取IP地址

域名仅仅是IP地址的一个助记符,目的是方便记忆,通过域名并不能找到目标计算机,通信之前必须要将域名转换成IP地址。

gethostbyname() 函数可以完成这种转换,它的原型为:



  1. struct hostent *gethostbyname(const char *hostname);

hostname 为主机名,也就是域名。使用该函数时,只要传递域名字符串,就会返回域名对应的IP地址。返回的地址信息会装入 hostent 结构体,该结构体的定义如下:



  1. struct hostent{
  2. char *h_name; //official name
  3. char **h_aliases; //alias list
  4. int h_addrtype; //host address type
  5. int h_length; //address lenght
  6. char **h_addr_list; //address list
  7. }

从该结构体可以看出,不只返回IP地址,还会附带其他信息,各位读者只需关注最后一个成员 h_addr_list。下面是对各成员的说明:

  • h_name:官方域名(Official domain name)。官方域名代表某一主页,但实际上一些著名公司的域名并未用官方域名注册。
  • h_aliases:别名,可以通过多个域名访问同一主机。同一IP地址可以绑定多个域名,因此除了当前域名还可以指定其他域名。
  • h_addrtype:gethostbyname() 不仅支持 IPv4,还支持 IPv6,可以通过此成员获取IP地址的地址族(地址类型)信息,IPv4 对应 AF_INET,IPv6 对应 AF_INET6。
  • h_length:保存IP地址长度。IPv4 的长度为4个字节,IPv6 的长度为16个字节。
  • h_addr_list:这是最重要的成员。通过该成员以整数形式保存域名对应的IP地址。对于用户较多的服务器,可能会分配多个IP地址给同一域名,利用多个服务器进行均衡负载。


hostent 结构体变量的组成如下图所示:



下面的代码主要演示 gethostbyname() 的应用,并说明 hostent 结构体的特性:

纯文本复制


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <WinSock2.h>
  4. #pragma comment(lib, "ws2_32.lib")
  5. int main(){
  6. WSADATA wsaData;
  7. WSAStartup( MAKEWORD(2, 2), &wsaData);
  8. struct hostent *host = gethostbyname("www.baidu.com");
  9. if(!host){
  10. puts("Get IP address error!");
  11. system("pause");
  12. exit(0);
  13. }
  14. //别名
  15. for(int i=0; host->h_aliases[i]; i++){
  16. printf("Aliases %d: %s\n", i+1, host->h_aliases[i]);
  17. }
  18. //地址类型
  19. printf("Address type: %s\n", (host->h_addrtype==AF_INET) ? "AF_INET": "AF_INET6");
  20. //IP地址
  21. for(int i=0; host->h_addr_list[i]; i++){
  22. printf("IP addr %d: %s\n", i+1, inet_ntoa( *(struct in_addr*)host->h_addr_list[i] ) );
  23. }
  24. system("pause");
  25. return 0;
  26. }

运行结果:
Aliases 1: www.baidu.com
Address type: AF_INET
IP addr 1: 61.135.169.121
IP addr 2: 61.135.169.125

理解UDP套接字

TCP 是面向连接的传输协议,建立连接时要经过三次握手,断开连接时要经过四次握手,中间传输数据时也要回复ACK包确认,多种机制保证了数据能够正确到达,不会丢失或出错。

UDP 是非连接的传输协议,没有建立连接和断开连接的过程,它只是简单地把数据丢到网络中,也不需要ACK包确认。

UDP 传输数据就好像我们邮寄包裹,邮寄前需要填好寄件人和收件人地址,之后送到快递公司即可,但包裹是否正确送达、是否损坏我们无法得知,也无法保证。UDP 协议也是如此,它只管把数据包发送到网络,然后就不管了,如果数据丢失或损坏,发送端是无法知道的,当然也不会重发。

既然如此,TCP应该是更加优质的传输协议吧?

如果只考虑可靠性,TCP的确比UDP好。但UDP在结构上比TCP更加简洁,不会发送ACK的应答消息,也不会给数据包分配Seq序号,所以UDP的传输效率有时会比TCP高出很多,编程中实现UDP也比TCP简单。

UDP 的可靠性虽然比不上TCP,但也不会像想象中那么频繁地发生数据损毁,在更加重视传输效率而非可靠性的情况下,UDP是一种很好的选择。比如视频通信或音频通信,就非常适合采用UDP协议;通信时数据必须高效传输才不会产生“卡顿”现象,用户体验才更加流畅,如果丢失几个数据包,视频画面可能会出现“雪花”,音频可能会夹带一些杂音,这些都是无妨的。

与UDP相比,TCP的生命在于流控制,这保证了数据传输的正确性。

最后需要说明的是:TCP的速度无法超越UDP,但在收发某些类型的数据时有可能接近UDP。例如,每次交换的数据量越大,TCP 的传输速率就越接近于 UDP。

基于UDP的服务器端和客户端

前面的文章中我们给出了几个TCP的例子,对于UDP而言,只要能理解前面的内容,实现并非难事。

UDP中的服务器端和客户端没有连接

UDP不像TCP,无需在连接状态下交换数据,因此基于UDP的服务器端和客户端也无需经过连接过程。也就是说,不必调用 listen() 和 accept() 函数。UDP中只有创建套接字的过程和数据交换的过程。

UDP服务器端和客户端均只需1个套接字

TCP中,套接字是一对一的关系。如要向10个客户端提供服务,那么除了负责监听的套接字外,还需要创建10套接字。但在UDP中,不管是服务器端还是客户端都只需要1个套接字。之前解释UDP原理的时候举了邮寄包裹的例子,负责邮寄包裹的快递公司可以比喻为UDP套接字,只要有1个快递公司,就可以通过它向任意地址邮寄包裹。同样,只需1个UDP套接字就可以向任意主机传送数据。

基于UDP的接收和发送函数

创建好TCP套接字后,传输数据时无需再添加地址信息,因为TCP套接字将保持与对方套接字的连接。换言之,TCP套接字知道目标地址信息。但UDP套接字不会保持连接状态,每次传输数据都要添加目标地址信息,这相当于在邮寄包裹前填写收件人地址。

发送数据使用 sendto() 函数:

 
  
  1. ssize_t sendto(int sock, void *buf, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen); //Linux
  2. int sendto(SOCKET sock, const char *buf, int nbytes, int flags, const struct sockadr *to, int addrlen); //Windows

Linux和Windows下的 sendto() 函数类似,下面是详细参数说明:

  • sock:用于传输UDP数据的套接字;
  • buf:保存待传输数据的缓冲区地址;
  • nbytes:带传输数据的长度(以字节计);
  • flags:可选项参数,若没有可传递0;
  • to:存有目标地址信息的 sockaddr 结构体变量的地址;
  • addrlen:传递给参数 to 的地址值结构体变量的长度。


UDP 发送函数 sendto() 与TCP发送函数 write()/send() 的最大区别在于,sendto() 函数需要向他传递目标地址信息。

接收数据使用 recvfrom() 函数:

 
  
  1. ssize_t recvfrom(int sock, void *buf, size_t nbytes, int flags, struct sockadr *from, socklen_t *addrlen); //Linux
  2. int recvfrom(SOCKET sock, char *buf, int nbytes, int flags, const struct sockaddr *from, int *addrlen); //Windows

由于UDP数据的发送端不不定,所以 recvfrom() 函数定义为可接收发送端信息的形式,具体参数如下:

  • sock:用于接收UDP数据的套接字;
  • buf:保存接收数据的缓冲区地址;
  • nbytes:可接收的最大字节数(不能超过buf缓冲区的大小);
  • flags:可选项参数,若没有可传递0;
  • from:存有发送端地址信息的sockaddr结构体变量的地址;
  • addrlen:保存参数 from 的结构体变量长度的变量地址值。

基于UDP的回声服务器端/客户端

下面结合之前的内容实现回声客户端。需要注意的是,UDP不同于TCP,不存在请求连接和受理过程,因此在某种意义上无法明确区分服务器端和客户端,只是因为其提供服务而称为服务器端,希望各位读者不要误解。

下面给出Windows下的代码,Linux与此类似,不再赘述。

服务器端 server.cpp:



  1. #include <stdio.h>
  2. #include <winsock2.h>
  3. #pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
  4. #define BUF_SIZE 100
  5. int main(){
  6. WSADATA wsaData;
  7. WSAStartup( MAKEWORD(2, 2), &wsaData);
  8. //创建套接字
  9. SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
  10. //绑定套接字
  11. sockaddr_in servAddr;
  12. memset(&servAddr, 0, sizeof(servAddr)); //每个字节都用0填充
  13. servAddr.sin_family = PF_INET; //使用IPv4地址
  14. servAddr.sin_addr.s_addr = htonl(INADDR_ANY); //自动获取IP地址
  15. servAddr.sin_port = htons(1234); //端口
  16. bind(sock, (SOCKADDR*)&servAddr, sizeof(SOCKADDR));
  17. //接收客户端请求
  18. SOCKADDR clntAddr; //客户端地址信息
  19. int nSize = sizeof(SOCKADDR);
  20. char buffer[BUF_SIZE]; //缓冲区
  21. while(1){
  22. int strLen = recvfrom(sock, buffer, BUF_SIZE, 0, &clntAddr, &nSize);
  23. sendto(sock, buffer, strLen, 0, &clntAddr, nSize);
  24. }
  25. closesocket(sock);
  26. WSACleanup();
  27. return 0;
  28. }

代码说明:
1) 第12行代码在创建套接字时,向 socket() 第二个参数传递 SOCK_DGRAM,以指明使用UDP协议。

2) 第18行代码中使用htonl(INADDR_ANY)来自动获取IP地址。

利用常数 INADDR_ANY 自动获取IP地址有一个明显的好处,就是当软件安装到其他服务器或者服务器IP地址改变时,不用再更改源码重新编译,也不用在启动软件时手动输入。而且,如果一台计算机中已分配多个IP地址(例如路由器),那么只要端口号一致,就可以从不同的IP地址接收数据。所以,服务器中优先考虑使用INADDR_ANY;而客户端中除非带有一部分服务器功能,否则不会采用。

客户端 client.cpp:



  1. #include <stdio.h>
  2. #include <WinSock2.h>
  3. #pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
  4. #define BUF_SIZE 100
  5. int main(){
  6. //初始化DLL
  7. WSADATA wsaData;
  8. WSAStartup(MAKEWORD(2, 2), &wsaData);
  9. //创建套接字
  10. SOCKET sock = socket(PF_INET, SOCK_DGRAM, 0);
  11. //服务器地址信息
  12. sockaddr_in servAddr;
  13. memset(&servAddr, 0, sizeof(servAddr)); //每个字节都用0填充
  14. servAddr.sin_family = PF_INET;
  15. servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  16. servAddr.sin_port = htons(1234);
  17. //不断获取用户输入并发送给服务器,然后接受服务器数据
  18. sockaddr fromAddr;
  19. int addrLen = sizeof(fromAddr);
  20. while(1){
  21. char buffer[BUF_SIZE] = {0};
  22. printf("Input a string: ");
  23. gets(buffer);
  24. sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr*)&servAddr, sizeof(servAddr));
  25. int strLen = recvfrom(sock, buffer, BUF_SIZE, 0, &fromAddr, &addrLen);
  26. buffer[strLen] = 0;
  27. printf("Message form server: %s\n", buffer);
  28. }
  29. closesocket(sock);
  30. WSACleanup();
  31. return 0;
  32. }

先运行 server,再运行 client,client 输出结果为:

Input a string: C语言中文网
Message form server: C语言中文网
Input a string: c.biancheng.net Founded in 2012
Message form server: c.biancheng.net Founded in 2012
Input a string:


从代码中可以看出,server.cpp 中没有使用 listen() 函数,client.cpp 中也没有使用 connect() 函数,因为 UDP 不需要连接。

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

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

相关文章

【Spring】SpringBoot 配置 logback 日志

1. 概述 日志在一个业务系统重非常重要&#xff0c;包含有非常重要的数据&#xff0c;可以用于客户反馈问题的排查、线上问题的追踪&#xff0c;以及根据日志数据对业务情况进行有效的监控配置&#xff0c;及时发现线上问题。 常用的日志框架有好几种&#xff0c;但最常用的是…

[附源码]Node.js计算机毕业设计大学校园兼职网站Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

【观察】定义下一代云原生实时数仓,SelectDB Cloud“打了个样”

2020年&#xff0c;国家在《关于构建更加完善的要素市场化配置体制机制的意见》中&#xff0c;首次将数据与土地、资本、劳动力并列为关键生产要素&#xff0c;并提出加快培育数据要素市场的愿景&#xff0c;此举可谓意义重大。背后的原因是&#xff0c;当下中国企业正在加速从…

【车载开发系列】UDS诊断---写入内存($0x3D)

【车载开发系列】UDS诊断—写入内存&#xff08;$0x3D&#xff09; UDS诊断---写入内存&#xff08;$0x3D&#xff09;【车载开发系列】UDS诊断---写入内存&#xff08;$0x3D&#xff09;一.概念定义二.报文格式1&#xff09;请求报文2&#xff09;肯定响应3&#xff09;否定响…

Redis实战——消息队列

目录 1. 什么是消息队列&#xff1f; 2. 基于List结构模拟消息队列 3. 基于PubSub的消息队列 4. 基于Stream的消息队列 4.1 基于Stream的单消费者模式 4.2 基于Stream的消息队列-消费者组 1. 什么是消息队列&#xff1f; 字面意思就是存放消息的队列。最简单的消息队列模…

学习参数化计算优化风扇定位step by step

一、写在前面 本教程的目的是演示ANSYS Icepak的参数和优化功能。假定读者已经熟悉ANSYS Icepak界面&#xff0c;但是缺乏实战经验。 在这个案例中&#xff0c;读者可以掌握&#xff1a; 1、使用Network网络热阻Block来模拟IC芯片封装模型。 2、将变量定义为参数并通过参数…

算法竞赛入门【码蹄集进阶塔335题】(MT2226-2250)

算法竞赛入门【码蹄集进阶塔335题】(MT2226-2250&#xff09; 文章目录算法竞赛入门【码蹄集进阶塔335题】(MT2226-2250&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f;目录1. MT2226 36进制22. MT2227 36进制33. MT2228 36进制44.…

电商、线上教育、在线医疗等必备资质——ICP许可证 。

危中有机&#xff0c;疫情也概莫能外。一场突如其来的疫情&#xff0c;引发了消费、健康、办公、学习等领域的新变革&#xff0c;电商、短视频、游戏、线上教育、在线医疗、知识付费等“互联网”项目&#xff0c;再次迎来发展机遇。 然而&#xff0c;如果想要借助互联网进行经…

[附源码]Python计算机毕业设计大学生网上书店Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

type 与 interface

type 与 interface 官方文档是这么说的&#xff1a; For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you…

基于C#+SQL Server2008实现(WinForm)学生宿舍管理系统【100010029】

1 概述 本次课程设计是数据库课程设计&#xff0c;我选的是学生宿舍管理系统&#xff0c;功能块主要是宿舍管理、学生管理、出入登记和来访登记&#xff0c;目的在于将学生宿舍的管理实现信息化&#xff0c;监控学生宿舍的情况防止意外发生。 课程设计任务的需求分析 2.1设计…

01算法的概念

开始系统学习算法啦&#xff01;为后面力扣和蓝桥杯的刷题做准备&#xff01;这个专栏将记录自己学习算法是的笔记&#xff0c;包括概念&#xff0c;算法运行过程&#xff0c;以及代码实现&#xff0c;希望能给大家带来帮助&#xff0c;感兴趣的小伙伴欢迎评论区留言或者私信博…

Day828.多线程原语:管程 -Java 并发编程实战

多线程原语&#xff1a;管程 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于多线程原语&#xff1a;管程的内容。 并发编程这个技术领域已经发展了半个世纪了&#xff0c;相关的理论和技术纷繁复杂。 那有没有一种核心技术可以很方便地解决的并发问题呢&#xff1…

JAVA毕业设计——基于SpringBoot的健身房管理系统(源代码+数据库)

github代码地址 https://github.com/ynwynw/gym-public 毕业设计所有选题地址 https://github.com/ynwynw/allProject 基于SpringBoot的健身房管理系统(源代码数据库) 一、系统介绍 系统层次结构图如下&#xff1a; 管理员登录模块会员管理模块教练管理模块课程管理模块器材…

基于Java+SQL Server2000实现(界面)学生选课管理系统【100010025】

学生选课管理系统 二、需求描述 本系统是一个单机版的小型的学生选课管理系统&#xff0c;在本系统中&#xff0c;开发了一个面向管理员、学生和教师这三个对象的教务平台&#xff0c;对学生提供的服务有登录、选课、、修改登录密码、和查询成绩这几个服务&#xff0c;所选课…

第二季5:配置视频捕获(step3:VI部分)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 本文将详细介绍博文第二季3&#xff1a;sample_venc.c的整体分析提及的“配置视频捕获”。 该部分主要涉及以下步骤&#xff1a; 5、配置MIPI6、初始化ISP7、运行ISP线程8、配置开启VI 设备捕…

Mybatis 参数处理器注入配置解析流程

参数处理器的作用 Mybatis作为一个ORM框架&#xff0c; 其最原始的本质就是JDBC&#xff0c;对于JDBC的使用步骤中有2步和参数处理器有关&#xff0c; 就是给预处理器PreparedStatement 设置参数以及通过结果集获取字段值。 这两个步骤在Mybatis中已经成为框架底层逻辑流程&am…

【K8S系列】第十一讲:包管理神器-Helm

目录 序言 1.背景介绍 1.1 k8s 简单介绍 1.2 k8s部署挑战 2.Helm 2.1 Helm介绍 2.1 使用优势&#xff1a; 3.Helm模块 3.1 Helm 3.1.1 安装Helm 3.2 Chart 3.2.1 Chart 基本介绍 3.2.2 Chart目录结构 3.3 Repoistory 3.4 Config 3.5 Release 4.投票 序言 当…

Windows及Linux系统查找某个端口和文件被占用

概述 开发中很常见的问题&#xff0c;每次遇到这个问题&#xff0c;都是去Google搜索&#xff0c;不一定能搜到满意的答案&#xff0c;有点耗时&#xff0c;故记录一下&#xff0c;得到本文。 端口被占用&#xff0c;导致IDEA启动应用失败。又或者某个文件被某个未知的应用使…

c#入门-抽象类

抽象类 有些类存在的意义就是为了让别的类继承。自己本身不应该具有实例。例如 class 单位 {public int 生命; } class 建筑 : 单位 {public int 护甲; } class 英雄 : 单位 {public int 护盾; } class 小兵 : 单位 {public int 经验; }可以使用abstract把他声明为抽象类。 抽…