网络编程套接字(一)
学习任务:
我们先来认识端口号,区分好主机IP和端口号的区别,以及涉及到进程PID和端口号的区别。
然后简单认识一下TCP协议和UDP协议,这两个协议都是传输层的。接着了解什么是网络字节序,它有什么作用。然后是网络编程的一些接口。最后写代码简单实践一下。
目录
1、认识端口号,区分IP/port,PID/port
2、认识TCP协议,认识UDP协议
3、认识网络字节序
4、socket编程接口
5、代码示例:能够实现一个简单的udp客户端/服务器
1、认识端口号,区分IP/port,PID/port
IP地址(公网IP)是用来唯一标识互联网中的一台主机,一台主机一个IP。而IP分源IP和目的IP,源IP和目的IP对一个报文来讲,是起从哪里来,到哪里去的作用,其最大的意义是指导报文该如何进行路径的选择,而路径中,每一个“站点”就是MAC地址的变化。
认识端口号port
数据从计算机A到达计算机B,并不是真正的目的,而是到达计算机B的某一个进程,提供数据处理的服务,才是网络传输数据最终的目的。
数据本身并不是由计算机产生的,而是由人,即用户通过特定的客户端等等输入进去的,因此本质上,所有的网络通信,站在人的角度上,就是人与人之间的通信,这是一个比较好的理解方向,站在计算机角度上,是进程间通信!只不过通信的进程不在一台计算机上。就比如抖音的app客户端,它是一个进程,抖音的服务器,也是一个进程。我们通过抖音客户端达到网络通信,在抖音的服务器上获取信息,便是进程间通信。
而IP地址,仅仅是解决了两台物理机器之间的相互通信的识别问题,我们还要解决是在这两台计算机之间的进程间的通信,就是怎么知道计算机A发出的信息是要传给计算机B中的某个进程呢?这就需要端口号了!
因此,端口号的作用是唯一标识一台机器上的唯一一个进程!通过IP+端口号port,就能够标识互联网中的唯一一个进程!
我们可以将整个网络看成是一个大的OS,所有的网络行为,几乎都是在这一个大的OS进行进程间通信!
既然说端口号port是进程的一个身份,那么进程的PID按理论上来说,也能通过PID来进行网络上的进程间通信,那么为什么还需要一个port呢?
区分IP/PORT,PID/PORT
上面我们已经很清楚了,IP的标识物理机器的,port是标识进程的。而PID也是用来标识进程的,也是唯一性的!其实PID跟port,都属于进程的身份,就好像学生由身份证,也有他的学生证,一句话来说,将进程的PID和port分开来使用,是为了解耦!
一个进程可以关联多个端口号,而一个端口号不能关联多个进程。
网络是一份共享资源
要在网络上进行进程间通信,我们首先需要找到目标主机,然后找到该主机上的服务(进程),完成进程间通信。我们可以说网络世界,是一个进程间通信的世界。而进程要通信的话,由于进程具有独立性,因此不同的进程必须看到同一份资源,即共享资源!所有,网络便是一份共享资源!
2、认识TCP协议,认识UDP协议
这里先简单得对TCP和UDP来一个直观的认识:
TCP协议和UDP协议都是传输层的控制协议,以下是两种协议的特定,我们需要根据它们的特定,在不同场景下,权衡使用哪种协议。
TCP协议:
*传输层协议 *有连接 *可靠传输 *面向字节流
YDP协议:
*传输层协议 *无连接 *不可靠传输 *面向数据报
3、认识网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
网络数据流觉得这样分来分去太麻烦了,这样吧!我就使用大端的形式吧!如果你的数据流本来就是大端,那你就直接传输,如果你的数据流是小端,那么麻烦你先转换成大端,再来传输!
因此,网络字节序指的就是在网络上的采用的大端形式,先发出的数据是低地址,后发出的数据是高地址。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换:

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。
总结一下网络字节序:
⭐发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出。
⭐接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存。
⭐因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。
⭐TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
⭐不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据。
⭐如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可。
4.socket编程接口
socket是套接字的意思,用于描述IP地址和端口号,是一个通信链的句柄。应用程序通过socket向网络发出请求或者回应。
socket 常见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);
我们逐一来理解一下这些接口:
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

