Linux网络基础(协议 TCP/IP 网络传输基本流程 IP VS Mac Socket编程UDP)
文章目录
- 一.前言
- 二.协议
- 协议分层
- 分层的好处
- OSI七层模型
- TCP/IP五层(或四层)模型
- 为什么要有TCP/IP协议
- TCP/IP协议与操作系统的关系(宏观上是如何实现的)
- 什么是协议
- 三.网络传输基本流程
- 局域网(以太网为例)通信原理
- MAC地址
- 令牌环网
- 封装与解包分用
- 四.IP地址
- IP VS Mac地址
- 五.Socket编程预备
- 理解源IP地址和目的IP地址
- 端口号
- 端口号范围划分
- 认识TCP协议与UDP协议
- 网络字节序(大小端)
- 网络主机相互转换接口
- socket编程接口
- sockaddr结构
- 六.Socket 编程 UDP
- echo server 服务端
- 收数据
- Client 客户端
- 注意事项
- 整体的代码与效果
- 效果
- 七.网络命令
- Ping 命令
- netstat 命令
- pidof 命令
一.前言
一开始时,计算机都相互独立,只能通过软盘,才能把数据交给另一台计算机
后面因为效率原因,就有了服务器,都从服务区上拿数据
美国一开始有贝尔实验室、麻省理工实验室…一开始实验室各搞各的,所以局域网有很多种类的以太网、令牌网…
新技术的产生,一定产生了新设备(芯片技术应用到笔记本,就叫做计算机;用到汽车上,就叫做智能汽车),所以计算机的发展,肯定伴随着新的设备(比如网络通信,得有网线)
1994年中国进入互联网,不仅让腾讯阿里发展很好,华为和其它通信的公司也发展起来。
当计算机太多时,就有了交换机和路由器
计算机越来越多,就有了广域网
二.协议
协议就是一种约定。
比如:1+1=2是公认,它也可以说成One plus one equals two。语言可以不同,但只能1+1=2。这就是一种协议。
计算机厂商上有很多,但要让计算机之间有需要一种协议
能定制标准的都是世界公认的组织或公司,但定标准,不一定要实现标准。比如:华为定的5G标准,不一定要华为自己实现,但其它公司要做5G,得按照华为定的标准实现。
协议分层
协议本身就是软件,为了更好的模块化,解耦合,最好也要分层。
分层的好处
以两个人说话方式为例:
这是分两层,语言层和通信设备层:
这就是模块化的解耦合,一层出现问题,另一层并不影响,下一层出现了问题,替换了协议,上一层完全不受影响。(软件层划分的越合理,越好,比如封装、STL容器)
出了问题也好排查。张三听不清楚李四打电话,无非就是语言不同或者交流方式出现了问题。
OSI七层模型
OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范。
定标准和实现标准是两批人,实现由顶级的工程师实现,windows和Linux能通信就是因为两者之间遵守着同一个协议。
分层名称 | 功能 | 每层功能概述 | |
---|---|---|---|
7 | 应用层 | 针对特定应用的协议 | ![]() |
6 | 表示层 | 设备固有数据格式和网络标准数据格式的转换 | ![]() |
5 | 会话层 | 通信管理。负责建立和断开通信连接(数据流动的逻辑通路)。管理传输层以下的分层 | ![]() |
4 | 传输层 | 管理两个节点之间的数据传输。负责可靠传输(确保数据被可靠地传送到目标地址) | ![]() |
3 | 网络层 | 地址管理与路由选择。 | ![]() |
2 | 数据链路层 | 互连设备之间传送和识别数据帧。 | ![]() |
1 | 物理层 | 以"0"、"1"代表电压的高地、灯光的闪灭 | ![]() |
实际真正落地的不是OSI七层模型,而是5层协议,会话层、表示层、应用层这三个规划为一个大的应用层,因为会话层和表示层是不可能接入到操作系统中的。
TCP/IP五层(或四层)模型
TCP是传输层的协议,IP是网络层的协议。TCP/IP是一组协议的代名词。
5层分别为:
物理层 | 规定什么是’0’、‘1’,特定的01序列是什么含义。通信媒介不一样,规定传递的方式。 |
---|---|
数据链路层 | 设备和设备之间的传递和识别,有以太网、令牌环网、无线LAN等保证设备之间的通信。 |
网络层 | 两个跨网络的设备需要IP地址传输。 |
传输层 | 数据传输长距离传输时丢包或乱序怎么处理? |
应用层 | 设备A从北京送到在西安的设备B,其中传数据的目的,如何应用? |
其中物理层是光电信号的传递,所以只谈剩下的4层。
为什么要有TCP/IP协议
首先,即便是单个计算机,都是存在协议的,比如与磁盘通信会有磁盘相关的协议:SATA、IDE、SCSI等。设备之间的协议都是在计算机内部,比如内存和磁盘距离个10厘米,通信的成本低,问题就少。
网络因为距离很远,由于物理特性就会出现很多问题。比如男女朋友,在同一个学校,出现问题,解决问题的成本低;异地恋,则会提高成本。
假设主机A发送数据给主机C的过程中,依次会产生的问题:
1.主机A发送给主机C,得把数据交给路由器。(异地恋给女朋友买东西,得交给快递小哥)
2.网络上这么多主机,如何准确的找到主机C。
3.如果我的数据在发送的过程中,在某一个节点丢失了怎么办?(快递小哥转发错地方了)
4.发送数据不是目的,只是手段,让主机C使用数据是目的。(主机A把‘你好’发给主机C,主机C需要处理‘你好’,是以读不回还是以读乱回)
所以有了各种协议,去解决上面的问题。
TCP/IP协议是一种解决方案,本质问题是两台通信的主机距离变远了,所以有了各种问题,需要通过TCP/IP协议这种解决方案去解决。
TCP/IP协议与操作系统的关系(宏观上是如何实现的)
Windows和Linxu底层是不一样的,传输层(典型协议是TCP)和网络层(典型协议是IP)在操作系统内部实现的,虽然操作系统实现本身不一样,但网络部分必须是一样的,这就是为什么Windows和Linux能通讯。
不同主机或厂商所对应不同设备之间可以通信的秘密就在于,大家实现的是相同的网络协议栈。
什么是协议
OS一般都是用C/C++写的代码。
假设有两个主机A、B,两个主机的内核的代码不一样,但传输层和网络层的的代码一样。在传输层有一个结构体protocol,主机A发送struct protocol data给主机B。
问题:在通信的时候是传输二进制数据,主机B能识别data,并精确的识别a=10,b=20,c=30吗?
答:是可以的;因为双方都有同一个结构体struct protocol,所以主机B能立马识别abc的值。
所谓协议:就是通信双方都认识的结构化的数据类型。
因为协议是分层的,所以每层双方都有协议,同层之间,互相可以认识对方的协议。
以网上购物发快递为例子:
买键盘,实际就想要一个键盘。但实际给我的,除了键盘还给了盒子上贴的一张快递单,我看到这个快递单子,就能确认这是我的快递。快递单+键盘两个共同构成了一个完整的报文。
快递单上所有的信息都是发快递的人填的,快递小哥和我也明白快递单上面的内容。这个单子就是三者之间的约定,这个约定是快递公司定的(定标准的人)。发快递的人、快递小哥与收快递的人一起遵守这个约定。
用C语言表示这个快递单,不就是一个结构体。快递单就是协议的报头,键盘就是有效载荷,合在一起叫做报文。
发送方构建快递单叫做构建报头,把键盘装起来叫做封装。
综上所述:协议就是约定。
三.网络传输基本流程
以太网、无线LAN等局域网之间的标准是可以不同的。
局域网(以太网为例)通信原理
问题:两台主机在同一个局域网,是否可以直接通信?
答:是可以的;宿舍的无线路由器,你和舍友连着同一个局域网,和隔壁宿舍连着是不同的局域网,小时候玩<<我的世界>>开个热点把4G网络关上依旧能一起玩,在同一局域网。
故事:在教室里,老师喊张三,问作业为什么没做,站起来!教室里的同学都听到了,张三分析老师刚刚说的信息,提取目的地址叫做张三,就站起来了,然后张三跟老师说:我作业交了。其他人分析刚刚老师说的消息里面的一个目的地址叫做张三,跟自己没关系,直接把听到的这句话丢弃掉,不做响应。
双方在通信的整个过程,老师与张三都互相认为在单独通信。周围其他人也能听到这个消息,但不做响应。
其中教室叫做局域网,老师为主机A,张三为主机B,两者通信时只要知道对方名字就能通信,其他人能收到但不做响应,这就是局域网通信的基本原理。
总结:局域网能直接通信,每台主机(局域网)都要有一个唯一的标识:MAC地址
MAC地址
MAC地址是数据链路层中相连的节点。(就是标识主机的唯一性)
长度为48位,6个字节,一半用16进制+冒号的形式来表示。
可以用ifconfig指令查看:
ifconfig
Mac地址是集成在网卡中的地址,在出厂时就设置好了,可以说在全球具有唯一性。
问题:操作系统时如何获取Mac地址?
答:开机时,网卡的驱动程序会根据网卡的协议,会把Mac地址读到操作系统。
不仅仅网卡有唯一标识,很多设备都有唯一标识,比如磁盘也是有全球唯一标识,叫做序列号。
虚拟机中的Mac地址不是真实的地址,而是虚拟出来的,可能会产生冲突。
以主机A用以太网给主机E发送数据为例:
以太网通信的原理:
1.在以太网中,任何时刻,只允许一台机器向网络中发送数据。
2.如果有多台同时发送,就会产生数据干扰,称为数据碰撞。(发送的01序列,之间可能干扰)
3.所有发送数据的主机要进行碰撞检测和碰撞避免。(主机A把数据发出去了,检测数据有没有发送成功,主机A自己也是一台主机,所以自己发送的数据自己能到,跟发的数据和收的数据做比较,查看是否碰撞,一旦发生碰撞了,就要进行碰撞避免。比如主机A与主机B发生了碰撞,两主机都要休眠一定的随机时间,再次碰撞的概率减小,同时这个局域网中少了两台主机,可以让其它主机趁着这个间隙发送数据)
根据上面三个原理,很像一种临界资源(公共的数据),只允许一种主机,访问该临界资源里的资源。
4.没有交换机的情况下,一个以太网就是一个碰撞域。
5.局域网通信的过程中,主机对收到的报文确定是否发给自己的,是通过目标Mac地址判定。
可以通过系统角度来理解局域网通信。
令牌环网
主机A持有数据,就能发消息。主机B持有数据,也能发消息。这不就是一把锁。
封装与解包分用
在同一个局域网内,发送消息的过程:
其中每层都有协议,所以当进行上述传输流程时,要进行封装和解包:
明确概念:
以网络层为例:网络层报头为报头。传输层报头、应用层报头与“你好”为有效载荷。
从上到下添加报头,可以想象成一个入栈的结构,往上交付的过程就是出栈的过程,所以我们也称为网络协议栈:
数据在网络中发送的时候,一定最终要在硬件上跑,主机2的网卡先收到数据,然后再向上解包分用。就比如你在三楼宿舍要去3楼教学楼,只能从宿舍的三楼跑到1楼,然后再从教学楼的1楼跑到3楼:
应用层存在上百种协议,传输层有tcp/udp…,网络层有IP/ICMP…链路层有mac
若发送数据应用层用http,传输层用tcp,网络层用ip,链路层用mac,那问题在于接收层,网络层中有ip/icmp…等协议,如何知道发送方的网络层用到什么协议?
在前面说了,报头(协议)就是一种结构体,那么封装的时候,他会把有效载体封装进结构体中:
总结:网络协议有两个共性:
1.报头和有效载荷分离的问题 – 解包。
2.除了应用层,每一层协议都得解决一个问题:自己的有载荷,应该交给上一层的那一种协议 – 分层。
快递就有分用的功能,快递小哥到当地了,给每一个收快递的人打电话,不就是一种分用。电话是什么时候填的?是在发快递时候填好了(封装)。
报文 = 报头 + 有效载荷
在不同场景下,不同的报文有不同的名字(比如在学校叫某同学,工作某同事)
补充:
1.应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装。
2.首部信息中包含了一些类似于首部有多长, 载荷(payload)有多长, 上层协议是什么等信息。
3.数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部, 根据首部中的 “上层协议字段” 将数据交给对应的上层协议处理。
四.IP地址
上面讲述的都是在同一局域网中通信,那么不在同一局域网如何通信?
路由器:路由器要能够跨网络转发,至少连接两个网络,就得配上多个网络与驱动程序。
下图主机A的路由器与主机B的路由器分别配两张网卡:1个网卡连接的是以太网;1个连接着令牌环网。
因为用的局域网不同,所以主机A用的以太网的驱动程序,主机B用的令牌环网的驱动程序。
其中,IP地址是标识网络中唯一性的,IP协议有两个版本:IPv4和IPv6。
IPv4是一个4字节,32位的整数,通常用“点分十进制”的字符串标识IP地址,例如:192.168.0.1;用点分隔的每一个数字为一个字节,数字区间为0~255。
IPv6是一个16字节,128位的整数。
Linux下的IP地址:
Windows下看网络地址:ipconfig
紧接着上面用户A与用户B通信,本质就是网络协议栈之间的通信:
路由器和用户A属于一个局域网,路由器和用户B也属于通一个局域网。他们两两配置的网卡是相似的,在同一网段就能通信,以Windows为例:
在网络层不仅要添加报头,还要路由。(路由:信息要去172.168.22,肯定不是自己这个局域网(在相同的局域网中,主机都是同样的开头,比如192开头),所以肯定不是交给内网,要交给路由器)
路由:发现不是发给自己局域网主机的报文,就推送给服务器。
网络层到数据链路层的报头是:macA帧(其中包含scr:macA,代表是主机A发送的。还有dst:macleft),其余主机会收到该报文,但发现dst的地址不是他们自己,就不做处理,只有路由器会收到。路由器收到该报文,确认报文是自己的,向上交付给网络层:
网络层再次路由,然后路由器发现你要去dst:172.168.2.2和网卡对应的IP地址(172.168.2.1)差不多,于是再把报文再进行向下交付,重新封装(报头:src:macRight,dst:macB)
后有数据电路层收到报文并解包交给上层。
这个过程我们发现:主机A网络层拿到的报文、路由器网络层拿到的报文、主机B网络层拿到的报文,他们原IP、目的IP、数据是一模一样的:
结论:网络层(就是IP层)向上(包括网络层)看到的所有的报文都是一样的,所以IP可以屏蔽底层网络的差异!
这就是为什么用的手机用无线网、令牌环、油量…都能相互通信!
IP VS Mac地址
IP地址和mac地址的区别:
故事:
唐僧去西天取经
其中发现上面有两套地址中,其中车迟国领主、女儿国女王、火焰山领主就相当于一张路由表,方便我们查询。
唐僧问车迟国国王下一站去哪里时,国王想的过程就是查询一次路由表的过程。
唐僧提供地址信息,车迟国国王提供的路由表的信息,两个信息一结合就知道接下来唐僧要去女儿国。
其中IP地址不变,变得都是mac地址。
问题:为什么车迟国国王提供的信息是女儿国,不是其它的国家?
答:你和朋友在北京,要去云南旅游:
其中mac地址受IP地址影响。(阶段性目标受最终目标影响),比如最终目标是成为一名博士,前一个阶段得先是研究生。
五.Socket编程预备
理解源IP地址和目的IP地址
IP在网络中是标识主机唯一性的,跨网络的。
mac地址是在局域网中标识唯一性的,只在局域网内有效,出了局域网就把“衣服”脱掉了。
问题:数据传输到主机是目的吗?主机A把数据交给主机B就完了吗?
答:不是的。因为数据都是给人用的,比如:聊天是人与人聊天,游览网页是人在游览。人是通过QQ来聊天、通过游览器游览网页。启动QQ和游览器都是进程,进程是人在系统中代表,只要数据交给进程,人就相当于拿到了数据:
传输数据到主机不是目的,而是手段。到达主机内部,在交给主机内的进程,才是目的。
为了在任意主机内标识进程,引入了一个概念:端口号。
端口号
端口号(port)是传输层协议的内容:
1.端口号是一个2字节16位的整数;
2.端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理;
在传输层报头当中,会把端口号(port)写入其中:
需要网络通信的这类进程(比如QQ、B站…)启动的时候,需要自己绑定特定的端口号。
传输层收到报文时,通过报文中的端口号进行转发给指定的进程,我们在未来写代码的时候,就一定要和指定的port进行关联:
通信是两个人在通信,也可以说是两个进程在通信。
IP + PORT = 互联网中唯一的一个进程:
主机收到数据不是目的,而是手段,真正收到数据的是进程。
综上所述:网络通信的本质是进程间通信!!!
进程之间通信需要:
1.保证独立性。
2.看到一份公共资源(网络)。
其中IP地址+PORTA(端口)叫做socket通信。
问题:PID也是标识进程唯一性的,为什么不能用PID标识,而用port标识?
答:1.从技术上是可以做到的,但是PID是系统级别的概念,而port是网络的概念,让传输层用PID把数据交道应用层,就会产生强耦合。如果一个PID发生变换,那么整个网络部分都要调整。所以要做到系统是系统,网络是网络,系统与网络要做到解耦。
2.所有的进程都有PID,但不是所有的进程都想网络通信,所以在系统中,只有少量通信的进程,需要端口号,未来可以用端口号来分别该进程是否需要网络通信。
例子:在学校,每个人都有学号,且每个人都有身份证号,为什么学校不用身份证号来分辨而多出个学号呢?
方便管理,这就很好的做到了社会层面和学校层面的解耦,学号变了不影响身份证号。
注意:一个端口号只能被一个进程占用。
端口号范围划分
腾讯的QQ号不是所有的都能用,其内部保留了一些。
端口号也不是都能用的,一共有0~65535个端口号,说明一个服务器可以启动65536个网络服务,但不会那么多,3、4个服务已经够多了。
0~1023:知名端口号,HTTP,FTP,SSH等这些广为使用的应用层协议,他们的端口号都是固定的,深度捆绑。(这就跟110、120电话的性质类似)
1024~65535:操作系统动态分配的端口号(可以自己手动绑定)、客户端程序的端口号。
认识TCP协议与UDP协议
TCP协议:传输层协议、有连接、可靠传输、面向字节流。
UDP协议:传输层协议、无连接、不可靠传输、面向数据报。
传输数据时,如果数据丢失了,TCP协议可以提供相对应的策略(比如:丢失后重传、乱了进行重组排序),UDP协议丢了就是不管。
那就说了,直接用TCP协议不就得了,其实可靠传输是其的特征,不是优缺点,因为要保证可靠传输,说明要做更多的工作,得知道哪些丢了、哪些没丢。UDP协议不用做这么多工作,使用上更简单。
TCP协议常用于转账的领域;UDP协议常用于直播领域:
网络字节序(大小端)
我们学习C语言时就明白,存在大小端的问题,就是数据的存储方式。
把低权值位放到高地址就是大端机,把低权值位放到低地址处就是小端机:
不同的机器存储的方式是由差别的。
问题:如果主机A以大端的方式发送数据,主机B以小端的方式接收数据,不就反了?
答:定了个标准,网络通信必须大端!!即低地址高字节。
网络主机相互转换接口
未来为了使我们写的程序具有可移植性,在写通信的过程中大部分的大小端会自动帮我们转了,但端口号和IP地址之类的需要自己转化,系统提供了函数:
socket编程接口
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 开始监听socket (TCP, 服务器)int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 建立连接 (TCP, 客户端)int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen)
发现很多函数都用了sockaddr结构体,先说该结构体。
sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6。
socket的发明者不仅仅想让进程可以完成网络通信,也可以完成本主机通信:
网络在设计的时候,就考虑到让用户使用同一套的接口,能用来表示不同种类的通信方式,这不就是多态嘛!!
六.Socket 编程 UDP
echo server 服务端
首先要创建套接字。
socket函数:
其中AF_UNIX与套接字SOCK_DGRAM都是宏:
返回值:成功时,返回文件描述符;出错时,返回-1
创建套接字可以理解成把网卡打开了,在Linux系统中一切皆文件,网卡也是文件,所以可以通过返回的文件描述符对网卡设备进行读写(IO)。
未来进行收消息或发消息都需要sockfd(套接字):
- 创建socket文件:
void InitServer() // 初始化_sockfd
{// 1.创建socket文件_sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "create socket error");exit(SOCKET_ERROR);}LOG(DEBUG, "create socket success,sockfd :%d", _sockfd);
}
- 绑定:
客户端和服务器都要有IP地址与端口号:
bind函数,让套接字信息(IP、端口号…)与套接字(socket)关联起来。
struct sockaddr_in 里面的内容:
- sin_family储存AF_INET(网络的信息)。
- sin_port储存16位的端口号。
- sin_addr存储IP36位的IP地址。
发短信时,主机A不仅要把信息发送给主机B,主机A还把IP地址与端口交给主机B,因为主机B要给主机A应答。说明IP地址与端口都要走网络,所以IP地址与端口号要转成网络序列(大端)。
平常我们喜欢用string类型的变量当IP地址,但是使用时需要4字节的网络序列,所以需要把string类型转换为4字节的网络序列。
就有了接口:inet_addr
// 2.bindstruct sockaddr_in local;memset(&local,0,sizeof(local)); //置空local.sin_family = AF_INET;local.sin_port = htons(_localport); //主机转网络序列local.sin_addr.s_addr = inet_addr(_locallip.c_str()); //1.4字节 2.网络序列int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local)); if(n < 0){LOG(FATAL,"bind error");exit(BIND_ERROR);}LOG(DEBUG,"socket bind success");
服务器运行之后就不会关,就比如某杀毒软件,你关了也关不上。
所以启动是死循环,干什么事情在里面填写:
void Start(){_isrunning = true;while(_isrunning){//otherthing}}
整体代码如下:
#pragma once#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"using namespace log_ns;enum
{SOCKET_ERROR = 1,BIND_ERROR,
};int gsockfd = -1;
uint16_t glocalport = 888;// UdpServer user("192.1.1.1",8899);
class UdpServer : public nocopy //继承单例
{
public:UdpServer(std::string &locallip,uint16_t localport = glocalport): _sockfd(gsockfd),_localport(localport),_locallip(locallip),_isrunning(false){}void InitServer(){// 1.创建socket文件_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "create socket error");exit(SOCKET_ERROR);}LOG(DEBUG, "create socket success,sockfd :%d", _sockfd);// 2.bindstruct sockaddr_in local;memset(&local,0,sizeof(local)); //置空local.sin_family = AF_INET;local.sin_port = htons(_localport); // 主机转网络序列local.sin_addr.s_addr = inet_addr(_locallip.c_str()); //1.string转成4字节 2.主机转网络网络序列int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n < 0){LOG(FATAL,"bind error");exit(BIND_ERROR);}LOG(DEBUG,"socket bind success");}void Start(){_isrunning = true;while(_isrunning){//otherthing}}~UdpServer(){}private:int _sockfd;uint16_t _localport;std::string _locallip;bool _isrunning;
};
可以用指令netstat查看:
收数据
虽然sockfd和文件描述符相似,但不能直接使用write和read…因为sockfd是数据报的,不是字节流的。
要用recvfrom来读:
返回值:失败返回-1;返回收到信息的大小。
发送消息:sendto函数
返回值:失败返回-1;返回发送信息的大小:
代码:
void Start(){_isrunning = true;char buffer[1024];while(_isrunning){struct sockaddr_in peer;socklen_t len = sizeof(peer);int n = recvfrom(_sockfd,&buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);}}
Client 客户端
- 首先客户端一定在未来知道服务器的IP地址和端口号的。
IP地址:比如在网页登入QQ时,www.qq.com,这是域名,登入时会转化成IP地址。
端口号:跟服务端是强关联的(比如:我们平常说打报警电话,很少说打报警电话110)。 - 其次客服端一定要有自己的IP与端口,来区分客户端的唯一性。
server的端口号,必须由用户指明,而且是明确的,不能随意改变。(淘宝、百度…)
client的端口号,一般不让用户自己设定,而是让client OS随机选择。
原因:服务端是被多个人访问的,所以不能随意改变端口号。客户端,以手机为例,有微信、京东…不同公司的客户端,比如淘宝喜欢888的端口号,京东也喜欢888的端口号,在点击启动的时候,因为一个进程绑定888后,其余进程就用不了888了,就造成淘宝能打开,京东就打不开了。所以不能固定绑定客户端的端口号。
综上所述:client 需要 bind 它自己的IP和端口号,但是client 不需要 自己手动填充 bind它自己的IP和端口。
问题:client如何选择端口号,什么时候选择端口号?
答:客户端在首次向服务器发送数据的时候,OS会自动给client bind 它自己家的IP和端口,在首次调用sendto函数时,绑定IP与端口号。
client总的代码:
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"// ./udp_client server-ip server-port
// ./udp_client 127.0.0.1 8888
int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << "server-ip server-port" << std::endl;}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 创建socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "create socket error" << std::endl;exit(1);}struct sockaddr_in server;memset(&server, 0, sizeof(server)); //初始化server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_port = htons(serverport);while (1){std::string line;std::cout << "Please Enter# ";getline(std::cin, line);int n = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr *)&server, sizeof(server)); // 发数据if (n > 0){struct sockaddr_in temp;socklen_t len = sizeof(temp);char in_buffer[1024];int m = recvfrom(sockfd, in_buffer, sizeof(in_buffer)-1, 0, (struct sockaddr *)&temp, &len); //收数据if (m > 0){in_buffer[m] = 0;std::cout << in_buffer << std::endl;}else{break;}}else{break;}}return 0;
}
注意事项
- 若服务器和客户端在同一台机器上,通信的过程不会走到网络里,这样保证双方的软件内部不会出错,测试通过再引入跨网络通信。
- 云服务器绑定IP比较特殊,服务端不能直接(也强烈不建议)bind自己的公网IP:
因为云服务器的共享IP是虚拟出来的,服务器上本身ip就没有101.200.125.68这个IP。
下面这两个才是真正的服务器IP。
内网IP是可以绑定的:
但绑定了内网IP,是无法从外网上收消息了。
为了解决上面的两种情况,一般把云服务的IP地址设为0:
0代表的是:可以让服务器bind任意IP。
举个例子:比如服务器配了俩共享IP,IP1和IP2,端口号:8888,那么这个服务器可以收到IP1:8888的报文,也可以收到IP2:8888的报文:
绑定的IP为0的服务器,就能收到端口号相同的任何IP报文了。
整体的代码与效果
Log.hpp 用来检查用的日志,直接调用LOG(level,内容);即可
#pragma once#include <iostream>
#include <unistd.h>
#include <time.h>
#include <cstdarg>
#include <pthread.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "LockGuard.hpp"#define SCREEN_TYPE 1
#define FILE_TYPE 2namespace log_ns
{std::string glogfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;enum{DEBUG = 1,INFO,WARNING,ERROR,FATAL};std::string LevelToString(int level) // 日志等级{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "Error";case FATAL:return "FATAL";default:return "UNKONE";}}std::string GetCurrTime() // 目前的时间{time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",curr_time->tm_year + 1900,curr_time->tm_mon + 1,curr_time->tm_mday,curr_time->tm_hour,curr_time->tm_min,curr_time->tm_sec);return buffer;}class Logmessage // 日志的内容{public:std::string _level;pid_t _pid;std::string _filename;int _filenumber;std::string _time;std::string _messageinfo;};class Log{public:Log(const std::string &logfile = glogfile) : _logfile(logfile), _type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const Logmessage &lg){printf("[%s][%d][%s][%d][%s][%s]\n",lg._level.c_str(),lg._pid,lg._filename.c_str(),lg._filenumber,lg._time.c_str(),lg._messageinfo.c_str());}void FlushLogToFile(const Logmessage &lg){char logtxt[2048];snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s][%s]\n",lg._level.c_str(),lg._pid,lg._filename.c_str(),lg._filenumber,lg._time.c_str(),lg._messageinfo.c_str());int fd = open(_logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd < 0)return perror("open file false");write(fd, logtxt, strlen(logtxt));close(fd);}void FlushLog(const Logmessage &lg) // 判断刷新到到屏幕还是文件{if (lg._level == "DEBUG")return;LockGuard lockguard(&glock);switch (_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;default:printf("_type error \n");break;}}void LogMessage(std::string filename, int filenumber, int level, const char *format, ...) // 刷新的内容{Logmessage lg;lg._level = LevelToString(level);lg._pid = getpid();lg._filename = filename;lg._filenumber = filenumber;lg._time = GetCurrTime();va_list ap;va_start(ap, format);char log_info[1024];vsnprintf(log_info, sizeof(log_info), format, ap);va_end(ap);lg._messageinfo = log_info;FlushLog(lg);}~Log(){}private:int _type; // 打印到屏幕还是文件中std::string _logfile; // 哪个文件出了问题};Log lg;#define LOG(level, format, ...) \do \{ \lg.LogMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); \} while (0)
#define EnableScrean() \do \{ \lg.Enable(SCREEN_TYPE); \} while (0)
#define EnableFILE() \do \{ \lg.Enable(FILE_TYPE); \} while (0)
}
NoCopy.hpp 单例模式
#pragma onceclass nocopy
{
public:nocopy(){}~nocopy(){}nocopy(const nocopy&) = delete;const nocopy& operator =(const nocopy& ) = delete;
};
UdpServer.hpp:
#pragma once#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"using namespace log_ns;enum
{SOCKET_ERROR = 1,BIND_ERROR,
};int gsockfd = -1;
uint16_t glocalport = 8888;// UdpServer user(8899);
class UdpServer : public nocopy
{
public:UdpServer(uint16_t localport = glocalport): _sockfd(gsockfd),_localport(localport),_isrunning(false){}void InitServer(){// 1.创建socket文件_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "create socket error");exit(SOCKET_ERROR);}// LOG(DEBUG, "create socket success,sockfd :%d", _sockfd);// 2.bindstruct sockaddr_in local;memset(&local, 0, sizeof(local)); // 置空local.sin_family = AF_INET;local.sin_port = htons(_localport); // 1.网络序列local.sin_addr.s_addr = INADDR_ANY;int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error");exit(BIND_ERROR);}LOG(INFO, "socket bind success");}void Start(){_isrunning = true;char buffer[1024];while (_isrunning){struct sockaddr_in peer;socklen_t len = sizeof(peer);int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (n > 0) // 返回收到的信息{std::string ip = inet_ntoa(peer.sin_addr); // 4字节-》stringuint16_t port = htons(peer.sin_port); // 网络转主机buffer[n] = 0;std::cout << "[" << ip << ":" << port << "]#" << buffer << std::endl;// LOG(DEBUG, "recvfrom return is :%d", n);std::string echo_server = "[udp_server echo] #";echo_server += buffer;LOG(DEBUG, "echo_server :%s", echo_server.c_str());sendto(_sockfd, echo_server.c_str(), sizeof(echo_server), 0, (struct sockaddr *)&peer, len);}}}~UdpServer(){if (_sockfd > gsockfd)::close(_sockfd);}private:int _sockfd;uint16_t _localport;bool _isrunning;
};
UdpServerMain.cc
#include "UdpServer.hpp"
#include <memory>// ./udp_server local-port
// ./udp_server 8888
int main(int argc, char *argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << "server-port" << std::endl;}EnableScrean();uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port);usvr->InitServer();usvr->Start();return 0;
}
UdpClientMain.cc
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"using namespace log_ns;// ./udp_client server-ip server-port
// ./udp_client 127.0.0.1 8888
int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << "server-ip server-port" << std::endl;exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 创建socketint sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "create socket error" << std::endl;exit(1);}struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_port = htons(serverport);while (1){std::string line;std::cout << "Please Enter# ";getline(std::cin, line);LOG(DEBUG,"%s",line.c_str());int n = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr *)&server, sizeof(server)); // 发数据// LOG(DEBUG,"sendto return is %d",n);if (n > 0){struct sockaddr_in temp;socklen_t len = sizeof(temp);char in_buffer[1024];int m = recvfrom(sockfd, in_buffer, sizeof(in_buffer)-1, 0, (struct sockaddr *)&temp, &len);if (m > 0){in_buffer[m] = 0;std::cout << in_buffer << std::endl;}else{break;}}else{break;}}return 0;
}
效果
七.网络命令
Ping 命令
检查网络连通性。
问题:在Windows电脑上有一个云服务器,如何保证电脑与云服务器连通呢?(比如windows没有连网,没法连)
答:Ping命令就算检查两台主机是否能联通。
测试是否能连通网络,连通百度:
未来写了一种网络服务,比如写了一个TCP的服务,请求时怎样都拿不到结果,可以先拿Ping命令测试网络是否连通,若连通,说明网络服务本身就有问题。
- Ping命令一旦启动,就不会停止的,若只想Ping 1次,就有了选项-c(cont的意思)
netstat 命令
netstat时一个用来查看网络状态的重要工具。
netstat通常用来查看网络服务,UDP/TCP启动起来就是一个进程,我们能用ps能查到该进程偏于进程方面的信息,若想查看网络方面的属性的字段,就用netstat:
-u | 查看udp服务 |
---|---|
-t | 查看TCP服务 |
-a | 查看所有的服务 |
-p | 查看与哪个进程关联 |
-n | 把能显示成数字的,显示成数字 |
-l | 把处于LISTEN状态的TCP服务显示出来 |
watch命令可以每隔x秒让该命令执行一次:
watch -n 1 netstat -nuap #每隔一秒执行一次
pidof 命令
拿到进程的PID。
服务器有时候处于后端,简单的ctrl+c是杀不死的,所以需要kill命令。
经常会这么用:
pidof [进程名] | xargs kill -9
xargs的作用:把管道中的数据,转换成后续的数据。
kill是用标准输入文件描述符0,来把数据读到kill中。
相关文章:
Linux网络基础(协议 TCP/IP 网络传输基本流程 IP VS Mac Socket编程UDP)
文章目录 一.前言二.协议协议分层分层的好处 OSI七层模型TCP/IP五层(或四层)模型为什么要有TCP/IP协议TCP/IP协议与操作系统的关系(宏观上是如何实现的)什么是协议 三.网络传输基本流程局域网(以太网为例)通信原理MAC地址令牌环网 封装与解包分用 四.IP地址IP VS Mac地址 五.So…...
MFC: 控件根据文本内容大小自动调整
背景: 针对不同语言下,控件显示不全的现象; 例如: 现象1:中文下显示全部信息,英语下只能显示部分文字 现象2:中文下显示不全## 实现思路: 控件绑定按钮计算控件文本长度根据文本长…...
记一次线上Tomcat服务内存溢出的问题处理
背景:JavaWeb项目部署在Tomcat服务器上,服务器用的Windows。 问题表现:系统出现偶发性无法访问(隔几天就会在早上无法访问) Tomcat的日志catalina中,有如下报错信息。 java.lang.OutOfMemoryError: GC ov…...
go设计模式
刘:https://www.bilibili.com/video/BV1kG411g7h4 https://www.bilibili.com/video/BV1jyreYKE8z 1. 单例模式 2. 简单工厂模式 代码逻辑: 原始:业务逻辑层 —> 基础类模块工厂:业务逻辑层 —> 工厂模块 —> 基础类模块…...
通往 AI 之路:Python 机器学习入门-语法基础
第一章 Python 语法基础 Python 是一种简单易学的编程语言,广泛用于数据分析、机器学习和人工智能领域。在学习机器学习之前,我们需要先掌握 Python 的基本语法。本章将介绍 Python 的变量与数据类型、条件语句、循环、函数以及文件操作,帮助…...
【再谈设计模式】备忘录模式~对象状态的守护者
一、引言 在软件开发过程中,我们常常会遇到需要保存对象状态以便在之后恢复的情况。例如,在文本编辑器中,我们可能想要撤销之前的操作;在游戏中,玩家可能希望恢复到之前的某个游戏状态。备忘录模式(Memento…...
算法:判断链表是否有环
/*** brief 判断链表是否有环* * 该函数使用快慢指针法来判断链表中是否存在环。* 快指针每次移动两步,慢指针每次移动一步。* 如果链表中存在环,那么快指针最终会追上慢指针;* 如果链表中不存在环,快指针会先到达链表末尾。* * p…...
Android Logcat 高效调试指南
工具概览 Logcat 是 Android SDK 提供的命令行日志工具,支持灵活过滤、格式定制和实时监控,官方文档详见 Android Developer。 基础用法 命令格式 [adb] logcat [<option>] ... [<filter-spec>] ... 执行方式 直接调用(通过ADB守…...
【数据结构与算法】Java描述:第一节:ArrayList顺序表
这篇文章我们自己实现一个顺序表, 从而更好的认识它。 一、顺序表的本质 顺序表的本质其实就是一个数组,但是在插入,查找与删除上,有些复杂,顺序表通过对方法进行封装,方便了使用。 二、自己的顺序表 2.…...
报错The default superclass, “jakarta.servlet.http.HttpServlet“(已经配置好tomcat)
报错报错DescriptionResourcePathLocationType The default superclass,“jakarta.servlet.http.HttpServlet”, according to the project’s Dynamic Web Module facet version (5.0), was not found on the Java Build Path. 解决办法: 根据错误信息࿰…...
在笔记本电脑上用DeepSeek搭建个人知识库
最近DeepSeek爆火,试用DeepSeek的企业和个人越来越多。最常见的应用场景就是知识库和知识问答。所以本人也试用了一下,在笔记本电脑上部署DeepSeek并使用开源工具搭建一套知识库,实现完全在本地环境下使用本地文档搭建个人知识库。操作过程共…...
数学建模:MATLAB极限学习机解决回归问题
一、简述 极限学习机是一种用于训练单隐层前馈神经网络的算法,由输入层、隐藏层、输出层组成。 基本原理: 输入层接受传入的样本数据。 在训练过程中随机生成从输入层到隐藏层的所有连接权重以及每个隐藏层神经元的偏置值,这些参数在整个…...
Immich自托管服务的本地化部署与随时随地安全便捷在线访问数据
文章目录 前言1.关于Immich2.安装Docker3.本地部署Immich4.Immich体验5.安装cpolar内网穿透6.创建远程链接公网地址7.使用固定公网地址远程访问 前言 小伙伴们,你们好呀!今天要给大家揭秘一个超炫的技能——如何把自家电脑变成私人云相册,并…...
Python标准库【os】5 文件和目录操作2
文章目录 8 文件和目录操作8.7 浏览目录下的内容8.8 查看文件或目录的信息8.9 文件状态修改文件标志位文件权限文件所属用户和组其它 8.10 浏览Windows的驱动器、卷、挂载点8.11 系统配置信息 os模块提供了各种操作系统接口。包括环境变量、进程管理、进程调度、文件操作等方面…...
相控阵雷达
相控阵雷达 **1. 基本概念与数学模型**(1) **阵列信号模型**(2) **波束形成原理** **2. 经典波束形成算法****(1) 常规波束形成(Conventional Beamforming, CBF)****(2) 自适应波束形成(Adaptive Beamforming)****2.1 最小方差无失…...
Java 大视界 -- 基于 Java 的大数据分布式缓存一致性维护策略解析(109)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
AI: Cursor是否已奠定AI开发环境的龙头地位?
近年来,人工智能(AI)在软件开发领域的应用迅速升温,而Cursor作为一款AI驱动的代码编辑器,凭借其创新功能和市场表现,引发了广泛讨论。许多人认为,Cursor已经奠定了AI开发环境的龙头地位。然而&a…...
PHP:IDEA开发工具配置XDebug,断点调试
文章目录 一、php.ini配置二、IDEA配置 一、php.ini配置 [xdebug] zend_extension"F:\wamp64\bin\php\php7.4.0\ext\php_xdebug-2.8.0-7.4-vc15-x86_64.dll" xdebug.remote_enable on xdebug.remote_host 127.0.0.1 xdebug.remote_port 9001 xdebug.idekey"…...
回忆Redis的持久化机制
Redis的持久化机制 前言RDB触发方式配置方式手动触发bgsave的执行流程 缺点 AOF重写机制触发方式手动触发自动触发 MP-AOF RDB和AOF混合模式 前言 大家都知道,Redis是内存数据库,也就是说client与Redis交互的过程,无论是读key还是写key都是直…...
partner‘127.0.0.1:3200‘ not reached
在SAP虚拟机中,如果LRPSAP 0显示黄色,通常表示服务启动异常或存在配置问题。以下是一些可能的处理方法: 检查主机文件配置 确保主机文件(hosts)中已正确配置SAP服务的域名解析。例如,添加以下内容到hosts文…...
网络配置的基本信息
目录 一、网络接口信息 1、关闭虚拟化服务 2、配置临时IP 3、配置静态IP 4、常见网络命令 5、安装Wireshark 一、网络接口信息 输入 ip address,会出现下面的内容 网卡名称及其含义: 网卡名称说明lo 表示本地回环地址。 ens32 有线网卡,…...
SpringBoot集成Mybatis(包括Mybatis-Plus)和日志
一、使用Mybatis 1.添加依赖 <!--Mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version> <!-- 选择与Java 8兼容的版本 --&g…...
如何在 IntelliJ IDEA 中集成 DeepSeek
如何在 IntelliJ IDEA 中集成 DeepSeek 在本教程中,我们将带您一步步完成将 DeepSeek 集成到 IntelliJ IDEA 中的过程。通过此集成,您可以在IDE中利用DeepSeek强大的功能,提高开发工作效率。 步骤 1:安装 Proxy AI 插件 首先&a…...
【自学笔记】大数据基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 大数据基础知识点总览1. 大数据概述2. 大数据处理技术3. 数据仓库与数据挖掘4. 大数据分析与可视化5. 大数据平台与架构6. 大数据安全与隐私 总结 大数据基础知识点…...
AWS ALB 实现灰度验证指南:灵活流量分配与渐进式发布
AWS Application Load Balancer (ALB) 作为七层负载均衡器,支持基于权重或内容的路由策略,是实施灰度验证(金丝雀发布)的核心工具。通过将部分流量导向新版本后端,可以安全验证功能稳定性。以下是使用 ALB 实现灰度发布的详细方案。 © ivwdcwso (ID: u012172506) 一…...
专线物流公共服务平台:全面提升专线物流效率
专线物流公共服务平台:全面提升专线物流效率 在物流行业高速发展的今天,专线物流作为物流供应链的重要环节,面临着效率低下、成本高企、信息孤岛等痛点。临沂呆马区块链网络科技有限公司(简称“呆马科技”)凭借其在大…...
行为型模式 - 观察者模式 (Publish/Subscribe)
行为型模式 - 观察者模式 (Publish/Subscribe) 又称作为订阅发布模式(Publish-Subscribe Pattern)是一种消息传递模式,在该模式中,发送者(发布者)不会直接将消息发送给特定的接收者(订阅者&…...
HTTP/2 服务器端推送:FastAPI实现与前端集成指南
HTTP/2 服务器端推送:FastAPI实现与前端集成指南 注意:本文末尾附有完整示例代码,文中仅展示核心关键代码。完整代码可在GitHub仓库获取。 本文将会讲解HTTP2协议和相关配置实践。但是不要混淆,SSE的实现完全基于HTTP/1.1的持久连…...
C++ 变量的输入输出教程
一、变量的基本概念 在 C 中,变量是用于存储数据的命名内存位置。在使用变量之前,需要先声明它的类型和名称,这样编译器才能为其分配适当大小的内存空间。例如: int age; // 声明一个整型变量 age double salary; // 声明一个…...
java作业
java作业 一. package shiyanbaogao; import java.util.Scanner; //给20块钱买可乐,每瓶可乐3块钱,喝完之后退瓶子可以换回1块钱,问最多可以喝到多少瓶可乐。请设计相应的Java程序。 public class BaoGaoDemo02 {public static void …...
LeeCode题库第四十题
40.组合总和II 项目场景: 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意:解集不能包含重复的组合。 示…...
list的模拟实现
目录 一、构造和扩容机制 二、普通迭代器 三、const迭代器 四、tip 有了前面vetcor的基础呢,我们在学习和使用list上就更加的方便快捷,浅显易懂了,所以相似的部分我就不做过多的言语阐述了,在使用方面呢,大家可以学…...
pandas DataFrame 数据筛选与排序
数据筛选: df[df[列标签] > xxx] 使用 &(与) |(或) 拼接多个条件代码应用: &(与)应用 # 引用 pandas import pandas as pd # 定义数据 data {"产品":["男装","女装","男鞋","女鞋"…...
elpis全栈课程学习之elpis-core学习总结
elpis全栈课程学习之elpis-core学习总结 核心原理 elpis-core是全栈框架elpis的服务端内核,主要应用于服务端接口的开发以及页面的SSR渲染,elpis-core基于约定优于配置的原理,通过一系列的loader来加载对应的文件,大大节约用户的…...
零基础deep seek+剪映,如何制作高品质的视频短片
以下是专为零基础学习者设计的 剪映专业版详细教程+Deep seek配合制 ,包含从入门到精通的系统化教学,配合具体操作步骤与实用技巧: 基于DeepSeek与剪映协同制作高品质视频短片的专业流程指南(2025年最新实践版&#x…...
解决单元测试 mock final类报错
文章目录 前言解决单元测试 mock final类报错1. 报错原因2. 解决方案3. 示例demo4. 扩展 前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。 而且听说点赞的人每天的运气都不会太差࿰…...
Git基本命令索引
GIT基本命令索引 创建代码库修改和提交代码日志管理远程操作操作分支 创建代码库 操作指令初始化仓库git init克隆远程仓库git clone 修改和提交代码 操作指令查看文件状态git status文件暂存git add文件比较git diff文件提交git commit回滚版本git reset重命名或者移动工作…...
非平稳时间序列分析(二)——ARIMA(p, d, q)模型
此前篇章(平稳序列): 时间序列分析(一)——基础概念篇 时间序列分析(二)——平稳性检验 时间序列分析(三)——白噪声检验 时间序列分析(四)—…...
[代码规范]接口设计规范
一个优雅的接口要如何设计?有哪些设计规范可以遵循? 下面抛砖引玉,分享一些规范。 目录 1、RESTful API 设计最佳实践 2、Shneiderman 的 8 条黄金法则 3、Nielsen 的 10 条启发式规则 1、RESTful API 设计最佳实践 一共18条,参考…...
4-3自定义加载器,并添加功能
一、自定义类加载器的实现步骤 继承ClassLoader类 自定义类加载器需继承java.lang.ClassLoader,并选择性地重写以下方法: findClass(String name):核心方法,用于根据类名查找并加载类的字节码。需从自定义路径(…...
北京大学DeepSeek提示词工程与落地场景(PDF无套路免费下载)
近年来,大模型技术飞速发展,但许多用户发现:即使使用同一款 AI 工具,效果也可能天差地别——有人能用 AI 快速生成精准方案,有人却只能得到笼统回答。这背后的关键差异,在于提示词工程的应用能力。 北京大…...
SSH密码更改
Windows User目录下的.ssh/config,全部删除 linux 在主用户文件夹,ctrlh显示隐藏文件。删除.shh文件夹内所有文件。...
蓝桥备赛(四)- 数组(下)
一 、 字符数组 1.1 介绍 数组的元素如果是字符类型 , 这种数组就是字符数组 , 字符数组可以是一维数组 , 可以是二维数组 (多维数组)。 接下来主要讨论一维的字符数组 : char arr1[5] //一维数组 char arr2[3][5] // 二维数组 C语言 中…...
基金 word-->pdf图片模糊的解决方法
1. 首先需要Adobe或福昕等pdf阅读器。 2. word中 [文件]--[打印],其中打印机选择pdf阅读器,例如此处我选择福昕阅读器。 3. 选择 [打印机属性]--[编辑]--[图像],将所有的采样、压缩均设置为 关闭。点击[另存为],保存为 基金报告…...
身为小兵,如何提升不可替代性?
之前聊过,研发、PIE、PE、可靠性等岗位,主要是对物的工作, 这类岗位,如何提升不可替代性? 我的经验是,学会识别创造性工作or重复性工作。 尽可能地做创造性工作,推重复性工作。 销售、采购、HR等岗位,主要是对人的工作, 这类岗位,如何提升不可替代性? 我的思考…...
easyExcel使用案例有代码
easyExcel 入门,完成web的excel文件创建和导出 easyExcel官网 EasyExcel 的主要特点如下: 1、高性能:EasyExcel 采用了异步导入导出的方式,并且底层使用 NIO 技术实现,使得其在导入导出大数据量时的性能非常高效。 2、易于使…...
linux服务器更新jar包脚本
【需求】Java每次发布新的版本都需要先kill掉原来的服务,然后再启动新的包 有了这个脚本只需要把包替换掉,服务会自动kill 以8184 为例 完整的脚本如下 #!/bin/bash# 检查端口 8184 是否被占用 PORT8184 PID$(lsof -t -i:$PORT)if [ -n "$PID…...
Tomcat 乱码问题彻底解决
1. 终端乱码问题 找到 tomcat 安装目录下的 conf —> logging.properties .修改ConsoleHandler.endcoding GBK (如果在idea中设置了UTF-8字符集,这里就不需要修改) 2. CMD命令窗口设置编码 参考:WIN10的cmd查看编码方式&…...
dify绑定飞书多维表格
dify 绑定飞书和绑定 notion 有差不多的过程,都需要套一层应用的壳子,而没有直接可以访问飞书文档的 API。本文记录如何在dify工具中使用新增多条记录工具。 创建飞书应用 在飞书开放平台创建一个应用,个人用户创建企业自建应用。 自定义应…...
深入浅出:插入排序算法完全解析
1. 什么是插入排序? 插入排序(Insertion Sort)是一种简单的排序算法,其基本思想与我们整理扑克牌的方式非常相似。我们将扑克牌从第二张开始依次与前面已排序的牌进行比较,将其插入到合适的位置,直到所有牌…...