[计算机网络]---网络编程套接字

前言

作者:小蜗牛向前冲

名言:我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 

目录

 一、基础知识

1、源IP地址和目的IP地址

 2、端口号

二、网络套接字

1、网络字节序

2、socket编程接口 

三、基于tcp协议的网络通信 

1、服务器的编写 

 2、客户端的编写

3、日志报告的编写 

 四、TCP协议通讯流程

1、通信流程

2、三次握手和四次挥手 


本期学习:网络基础知识,网络套接字,基于tcp协议的网络编程,tcp协议的三次握手和四次挥手。

 一、基础知识

1、源IP地址和目的IP地址

源IP地址:

  • 源IP地址是指发起网络通信的设备或主机的IP地址
  • 在TCP/IP协议中,源IP地址用于标识数据包的来源,使得接收方知道从哪里收到数据
  • 源IP地址包含在网络数据包的IP头部中。

目的IP地址:

  • 目的IP地址是指网络通信的目标设备或主机的IP地址
  • 在TCP/IP协议中,目的IP地址用于指定数据包的目标,确保数据包被传递到正确的位置。
  • 目的IP地址同样包含在网络数据包的IP头部中。

下面我们用唐僧取经的例子来理解:

唐僧到女儿国,那国王问,高僧从那来,到哪里去 ,在网络中就是问源ip和目的ip。

也可能会问高僧上一站从那来,下一站到哪里去。在网络中指的就是MAC地址

MAC地址:

MAC地址(Media Access Control address),也称为物理地址或硬件地址,是网络通讯中用于唯一标识网络接口控制器(NIC,网络接口卡)的一个地址。每个网络设备的NIC都有一个全球唯一的MAC地址,这个地址在生产时被固化在硬件中

MAC地址的长度通常是48位(6个字节),有时也表示为64位以适应某些特定技术标准。

 MAC地址通常以十六进制数表示,每个字节之间用冒号(:)或者破折号(-)分隔,例如00:1A:2B:3C:4D:5E00-1A-2B-3C-4D-5E

 2、端口号

      我们光有IP地址就可以完成通信了嘛? 想象一下发qq消息的例子, 有了IP地址能够把消息发送到对方的机器上, 但是还需要有一个其他的标识来区分出, 这个数据要给哪个程序进行解析。所以就提出用端口号来标识唯一的程序(服务器)。

端口号(port)是传输层协议的内容:

  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
  • 一个端口号只能被一个进程占用

问题1: 我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么这 两者之间是怎样的关系?


a:系统是系统,网络是网络,这里可以达到解耦的效果

b: 需要客户端每次都能找到服务端,而pid是在进程每次生成时随机分配的

c:不是所以的网络进程都需要网络提供网络服务或者请求,但是所以的进程都需要用pid进行标识

所以说:一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定


注意:

  • 端口号范围从0到65535,其中0到1023是被知名服务占用的端口号,称为“系统端口”或“保留端口”,而1024到65535是动态或私有端口,用于一般应用程序或自定义服务(下面我们进行的测试常用8080端口)
  • 传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 "数据是谁发的, 要 发给谁"。

二、网络套接字

1、网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏 移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢? 

在网络中无论是发送主机还是接收主机都是将:缓冲区中的数据按内存地址从低到高的顺序发送或者接收

因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址

TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节

也就是说不管这台主机是大端机还是小端机,到会按照大端机发送。

库函数做网络 字节序和主机字节序的转换。 

#include<arpa/inet.h>uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  •  这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回; 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回.

2、socket编程接口 

API(Application Programming Interface)是一组定义在软件中不同组件之间交互的规范和工具。API可以看作是一座桥梁,它定义了如何访问或使用软件组件的方法。在软件开发中,API允许不同的程序部分之间进行通信,以便它们能够相互协作而无需详细了解彼此的内部实现。

常见的API 

// 创建 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 结构(有时候在不同的系统中会有稍微不同的变体,如 sockaddr_in)是用于表示套接字地址信息的数据结构,在网络编程中经常会遇到。它通常用于指定网络通信中的端点地址,包括 IP 地址和端口号。