第一个参数:domain:协议域。就是需要用哪种协议,我们最常用的就两种->AF_INET (IPV4协议),AF_INET6 (IPV6协议)。
第二个参数:套接字的类型,即SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)。
第三个参数:这个我们置为0即可,它是用来制定某个协议的特定类型,即type类型中的某个类型。通常一种协议只有一种类型,那样该参数可以直接被设置为0;如果协议有多种类型,则需要指定协议类型。
返回值:返回一个文件描述符。
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);

第一个参数:socket函数返回的文件描述符。
第二个参数:指定想要绑定的IP和端口。下面将分析sockadder结构体。
第三个参数:address的长度。
返回值:成功为0,失败-1
sockaddr结构:
网络通信的方式有很多种,比如基于网IP的网络通信,AF_INET,原始套接字,域间套接字等等。有那么多方式,那么在绑定IP和端口的时候,就需要很多种方法了,因此系统需要将其统一一下结构,就有了sockadder。

IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址。
IPv4、 IPv6地址类型分别定义为常数AF_INET、 AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。
socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。
sockaddr 结构

sockaddr_in 结构

虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址。
我们简化看看表示IPV4的结构体:
struct sockaddr_in
{sa_family_t sin_family;//地址族uint16_t sin_port;//TCP/UDP端口号,16位整型struct in_addr sin_addr;//IP地址,32位整型char sin_sero[8];//别管它了
};
其中sin_famile:
| 地址族 | 含义 |
| AF_INET | IPV4网络协议中的使用的地址族 |
| AF_INET6 | IPV6网络协议中使用的地址族 |
| AF_LOCAL | 本地通信中采用的UNIX协议的地址族 |
in_addr结构

in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数。
我们使用这两个函数,再补充两个函数:recvfrom和sendto就可以写一个示例了(UDP的)。
recvfrom:适用于UDP协议
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

本函数用于从(已连接)套接口上接收数据,并捕获数据发送源的地址
第一个参数:套接字文件描述符
第二个参数:指明一个缓冲区,该缓冲区用来存放recvfrom函数接收到的数据
第三个参数:buf的长度
第四个参数:一般置0,即false。
第五个参数:是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。
第六个参数:第五个参数的sizeof
返回值:成功返回接收到的字节数。失败返回-1。
sendto:适用于UDP协议
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