struct sockaddr {unsigned short sa_family;    // 地址族,如 AF_INET(IPv4)或 AF_INET6(IPv6)char sa_data[14];            // 地址数据
};

主要的字段:

  1. sa_family:用于指定地址族,表示地址的类型。例如,AF_INET 表示 IPv4 地址族,AF_INET6 表示 IPv6 地址族等。这个字段是一个无符号短整型(unsigned short)。

  2. sa_data:包含地址的具体数据,通常用于存储 IP 地址和端口号等信息。在不同的地址族下,这个字段的内容会有所不同。

在实际使用中,为了更方便地表示 IPv4 地址,通常会使用更具体的套接字地址结构,如 sockaddr_in,它的定义如下: 

struct sockaddr_in {short int sin_family;          // 地址族,如 AF_INETunsigned short int sin_port;   // 端口号struct in_addr sin_addr;       // IPv4 地址unsigned char sin_zero[8];     // 未使用的填充字段
};

三、基于tcp协议的网络通信 

上面我们说了怎么多,下面我们就用起来,虽然我们现在还是那么清楚udp和tcp协议,但是我大概清楚了他是用来通信的。

这里我们要实现一个简单版本的服务器为客户端提供服务,这里我们的服务仅仅需要回显信息。

我们要写一个服务器

tcpServer.hpp
tcpServer.cc

一个客户端

tcpClient.hpp
tcpClinet.cc

一个日志报告

log.hpp

1、服务器的编写 

tcpServer.hpp:这里我们要完成服务器的初始化,启动和销毁。

服务器的初始化是通过socket创建套嵌字,bind绑定网络,在进行listen监听.

启动要完成accept获取新链接,在执行程序任务。

这里我们在执行任务的过程中,我们可以:

  • 让程序自己执行
  • 创建子进程执行
  • 让多线程执行
  • 让线程池执行

这里为了让程序更好的理解,我们就让子进程执行我们serverio。 

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"namespace server
{enum{USAGE_ERR = 1, // usage_errSOCKET_ERR,    // sockft_errBIND_ERR,      // bind_errLISTEN_ERR     // listen_err};static const uint16_t gport = 8080;static const int gbacklog = 5;class TcpServer; // 这是一个前置声明,我们在类tcpServer中定义了ThreadData数据的类防止出现循环依赖class ThreadData{public:ThreadData(TcpServer *self, int sock) : _self(self), _sock(sock){}public:TcpServer *_self;int _sock;};class TcpServer{public:TcpServer(const uint16_t &port = gport) : _listensock(-1), _port(port){}void initServer(){// 1 创建套接字_listensock = socket(AF_INET, SOCK_STREAM, 0); // sock_streamif (_listensock < 0){logMessage(FATAL, "create error socket");exit(SOCKET_ERR);}logMessage(NORMAL, "create socket success:%d", _listensock);// 2 bind自己的网络信息struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY; // inaddr_anyif (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "bind error ");exit(BIND_ERR);}logMessage(NORMAL, "bind socket success");// 3 设置socket 为监听状态if (listen(_listensock, gbacklog) < 0){logMessage(FATAL, "listen socket success");exit(LISTEN_ERR);}logMessage(NORMAL, "listen socket success");}void start(){for (;;){// 4 server获取链接//  sock, 和client进行通信的fdstruct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr *)&peer, &len);if (sock < 0){logMessage(ERROR, "accept error, next");continue;}logMessage(NORMAL, "accept a new link success, get new sock: %d", sock);// 5. 这里就是一个sock,未来通信我们就用这个sock,面向字节流的,后续全部都是文件操作!// version1// serviceIO(sock);// close(sock);// version2多进程版本pid_t id = fork();// 我们让子进程执行的io任务,父进程什么都不做,回收子进程就好if (id == 0){close(_listensock);serviceIO(sock);close(sock);exit(0);}close(sock);// 父进程pid_t ret = waitpid(id, nullptr, 0);if (ret > 0){std::cout << "waitsuccess: " << ret << std::endl;}// version3多线程// pthread_t tid;// ThreadData *td = new ThreadData(this, sock);// pthread_create(&tid, nullptr, threadRoutine, td);// pthread_join(tid, nullptr);// version4 线程池// ThreadPool<Task>::getInstance()->push(Task(sock, serviceIO));}}// static void *threadRoutine(void *args)// {//     pthread_detach(pthread_self());//     ThreadData *td = static_cast<ThreadData *>(args);//     serviceIO(td->_sock);//     close(td->_sock);//     delete td;//     return nullptr;// }~TcpServer(){}private:int _listensock; // 用了监听uint16_t _port;};
}

下面是一些任务:

#pragma once#include <iostream>
#include <string>
#include <cstdio>
#include <functional>using namespace std;void serviceIO(int sock)
{char buffer[1024];while (true){ssize_t n = read(sock, buffer, sizeof(buffer) - 1);if (n > 0){// 把读到的数据当做字符串buffer[n] = 0;cout << "recv message" << buffer << endl;// 信息返回string outbuffer = buffer;outbuffer += buffer;outbuffer += "server[echo]";// outbuffer.c_str(),将字符串类型的指针,转换为字符类型的指针write(sock, outbuffer.c_str(), sizeof(outbuffer) - 1);}else if (n == 0){// client退出logMessage(NORMAL, "client quit,me to");break;}}
}class Task
{// using 用于创建类型别名using func_t = function<void(int)>;public:Task(){}Task(int sock, func_t func): _sock(sock), _callback(func){}void operator()(){_callback(_sock);}~Task(){}private:int _sock;func_t _callback;
};

tcpServer.cc:主程序的执行

#include "tcpserver.hpp"
#include "daemon.hpp"
#include <memory>using namespace server;
using namespace std;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}// tcp服务器,启动上和udp server一模一样
// ./tcpserver local_port
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);unique_ptr<TcpServer> tsvr(new TcpServer(port));tsvr->initServer();// daemonSelf();tsvr->start();// daemonSelf();return 0;
}

 2、客户端的编写

tcpclient.hpp:对于客户端的初始化,我们仅仅只是需要创建套接字,而bind操作系统会帮助我们完成。

客户端的启动我们要用connect接收链接

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define NUM 1024
using namespace std;
class tcpclient
{
public:tcpclient(const string &serverip, const uint16_t &serverport): _sock(-1), _serverip(serverip), _serverport(serverport){}void initclient(){// 1 创建套接字_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){std::cerr << "socket create error" << std::endl;exit(2);}}void start(){struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(_serverport);server.sin_addr.s_addr = inet_addr(_serverip.c_str());if (connect(_sock, (struct sockaddr *)&server, sizeof(server)) != 0){std::cerr << "socket connect error" << std::endl;}else{string msg;while (true){cout << "Enter#";std::getline(std::cin, msg);write(_sock, msg.c_str(), sizeof(msg));char buffer[NUM];int n = read(_sock, buffer, sizeof(buffer));if (n > 0){// 目前我们把读到的数据当成字符串, 截止目前buffer[n] = 0;std::cout << "Server回显# " << buffer << std::endl;}elsebreak;}}}~tcpclient(){if (_sock >= 0)close(_sock);}private:int _sock;std::string _serverip;uint16_t _serverport;
};

tcpcline.cc:执行程序 

#include "tcpclient.hpp"
#include <memory>using namespace std;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}
// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);unique_ptr<tcpclient> tcli(new tcpclient(serverip, serverport));tcli->initclient();tcli->start();return 0;
}

3、日志报告的编写 

对于日志报告我们通过对错误进行分类,进行不同等级的日志错误信息输入。