·第一个参数:套接字文件描述符。
第二个参数:指明一个存放应用程序要发送数据的缓冲区。
第三个参数:buf的长度
第四个参数:置为0吧。
第五个参数:dest_addr表示目地机的IP地址和端口号信息
第六个参数:dest_addr的长度
返回值:成功返回接收到的字节数。失败返回-1。
示例代码:
实现一个网络通信功能,在client中输入信息,会在server中显示出来,并且返回信息给client,达到网络通信聊天的效果。
客户端client代码:
#include <iostream>
#include <string>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>void Usage(std::string proc)
{std::cout<<"Usage: \n\t"<<proc<<" server_ip server_port"<<std::endl;
}int main(int argc,char *argv[])
{if(argc!=3){Usage(argv[0]);return 0;}//1.创建套接字,打开网络文件int sock=socket(AF_INET,SOCK_DGRAM,0);if(sock<0){std::cerr << "socket error : " << errno << std::endl;return 1;}//客户端不需要显示bind。//首先,客户端必须也要有IP和port//但是,客户端不需要显示的bind。因为一旦显示bind,就必须明确客户端client//要和哪个端口port关联。//而如果客户端client指明了端口号,那么在客户端client不一定会有用,因为//这个端口号有可能被占用了,比如我们在联网的时候,一边打LOL,一边斗地主//被占用就会导致client无法使用//server要的是port必须明确,而且不变,但client只要有就行!一般是由OS自动给你bind()// 就是client正常发送数据的时候,OS会自动给你bind,采用的是随机端口的方式!struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(atoi(argv[2]));server.sin_addr.s_addr = inet_addr(argv[1]);//使用服务while(1){//数据从我们键盘输入std::string message;std::cout<<"输入# ";std::cin>>message;sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server, sizeof(server));struct sockaddr_in tmp;socklen_t len = sizeof(tmp);char buffer[1024];recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&tmp, &len);std::cout<<"server echo# "<<buffer<<std::endl;}return 0;
}
服务器server代码:
#include<iostream>
#include<string>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>const uint16_t port = 8080;int main()
{//1.创建套接字,打开网络文件int sock = socket(AF_INET,SOCK_DGRAM,0);if(sock<0){std::cerr<<"socket create error: "<<errno<<std::endl;return 1;}//2.给该服务器绑定端口和ipstruct sockaddr_in local;//填充字段local.sin_family = AF_INET;local.sin_port = htons(port);//此处的port是端口号,是计算机上的变量//是属于主机序列,说明需要主机转网络的操作htons();//需要将人识别的点分十进制,字符串风格IP地址,转化成为4字节整数IP //需要考虑大小端,因此使用in_addr_t inet_addr(const char *cp);//local.sin_addr.s_addr = inet_addr("43.139.32.198");//点分十进制【0-255】//我们不能像上面这行代码一样,直接绑定bind某个IP,因为如果指定绑定一个IP,那么//只有发送到该IP主机上的数据才会交给你的网络进程//但是,服务器一般会配置很多个网卡,有很多个IP。//因此,作为服务器,我们需要的不是某个IP上面的数据,//而是需要所有发送到该服务器主机上的某个端口的数据!//使用INADDR_ANY,不绑定指定IPlocal.sin_addr.s_addr = INADDR_ANY;//绑定IP和端口if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"bind error: "<<errno<<std::endl;return 2;}//3.提供服务bool quit = false;#define NUM 1024char buffer[NUM];while(!quit){struct sockaddr_in peer;//保存接受到的数据的空间socklen_t len = sizeof(peer);//空间的大小recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);std::cout<<"client# "<<buffer<<std::endl;std::string echo_hello = "hello";//服务器发送回给用户,表示收到消息了sendto(sock,echo_hello.c_str(),echo_hello.size(),0,(struct sockaddr*)&peer,len);}return 0;
}
接下来我们改造一下代码,实现一个功能:在client中输入bash命令,在server中执行命令,并且将执行结果返回到客户端client中。实现了简单的xshell。
client的代码:
#include <iostream>
#include <string>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<cstring>void Usage(std::string proc)
{std::cout << "Usage: \n\t" << proc << " server_ip server_port" << std::endl;
}// ./udp_client server_ip server_portint main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);return 0;}// 1. 创建套接字,打开网络文件int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){std::cerr << "socket error : " << errno << std::endl;return 1;}//客户端需要显示的bind的吗??// a. 首先,客户端必须也要有ip和port// b. 但是,客户端不需要显示的bind!一旦显示bind,就必须明确,client要和哪一个port关联// client指明的端口号,在client端一定会有吗??有可能被占用,被占用导致client无法使用// server要的是port必须明确,而且不变,但client只要有就行!一般是由OS自动给你bind()// 就是client正常发送数据的时候,OS会自动给你bind,采用的是随机端口的方式!// b. 你要给谁发??struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(atoi(argv[2]));server.sin_addr.s_addr = inet_addr(argv[1]);// 2.使用服务while (1){// a. 你的数据从哪里来??// std::string message;// std::cout << "输入# ";// std::cin >> message;std::cout<<"MyShell $ ";char line[1024];fgets(line,sizeof(line),stdin);sendto(sock, line, strlen(line), 0, (struct sockaddr*)&server, sizeof(server));//此处tmp就是一个”占位符“struct sockaddr_in tmp;socklen_t len = sizeof(tmp);char buffer[1024];ssize_t cnt = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&tmp, &len);if(cnt>0){//在网络通信中,只有报文大小,或者是字节流中字节的个数//没有C/C++字符串这样的概念buffer[cnt] = 0;//添加'\0'std::cout << buffer << std::endl;}else{//TODO}}return 0;
}
server的代码:
#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>//const uint16_t port = 8080;
std::string Usage(std::string proc)
{std::cout<<"Usage: "<<proc<<"port"<<std::endl;
}
// udp_server,细节最后在慢慢完善
// ./udp_server port
int main(int argc,char *argv[])
{if(argc!=2){Usage(argv[0]);return -1;}uint16_t port = atoi(argv[1]);//argv[1]是字符串类型,需要转成整型//1. 创建套接字,打开网络文件int sock = socket(AF_INET, SOCK_DGRAM, 0);if(sock < 0){std::cerr << "socket create error: " << errno << std::endl;return 1;}//2. 给该服务器绑定端口和ip(特殊处理)struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(port); //此处的端口号,是我们计算机上的变量,是主机序列// a. 需要将人识别的点分十进制,字符串风格IP地址,转化成为4字节整数IP// b. 也要考虑大小端// in_addr_t inet_addr(const char *cp); 能完成上面ab两个工作.// 坑: // 云服务器,不允许用户直接bind公网IP,另外, 实际正常编写的时候,我们也不会指明IP// local.sin_addr.s_addr = inet_addr("42.192.83.143"); //点分十进制,字符串风格[0-255].[0-255].[0-255].[0-255]// INADDR_ANY: 如果你bind的是确定的IP(主机), 意味着只有发到该IP主机上面的数据// 才会交给你的网络进程, 但是,一般服务器可能有多张网卡,配置多个IP,我们需要的不是// 某个IP上面的数据,我们需要的是,所有发送到该主机,发送到该端口的数据!local.sin_addr.s_addr = INADDR_ANY;if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cerr << "bind error : " << errno << std::endl;return 2;}//3. 提供服务bool quit = false;#define NUM 1024char buffer[NUM];while(!quit){struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t cnt = recvfrom(sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if(cnt>0){buffer[cnt] = 0;//0=='\0'FILE *fp = popen(buffer,"r");std::string echo_hello;char line[1024] = {0};while(fgets(line,sizeof(line),fp)!=NULL){echo_hello+=line;}//这里可以选择判断是否读到了文件结尾// if(feof(fp))// {// //读取结果完成// }pclose(fp);std::cout << "client# " << buffer << std::endl;//根据用户输入,构建一个新的返回字符串//echo_hello+="...";sendto(sock, echo_hello.c_str(), echo_hello.size(), 0, (struct sockaddr*)&peer, len);}else{//TODO}}return 0;
}
相关文章:
网络编程套接字(一)
学习任务: 我们先来认识端口号,区分好主机IP和端口号的区别,以及涉及到进程PID和端口号的区别。 然后简单认识一下TCP协议和UDP协议,这两个协议都是传输层的。接着了解什么是网络字节序,它有什么作用。然后是网络编程的…...
Mysql数据库存储过程
1、参数分类 存储过程的参数类型可以是IN、OUT和INOUT。根据这点分类如下: 1、没有参数(无参数无返回) 2、仅仅带 IN 类型(有参数无返回) 3、仅仅带 OUT 类型(无参数有返回) 4、既带 IN 又带 O…...
当我开始学习人工智能:人工智能的学派及研究目标
上课真是不认真啊,现在都写不来了作业了,真的会谢 一、人工智能的学派及其争论 1.1 对人工智能方法的争论 三个学派 符号主义 认为人的认知基元是符号,认知过程即符号操作过程。 认为人是一个物理符号系统,计算机也是一个物理符…...
Html5钢琴块游戏制作与分享(音游可玩)
当年一款手机节奏音游,相信不少人都玩过或见过。最近也是将其做了出来分享给大家。 游戏的基本玩法:点击下落的黑色方块,弹奏音乐。(下落的速度会越来越快) 可以进行试玩,手机玩起来效果会更好些。 点击…...
MySQL数据库——数据库设计概念和数据库设计步骤
数据库设计就是根据业务系统的具体需求,结合我们所选用的数据库,建立好表结构及表与表之间的管理关系,为这个业务系统构造出最优秀的数据存储模型的过程。使之能有效的对应用的数据进行存储,并高效的对已经存储的数据进行访问。 …...
【云原生】Kubernetes(k8s)之Pod概念和使用
k8s之Pod概念和使用一、Pod简介1.1、Pod的阶段(状态)1.2、容器状态二、Pod的定义2.1、restartPolicy2.2、imagePullPolicy2.3、command2.4、args2.5、resources三、Pod的使用3.1、创建并访问Pod3.2、多个应用容器3.3、Init容器3.3.1、Init容器与普通容器…...
数组(九)-- LC[316][321][402] 去除重复字母
1 移掉 K 位数字 1.1 题目描述 题目链接:https://leetcode.cn/problems/remove-k-digits/ 1.2 思路分析 这道题让我们从一个字符串数字中删除 k 个数字,使得剩下的数最小。也就说,我们要保持原来的数字的相对位置不变。 以题目中的 num1432…...
ubuntu下Thrift安装
thrift是一种常用rpc框架,工作中经常会用到,本文记录一下其安装过程。 目录 1.下载软件包 1.1thrift下载 1.2libevent下载 1.3boost下载 2.安装(注意步骤) 2.1安装libevent 2.2安装boost 2.3安装与Python2.7版本对应的py…...
读懂AUTOSAR :DiagnosticLogAndTrace DLT(四)-- API解析
一、周期调用的函数:Dlt_TxFunction 根据参数DltGeneralTrafficShapingSupport,决定如何去发送DLT消息。如果为TRUE,那需要参考参数DltLogChannelTrafficShapingBandwidth为每个Log通道设置发送带宽;如果为FALSE,那么…...
【LeetCode】剑指 Offer 56. 数组中数字出现的次数 p275 -- Java Version
1. 题目介绍(56. 数组中数字出现的次数) 面试题56.:数组中数字出现的次数, 一共分为两小题: 题目一:数组中只出现一次的两个数字题目二:数组中唯一只出现一次的数字 2. 题目1:数组中…...
Zookeeper集群 + Fafka集群
目录 第一章Zookeeper 概述 1.1.Zookeeper 定义 1.2.Zookeeper 工作机制 1.3.Zookeeper 特点 1.4.Zookeeper 数据结构 1.5.Zookeeper 应用场景 1.6.Zookeeper 原理之选举机制 1.7.部署 Zookeeper 集群 总结 第二章消息队列概述 2.1消息队列需求原因 2.2消息队列的优…...
全国青少年电子信息智能创新大赛(复赛)python·模拟四卷
目录 一、编程题 答案解析如下: 下载文档打印做题: 全国青少年电子信息智能创新大赛(复赛)python模拟四卷 一、编程题 第一题:描述 班上有学生若干名,给出每名学生的年龄《整数),求班上所有学生的平均年龄,保留到小数点后两企 输入 第一行有一个整数n (1<= n...
Redis - 介绍与使用场景
简介 Redis 的全称是 Remote Dictionary Server,是一个使用 C 语言编写的、开源的(BSD 许可)高性能非关系型(NoSQL)的键值对数据库。 Redis 的数据是存储在内存中的,所以读写速度非常快,被广泛…...
Spark SQL实战(07)-Data Sources
1 概述 Spark SQL通过DataFrame接口支持对多种数据源进行操作。 DataFrame可使用关系型变换进行操作,也可用于创建临时视图。将DataFrame注册为临时视图可以让你对其数据运行SQL查询。 本节介绍使用Spark数据源加载和保存数据的一般方法,并进一步介绍…...
Django DRF - 权限Permissions
权限Permissions 权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。 在执行视图的dispatch()方法前,会先进行视图访问权限的判断在通过get_object()获取具体对象时,会进行对象访问权限的判断 1.提供的权限 AllowAny 允许所有用户IsAuth…...
二叉树(OJ)
单值二叉树(力扣) ---------------------------------------------------哆啦A梦的任意门------------------------------------------------------- 我们来看一下题目的具体要求: 既然我们都学了二叉树了,我们就应该学会如何去…...
mysql中增删改成的练习
文章目录一、表的创建1.student表的数据2、课程表的数据course3、学生成绩表的数据二、操作序列1、查询计算机系cs的全体学生学号、姓名和性别2、检索选修了课程号为2的学生号和姓名3、检索至少选修了三门课以上的学生号4、检索选修了全部课程的学生5、在原表的基础上创建一个视…...
谈一谈Java的ThreadLocal
目录 先说原理: 再上代码: 运行结果: 先说原理: ThreadLocal 是一个本地线程副本变量工具类,它可以在每个线程中创建一个副本变量,每个线程可以独立地修改自己的副本变量,而不会影响其他线程…...
边缘检测与阈值分割
Canny [1] Canny Edge Detection. https://docs.opencv.org/3.4/da/d22/tutorial_py_canny.html [2] OpenCV Edge Detection ( cv2.Canny ). https://pyimagesearch.com/2021/05/12/opencv-edge-detection-cv2-canny/ 由John F. Canny提出 1、由于边缘检测容易受噪声影响&…...
QQ空间无敌装逼,复制下面的任一代码粘贴即可出现意想不到的图案。
复制下面的任一代码粘贴即可出现意想不到的图案。 打赏代码: [em]e10033[/em]{uin:123,nick: 打赏了你一个冰淇淋,who:1} [em]e10033[/em] 打赏了100000000000.00元红包 [em]e10011[/em] 赞代码:{uin:0000,nick: xx、xx、xx、xx、xx、xx、xx、xx、xx、xx、xx、x…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...