#pragma once#include <iostream>
#include <cstdio>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>// 定义错误类型
#define DEBUG 0   // debug
#define NORMAL 1  // normal
#define WARNING 2 // warning
#define ERROR 3   // error
#define FATAL 4   // fatal致命#define NUM 1024// 定于不同类型日常写入的文件
#define LOG_NORMAL "log.txt"
#define LOG_ERR "log.error"
// 标志位
const char *to_levelstr(int level)
{switch (level){case DEBUG:return "DEBUG";case NORMAL:return "NORMAL";case WARNING:return "NORMAL";case ERROR:return "ERROR";case FATAL:return "FATAL";}
}// 日志信息
void logMessage(int level, const char *format, ...) // 可变参数函数
{// [日志等级] [时间戳/时间] [pid] [messge]// [WARNING] [2023-05-11 18:09:08] [123] [创建socket失败]char logprefix[NUM]; // 存放日志前缀snprintf(logprefix, sizeof(logprefix), "[%d][%ld][pid::%d]",to_levelstr(level), (long int)time(nullptr), getpid());char logcontent[NUM];va_list arg;snprintf(logprefix, sizeof(logprefix), format, arg);FILE *log = fopen(LOG_NORMAL, "a"); // log_normalFILE *err = fopen(LOG_ERR, "a");    // log_errorif (log != nullptr && err != nullptr){FILE *curr = nullptr;if (level == DEBUG || level == NORMAL || level == WARNING)curr = log;if (level == ERROR || level == FATAL)curr = err;if (curr)fprintf(curr, "%s%s\n", logprefix, logcontent);fclose(log);fclose(err);}
}

程序运行测试:

 

 四、TCP协议通讯流程

1、通信流程

TCP协议的通讯流程可以分为以下几个步骤:

  1. 创建连接:客户端和服务端都需要创建一个套接字(socket),这通常涉及到系统调用如`socket()`。

  2. 连接请求:客户端向服务端的套接字发送连接请求报文,这一步可以通过`connect()`函数来实现。

  3. 服务器响应:服务器收到客户端的连接请求后,需要进行确认,以确定是否允许连接。这通常涉及三部分握手:

    • 服务器的确认ACK
    • 客户端的确认SYN
    • 服务器的应答ACK
  4. 连接建立:在双方完成了三次握手后,连接正式建立。此时,客户端可以开始发送数据给服务器,而服务器也可以开始接收数据。

  5. 数据传输:在这个阶段,客户端和服务器端可以进行数据的读写操作。这些操作包括读取(`recv()`)、写入(`send()`)以及关闭连接(`close()`)。

  6. 断开连接:当通信结束后,客户端或服务器端可以选择断开当前的连接。这通常涉及到四次挥手:

    • 服务器的释放FIN
    • 客户端的确认ACK
    • 服务器的确认ACK
    • 客户端的释放ACK
  7. 错误处理:在通信过程中,可能会遇到各种错误,如数据包丢失或乱序。TCP提供了自动重传机制来处理这些问题。

2、三次握手和四次挥手 

TCP三次握手(Three-way Handshake):

  1. 客户端发送连接请求(SYN): 客户端向服务器发送一个特殊的TCP报文段,该报文段中设置了SYN(同步)标志位,表明客户端要求建立连接,并指明初始序列号(ISN)

  2. 服务器确认请求并发送自己的连接请求(SYN + ACK): 服务器接收到客户端的连接请求后,向客户端发送一个确认报文段。该报文段中既包含了确认序号(ACK),也设置了SYN标志位,表示服务器同意建立连接,并指明自己的初始序列号。

  3. 客户端确认服务器的连接请求(ACK): 客户端接收到服务器的确认报文后,会再次向服务器发送一个确认报文段,其中确认号字段会加1,表示客户端也同意建立连接。

 TCP四次挥手(Four-way Handshake)

  1. 发起关闭请求: 通信的一方(称为主动关闭方)发送一个FIN报文段,表示它已经完成了数据的发送任务。

  2. 接收关闭请求并发送确认: 接收到关闭请求的一方(被动关闭方)收到FIN报文后,会发送一个确认报文,表明它已经接收到关闭请求。

  3. 发送数据并发起关闭请求: 被动关闭方发送完所有数据后,也会向主动关闭方发送一个FIN报文段,表示它已经完成了数据的发送任务,并且准备关闭连接。

  4. 确认关闭请求并发送关闭确认: 主动关闭方接收到被动关闭方的FIN报文后,会发送一个确认报文,表明它已经接收到了关闭请求。一旦被动关闭方收到了这个确认,连接就会关闭。

在linux下有许多命令,老是忘记,在下面的文章中多会为大家分享一些小命令: 

Linux小命令 :

netstat 是一个用于显示网络状态信息的命令行工具,常用于查看网络连接、路由表、接口统计等信息。在给定的命令中,-nltp 参数的含义如下:

  • -n: 显示数字形式的地址和端口号,而不进行域名和服务名称的解析。
  • -l: 仅显示监听状态的连接。
  • -t: 仅显示TCP协议的连接。
  • -p: 显示与连接相关的进程标识符(PID)及进程名称。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑马程序员微信小程序学习总结7.工具脚本、自定义组件、data和properties的区别

目录 工具脚本&#xff08;utils中的wxs&#xff09;自定义组件组件的引用方式局部引用全局引用组件和页面的区别组件样式隔离data数据组件method事件处理函数自定义组件添加属性 data和properties的区别使用setData修改properties的值 工具脚本&#xff08;utils中的wxs&#…

基于java后台的微信校园教室图书馆座位预约小程序系统设计与实现

博主介绍&#xff1a;黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者&#xff0c;CSDN博客专家&#xff0c;在线教育专家&#xff0c;CSDN钻石讲师&#xff1b;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程&#xff…

电商小程序07显示用户个人信息

目录 1 全局变量2 控制登录按钮显示3 设置布局4 搭建我的页面5 修改个人信息总结 在登录章节我们已经实现了用户名和密码登录首页的功能&#xff0c;在登录之后&#xff0c;可以切换到我的页面&#xff0c;显示用户的头像和名称&#xff0c;可以修改个人信息。本篇我们介绍一下…

腾讯云游戏联机服务器配置价格表,4核16G/8核32G/4核32G/16核64G

2024年更新腾讯云游戏联机服务器配置价格表&#xff0c;可用于搭建幻兽帕鲁、雾锁王国等游戏服务器&#xff0c;游戏服务器配置可选4核16G12M、8核32G22M、4核32G10M、16核64G35M、4核16G14M等配置&#xff0c;可以选择轻量应用服务器和云服务器CVM内存型MA3或标准型SA2实例&am…

【MySQL】:分组查询、排序查询、分页查询、以及执行顺序

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. 分组查询1.1 语法1.2 where与having区别1.3 注意事项:1.4 案例: 二. 排序查询…

ClickHouse--04--数据库引擎、Log 系列表引擎、 Special 系列表引擎

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.数据库引擎1.1 Ordinary 默认数据库引擎1.2 MySQL 数据库引擎MySQL 引擎语法字段类型的映射 2.ClickHouse 表引擎3.Log 系列表引擎几种 Log 表引擎的共性是&#…

项目02《游戏-10-开发》Unity3D

【完成本集功能后共享1-10集整套代码】 基于 项目02《游戏-09-开发》Unity3D &#xff0c; 任务&#xff1a;传送至其他场景&#xff0c; 首先在场景中加入传送门&#xff0c; 设置人物标签&#xff0c; using UnityEngine; using UnityEngine.SceneManagement; u…

调用讯飞火星AI大模型WebAPI

调用讯飞火星AI大模型 记录一次调用讯飞AI大模型的过程 官方文档 首先&#xff0c;去官网申请资格&#xff0c;获得必要秘钥等 再编写url&#xff0c;该url存在编码要求&#xff0c;具体看官网url编写 具体代码如下&#xff1a; getWebsocketUrl() {return new Promise((resol…

腾讯云4核8G服务器多少钱?646元一年零3个月

腾讯云服务器4核8G配置优惠价格表&#xff0c;轻量应用服务器和CVM云服务器均有活动&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;轻量应用服务器4核8G12M带宽一年446元、529元15个月&#xff0c;腾讯云百科txybk.com分…

Pytorch的可视化

1 使用 wandb进行可视化训练过程 本文章将从wandb的安装、wandb的使用、demo的演示进行讲解。 1.1 如何安装wandb&#xff1f; wandb的安装比较简单&#xff0c;在终端中执行如下的命令即可&#xff1a; pip install wandb在安装完成之后&#xff0c;我们需要&#xff0c;去…

Springboot拦截器中跨域失效的问题、同一个接口传入参数不同,一个成功,一个有跨域问题、拦截器和@CrossOrigin和@Controller

Springboot拦截器中跨域失效的问题 一、概述 1、具体场景 起因&#xff1a; 同一个接口&#xff0c;传入不同参数进行值的修改时&#xff0c;一个成功&#xff0c;另一个竟然失败&#xff0c;而且是跨域问题拦截器内的request参数调用getHeader方法时&#xff0c;获取不到前端…

如何写好一个简历

如何编写求职简历 论Java程序员求职中简历的重要性 好简历的作用 在求职过程中&#xff0c;一份好的简历是非常重要的&#xff0c;它甚至可以直接决定能否被面试官认可。一份出色或者说是成功的个人简历&#xff0c;最根本的作用是能让看这份简历的人产生一定要见你的强烈愿…

Idea Git Review插件

idea git plugin 添加了一些常用的小插件 可以右键打开git bash窗口 可以右键选中文字点击baidu fanyi 可以通过搜索git用户名 指定开始时间查询某个版本自己提交的所有代码文件 可以通过点击蓝色行数&#xff0c;跳转到指定的改动代码块 资源地址&#xff1a; git-pl…

伦敦金是现货黄金吗?

伦敦金属现货黄金交易的一种形式。投资者可以通过伦敦金市场直接买卖黄金&#xff0c;以实现投资收益。伦敦金市场具有高度流动性和透明度&#xff0c;是全球投资者广泛参与的贵金属交易市场之一。 什么是现货黄金&#xff1f; 现货黄金是指实物黄金的交易&#xff0c;投资者可…

C/C++模板初阶

目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.1 函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 1. 泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int&…

百面嵌入式专栏(面试题)驱动开发面试题汇总 2.0

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将介绍驱动开发面试题 。 1、Linux系统的组成部分? Linux内核、Linux文件系统、Linux shell、Linux应用程序。 2、Linux内核的组成部分? (1)第一种分类方式:内存管理子系统、进程管理子系统、文件管理子系…

[C#]winform制作仪表盘好用的表盘控件和使用方法

【仪表盘一般创建流程】 在C#中制作仪表盘文案&#xff08;通常指仪表盘上的文本、数字或指标显示&#xff09;涉及到使用图形用户界面&#xff08;GUI&#xff09;组件&#xff0c;比如Windows Forms、WPF (Windows Presentation Foundation) 或 ASP.NET 等。以下是一个使用W…

【上海大学数字逻辑实验报告】七、中规模元件及综合设计

一、实验目的 掌握中规模时序元件的测试。学会在Quartus II上设计序列发生器。 二、实验原理 74LS161是四位可预置数二进制加计数器&#xff0c;采用16引脚双列直插式封装的中规模集成电路&#xff0c;其外形如下图所示&#xff1a; 其各引脚功能为&#xff1a; 异步复位输…

Microsoft Excel 加载数据分析工具

Microsoft Excel 加载数据分析工具 1. 打开 Excel&#xff0c;文件 -> 选项2. 加载项 -> 转到…3. 分析工具库、分析工具库 - VBA4. 打开 Excel&#xff0c;数据 -> 数据分析References 1. 打开 Excel&#xff0c;文件 -> 选项 2. 加载项 -> 转到… ​​​ 3…