当前位置: 首页 > news >正文

socket 编程

1. socket 套接字

Socket 是一个用于网络通信的技术。Socket 通信允许客户端——服务器之间进行双向通信。它可以使任何客户端机器连接到任何服务器,安装在客户端和服务器两侧的程序就可以实现双向的通信。Socket的作用就是把连接两个计算机的通信软件“中间接”起来,能够实现远程连接


socket 是一个编程接口 (网络编程接口),是一种特殊的文件描述符 (write/read/close)


socket 并不仅限于TCP/IP协议

socket 是独立于具体协议的编程接口,这个接口位于TCP / IP四层模型中的应用层与传输层之间


socket类型:

        (1) 流式套接字 (SOCK_STREAM)

                面向字节流,针对传输层协议为TCP的应用

                保证数据传输是可靠的

                提供一种可靠的、面向连接的双向数据传输服务,实现了数据无差错、无重复的发 送。流式套接字内设流量控制,被传输的数据看作是无记录边界的字节流

        (2) 数据报套接字 (SOCK_DGRAM)
                针对传输层协议为UDP的应用
                提供一种无连接的服务,该服务并不能保证数据传输的可靠性

                它提供了一种无连接、不可靠的双向数据传输服务。数据在传输过程中可能会丢失或重复,并且不能保证在接收端按发送顺序接收数据


        (3) 原始套接字(SOCK_RAW)

                直接跳过传输层,该套接字允许对较低层协议 (如IP或ICMP) 进行直接访问,常用于网络协议分析,检验新的网络协议实现,也可用于测试新配置或安装的网络设备


把socket(网络编程接口)当成一个特殊的文件描述符即可

2. TCP网络应用

任何的网络应用都有通信双方:
        服务器(Server) / 客户端(Client)

        

        网络结构:CS架构

TCP套接字编程基本流程:

        TCP网络应用

                TCP   Server

                TCP   Client

        
        任何的网络应用:

                IP(目标主机) + 传输层协议(如何传输) + 端口号(具体应用)

TCP网络应用的数据传输的大致过程:

        (1) 建立连接:

                "三次握手"

        (2) 发送/接收网络数据 (操作socket)

                write / send / sendto
                read / recv / recvfrom

        (3) 关闭连接:

                "四次挥手"


TCP网络应用编程流程:

TCP Server

(1) socket:创建一个套接字


(2) bind:把一个套接字和网络地址绑定到一起
                 如果你想要其他人主动来连接你,你就必须bind一个地址,并且把这个地址告诉其他人
                注意:不调用bind,并不代表你的socket就没有地址,不管你调不调用bind,socket在通信时,内核都会为你的socket指定一个地址


(3) listen:让套接字进入“监听模式”

(4) 有连接请求的时候

        accept   接收一个监听队列上面的请求

        多次的调用accept就可以与不同的客户建立连接

        accept 在“监听套接字”上,创建一个与客户端的“连接套接字”

(5) 进行通信,读写数据

        write / send / sendto

        read / recv / recvfrom


(6) 关闭socket套接字:"四次挥手"

        关闭连接套接字就相当于和accept的客户端断开连接了

        close / shutdown

TCP Client
(1) socket:创建一个套接字


// 设置服务器的IP和端口
(2) connect 主动与server建立连接需要知道服务器的地址

(3) 进行通信,读写数据

        write / send / sendto

        read / recv / recvfrom


(4) 关闭socket套接字:"四次挥手"

        close / shutdown

3. socket 具体的API函数解析

(1) socket:创建一个套接字
NAMEsocket - create an endpoint for communication
SYNOPSIS#include <sys/types.h>#include <sys/socket.h>socket用来创建一个通信端口“socket”int socket(int domain, int type, int protocol);domain:指定域,协议族。socket接口不仅仅局限于TCP/IP,还可以用于buletooth,本地进程间通信...每一种通信模式下面都有一系列自己的协议,归到一类:协议族AF_INET   IPV4协议族AF_INET6  IPV6协议族AF_UNIX(UNIX域协议)/ AF_LOCAL  本地进程间通信type:指定要创建的套接字类型SOCK_STREAM  流式套接字     TCPSOCK_DGRAM  数据报套接字   UDPSOCK_RAW    原始套接字...protocol:指定具体的应用层协议,可以指定为0(不知名的私有应用协议)	返回值:成功返回一个套接字描述符(>0,特殊的文件描述符)失败返回-1,同时errno被设置
(2) bind:把一个套接字和网络地址绑定到一起
NAMEbind - bind a name to a socket
SYNOPSIS#include <sys/types.h>#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);			sockfd:	要绑定的套接字描述符addr:要绑定的网络地址结构体指针addrlen:要绑定的网络地址结构体的长度,单位:字节通过指针去访问内存,为了防止内存越界返回值:成功返回0失败返回-1,同时errno被设置
(3) 网络地址结构体

socket 描述符可以用于IPV4也可以用于IPV6,也可以用于蓝牙......

但是不同的协议里面,"地址"的描述方法不一样

设置了一个通用的地址结构体,所有的socket 函数接口用到的地址参数的类型都使用:

struct sockaddr  这种类型表示

所有协议的地址都是使用这个结构体去描述一个地址的,在这个结构体的第一个成员变量中,指定了协议族,按照相应的协议族去解析具体的地址

通用地址结构体:#include<linux/socket.h>
定义在/usr/include/linux/socket.h
struct sockaddr {sa_family_t  sa_family; // 指定协议族char         sa_data[14]; // 存放具体协议的地址};我们现在使用IPV4,所以这个地址需要使用一个IPV4协议族下面的地址
IPV4的地址结构体:vim /usr/include/netinet/in.h
// #include <netinet/in.h>
struct sockaddr_in { // 描述一个IPV4的地址(IP + 端口号)sa_family_t sin_family; // 指定协议族u_int16 sin_port; // 端口号,2个字节,必须是网络字节序struct in_addr sin_addr; // IPV4地址,整数,4个字节unsigned char sin_zero[8]; // 填充8个字节,为了和通用网络地址结构体一样大
};typedef uint32_t in_addr_t;
struct in_addr { // 32位的IP地址in_addr_t s_addr;
};
问题1:人类常说的ip是“点分式”,但是结构体中要求的却是struct in_addr,怎么办?IPV4地址转换函数//  INADDR_ANY:一个宏,表示任何地址 "0.0.0.0"  
// 一个监听地址,表示服务器愿意接收来自任何客户端的连接请求 #include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>// inet_aton:把“点分式”的IP地址转换为in_addr类型的地址int inet_aton(const char *cp, struct in_addr *inp);cp:指针,指向你要转换的Ip字符串(“点分式”)inp:指针,指向一块内存空间,保存转换后的Ip地址返回值:成功返回0失败返回-1,errno被设置例子:// 定义一个IPV4的结构体struct sockaddr_in sa;memset(&sa, 0, sizeof(sa));// 设置协议族为IPV4sa.sin_family = AF_INET;	// 把“192.168.1.4”这个地址转换后存入sa的成员中inet_aton("192.168.1.4", &(sa.sin_addr));// sa.sin_addr.s_addr = inet_addr("192.168.1.4");// sa就描述了一个IPV4的地址
------------------------------------------------------------------------
// inet_addr是把点分式的IP转换为in_addr_t类型
// 只不过此函数是把转换结果直接返回in_addr_t inet_addr(const char *cp);sa.sin_addr.s_addr = inet_addr("192.168.1.4");	   
-------------------------------------------------------------------------	
// inet_network与inet_addr功能一样
in_addr_t inet_network(const char *cp);
-------------------------------------------------------------------------	
// 把一个网络地址转换为IPV4的点分式字符串,返回这个字符串的首地址
char* inet_ntoa(struct in_addr in);	// inet_ntoa(cAddr.sin_addr);
问题2:PC上面一般是小端模式,在指定端口号的时候,需要使用大端模式(网络字节序)网络字节序与主机字节序之间的转换NAMEhtonl,htons,ntohl,ntohs - convert values between host and network byte order
SYNOPSIS#include <arpa/inet.h>h: host 主机字节序
n: network 网络字节序
l: long -->32bits
s: short --->16bits	// 将字符串变成整数
#include <stdlib.h>int atoi(const char *nptr);long atol(const char *nptr);long long atoll(const char *nptr);uint32_t htonl(uint32_t hostlong);
// htonl:把一个32位的数字(主机字节序)转换为网络字节序的数字uint16_t htons(uint16_t hostshort); <----- 端口号
// htons:把一个16位的数字(主机字节序)转换为网络字节序的数字uint32_t ntohl(uint32_t netlong);
// ntohl:把一个32位的数字(网络字节序)转换为主机字节序的数字uint16_t ntohs(uint16_t netshort);
// ntohs:把一个16位的数字(网络字节序)转换为主机字节序的数字
(4) listen:让套接字进入“监听模式”
NAMElisten - listen for connections on a socket
SYNOPSIS#include <sys/types.h>#include <sys/socket.h>开启对一个套接字描述符的监听int listen(int sockfd, int backlog);		sockfd:开启对哪一个套接字的监听backlog:监听队列上面最大的请求数量返回值:成功返回0失败返回-1,同时errno被设置
(5) accept:接收一个监听队列上面的请求
NAMEaccept, accept4 - accept a connection on a socket
SYNOPSIS#include <sys/types.h>#include <sys/socket.h>accept接收一个套接字监听队列上面的请求int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);sockfd:你要监听的套接字addr:网络地址结构体指针,指向一块可用的空间,用来保存客户端的地址的addrlen:指针,指向的数据保存第二个参数指针指向的可用空间的长度,防止内存越界,能够把客户端的网络地址保存起来,在调用的时候,addrlen一般保存addr指向的那个结构体的大小,函数返回时,addrlen指向的变量保存的是客户端地址的实际大小返回值:成功返回一个连接套接字(confd)表示与一个特定的客户端的连接,后续与这个客户端的数据通信都需要通过这个连接套接字失败返回-1,同时errno被设置阻塞到客户端连接			
(6) connect:主要用于TCP Client 去连接TCP Server
			
NAMEconnect - initiate a connection on a socket
SYNOPSIS#include <sys/types.h>#include <sys/socket.h>connect用来将参数sockfd表示的soctet文件描述符连接到参数addr描述的网络地址上面去int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);		sockfd:本地的套接字描述符addr:指定服务器的地址(ip+端口号),表示你要连接哪一个服务器,是一个网络地址指针addrlen:指定第二个参数的长度,通过第二个参数指针去访问指定的位置,但是不能越界返回值:成功返回0失败返回-1,同时errno被设置
(7) 发送数据  write / send / sendto

write/send/sendto 这三个函数,TCP都可以使用,但是UDP只能使用sendto

NAMEsend, sendto- send a message on a socket// 发送一个数据到指定的socket
SYNOPSIS#include <sys/types.h>#include <sys/socket.h>send用来往一个套接字上面发送数据ssize_t send(int sockfd, const void *buf, size_t len, int flags);sockfd:你要向哪一个套接字上面发送数据buf:你要发送的数据的指针len:你要发送多少数据(字节)flags:指定发送标志,一般为00               阻塞模式MSG_DONTWAIT    非阻塞模式返回值:成功返回实际发送的字节数失败返回-1,同时errno被设置
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);sendto和send类似,多了两个参数,多的两个参数是指定接收方的地址TCP是面向连接的通信,可以不指定,因为在通信前已经connect了,sockfd就是一个连接套接字,
已经保存了接收方的地址。但是UDP一定要指定,因为UDP无连接的通信dest_addr:指定接收方的地址addrlen:指定接收方的地址的长度返回值:成功返回实际发送的字节数失败返回-1,同时errno被设置
(8) 接收数据  read / recv / recvfrom
NAMErecv, recvfrom -  receive a message from a socket
SYNOPSIS#include <sys/types.h>#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);recv前面三个参数和read类似,都是从指定的文件描述符中读取count个字节存到buf指向的空间flags:指定接收标志,一般为00               阻塞模式MSG_DONTWAIT    非阻塞模式返回值:成功返回实际读取的字节数失败返回-1,同时errno被设置
recvfrom前面的四个参数与recv一样ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);src_addr:用来保存发送方的地址,TCP/UDP都可以不指定TCP一定知道数据的来源的地址,保存在sockfd中但是如果UDP不指定,虽然可以收到数据,但是不知道是谁发送给你的addrlen:用来保存发送方地址的长度,也是一个指针addrlen一般保存的是src_addr指向的结构体的大小,但是函数返回的时候addrlen指向的变量保存的是发送方地址的实际大小返回值:成功返回实际读取的字节数失败返回-1,同时errno被设置
(9) 关闭套接字   close / shutdown
NAMEshutdown - shut  down socket send and receive operations
SYNOPSIS#include <sys/socket.h>int shutdown(int socket, int how);	socket:要关闭的套接字how表示关闭方式:SHUT_RD  关闭读SHUT_WR  关闭写SHUT_RDWR 关闭读写--->close返回值:成功返回0失败返回-1,同时errno被设置

4. TCP服务端和客户端代码实现

tcp_client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>// IP + 端口
int main(int argc, char *argv[]) {if (argc != 3) {printf("argc num error\n");return -1;}// (1) socket:创建一个套接字int sockfd =  socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("tcp socket failed");return -1;}struct sockaddr_in sAddr;memset(&sAddr, 0, sizeof(sAddr));sAddr.sin_family = AF_INET; // 协议族sAddr.sin_port = htons(atoi(argv[2])); // 端口号sAddr.sin_addr.s_addr = inet_addr(argv[1]); // IPV4地址// (2) connectint res = connect(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr));if (res == -1) {perror("tcp connect failed");return -1;}while (1) {// (3) 进行通信,读写数据char buf[250] = {0};scanf("%s", buf);int w = send(sockfd, buf, strlen(buf), 0);printf("w = %d\n", w);if (buf[0] == '#') {break;}char buff[250] = {0};int r = recv(sockfd, buff, 250, 0);printf("r = %d,message = %s\n", r, buff);}// (4) 关闭socket套接字close(sockfd);return 0;
} 

tcp_server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* func(void *arg) {int confd = *((int *)arg);while (1) {// (5) 进行通信,读写数据char buf[250] = {0};int r = recv(confd, buf, 250, 0);if (r != -1) {printf("r = %d,messagee = %s\n", r, buf);}if (buf[0] == '#') {close(confd);break;}send(confd, "hello", sizeof("hello"), 0);}   
}// IP + 端口号
int main(int argc, char *argv[]) {if (argc != 3) {printf("argc num error\n");return -1;}// (1) socket:创建一个套接字int sockfd =  socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("socket sockfd failed");return -1;}// (2) bind:把一个套接字和网络地址绑定到一起// 需要一个网络地址结构体struct sockaddr_in sAddr;memset(&sAddr, 0, sizeof(sAddr));sAddr.sin_family = AF_INET; // 协议族sAddr.sin_port = htons(atoi(argv[2])); // 端口号sAddr.sin_addr.s_addr = inet_addr(argv[1]); // IPV4地址int res = bind(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr));if (res == -1) {perror("bind sockfd failed");close(sockfd);return -1;}printf("bind success\n");// (3) listen:让套接字进入“监听模式”res = listen(sockfd, 5);if (res == -1) {perror("listen sockfd failed");close(sockfd);return -1;}// 保存客户端的网络地址struct sockaddr_in cAddr;// 保存客户端的网络地址的长度socklen_t len = sizeof(cAddr);while (1) {printf("hahaha\n");// (4) 当没有客户端请求的时候,accept是阻塞的int *confd = malloc(sizeof(int));*confd = accept(sockfd, (struct sockaddr *)&cAddr, &len);if (*confd == -1) {continue;}printf("accept success\n");printf("client IP:%s, client port:%d\n", inet_ntoa(cAddr.sin_addr), ntohs(cAddr.sin_port));pthread_t pid;pthread_create(&pid, NULL, func, (void *)confd);}// (6) 关闭socket套接字:"四次挥手"close(sockfd);return 0;
}

5. UDP网络应用

UDP是传输层的一个协议,面向无连接,数据报的传输层协议

无连接:不需要三次握手,数据不可靠

在网络环境比较好的情况下,UDP的传输效率比较高
        常用于“实时应用”的情况
        数据包具有时效性

在应用层添加一些私有控制协议,提高数据传输的可靠性

​​​​​​编程流程:

        发送必须使用sendto,因为数据发送前没有连接,告诉socket,要把数据发给谁

        接收数据一般使用recvfrom,也可以使用read / recv,但是这两个函数只能读取到用户数据,不能获取发送方的网络地址

        如果客户端没有绑定,那么服务器就不能获取发送方的网络地址

6. UDP服务端和客户端代码实现




 

相关文章:

socket 编程

1. socket 套接字 Socket 是一个用于网络通信的技术。Socket 通信允许客户端——服务器之间进行双向通信。它可以使任何客户端机器连接到任何服务器&#xff0c;安装在客户端和服务器两侧的程序就可以实现双向的通信。Socket的作用就是把连接两个计算机的通信软件“中间接”起来…...

如何使用 HTTPie 进行高效的 HTTP 请求

如何使用 HTTPie 进行高效的 HTTP 请求 引言 HTTPie 是一个命令行 HTTP 客户端&#xff0c;它以其简洁的语法和人性化的输出格式赢得了广大开发者的喜爱。与 curl 相比&#xff0c;HTTPie 提供了更加直观和用户友好的接口&#xff0c;使得执行 HTTP 请求变得轻松愉快。本文将…...

Lingo求解器百度云下载 ling 8.0/lingo 18安装包资源分享

如大家所熟悉的&#xff0c;Lingo是Linear Interaction and General Optimizer的缩写&#xff0c;中文名称为“交互式线性和通用优化求解器”&#xff0c;是一套专门用于求解最优化问题的软件包。 在大部分人认知里&#xff0c;Lingo可用于求解线性规划、二次规划、整数规划、…...

文献综述如何为研究的理论框架做出贡献

VersaBot一键生成文献综述 文献综述在几个关键方面对塑造和巩固研究的理论框架起着至关重要的作用&#xff1b; 1. 识别相关理论和概念&#xff1a; 通过对现有研究的探索&#xff0c;您将遇到与您的主题相关的突出理论和概念。这些可以作为您自己的理论框架的构建块。 2. 理…...

FastAPI(七十九)实战开发《在线课程学习系统》接口开发-- 加入课程和退出课程

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 加入课程 我们先看下加入课程 1.是否登录 2.课程是否存在 3.是否已经存在 4.添加 首先实现逻辑 def get_student_course(db: Session, course: int…...

【赛事推荐】2024中国高校计算机大赛人工智能创意赛

“中国高校计算机大赛”&#xff08;China Collegiate Computing Contest&#xff0c;简称C4&#xff09;是面向全国高校各专业在校学生的科技类竞赛活动&#xff0c;于2016年由教育部高等学校计算机类专业教学指导委员会、教育部高等学校大学软件工程专业教学指导委员会、教育…...

C++沉思:预处理和编译

预处理和编译 条件编译源代码使用方式典型示例原理 使用static_assert执行编译时断言检查使用方式原理 在C中&#xff0c;编译是将源代码转换为机器代码并组织在目标文件中&#xff0c;然后将目标文件链接在一起生成可执行文件的过程。编译器实际上一次只处理一个文件&#xff…...

交通数据处理-计算途径某些路段的车辆数

根据车辆的运行轨迹&#xff0c;计算先经过某些路段&#xff0c;再经过某些路段的车辆数。 欢迎关注本人公众号--交通数据探索师 如下表&#xff0c; 其中&#xff1a;vehicle: 车辆编号&#xff1b;route: 车辆轨迹。 以第一行为例&#xff0c;车辆car1按顺序经过了路段123…...

从0到1入门系列 | 崖山公开课再加码,三小时带你入门崖山数据库!

对不断更新的技术心生迷茫 不知如何正确的提升自己&#xff1f; 对新兴的国产数据库领域充满好奇 却不知从何入手&#xff1f; 崖山专家团队精心筹备 《从0到1入门》系列直播课 6节课 三小时 助力数据库小白变身技术高手 掌握最前沿的数据库技术 现在开始 开启职场“金…...

Powershell自定义带参数的别名

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、函数二、使用步骤总结 前言 之前写了一篇文章定义别名让powershell尽可能接近Unix风格&#xff0c;增强两者的互操作性&#xff0c;今天给出方法可以定义带…...

文件操作相关的精讲

目录&#xff1a; 思维导图 一. 文件定义 二. 文件的打开和关闭 三. 文件的顺序读写操作 四. 文件的随机读写操作 五. 文本文件和二进制文件 六. 文件读取结束的判断 七.文件缓冲区 思维导图&#xff1a; 一. 文件定义 1.文件定义 C语言中&#xff0c;文件是指一组相…...

05 循环神经网络

目录 1. 基本概念 2. 简单循环网络 2.1 简单循环网络 2.2 长程依赖问题 3. 循环神经网络的模式与参数学习 3.1 循环神经网络的模式 3.2 参数学习 4. 基于门控的循环神经网络 4.1 长短期记忆网络 4.2 LSTM网络的变体网络 4.3 门控循环单元网络 5. 深层循环神经网络…...

C#初级——条件判断语句、循环语句和运算符

条件判断语句 简单的条件判断语句&#xff0c;if()里面进行条件判断&#xff0c;如果条件判断正确就执行语句块1&#xff0c;如果不符合就执行语句块2。 if (条件判断) { 语句块1 } else { 语句块2 } int age 18;if (age < 18){Console.WriteLine("未…...

Laravel路由模型绑定:简化依赖注入的艺术

Laravel路由模型绑定&#xff1a;简化依赖注入的艺术 引言 在现代Web应用开发中&#xff0c;Laravel框架以其优雅和简洁的代码而闻名。Laravel的路由模型绑定&#xff08;Route Model Binding&#xff09;是框架提供的一项强大功能&#xff0c;它允许开发者在路由处理中自动注…...

【vue前端项目实战案例】之Vue仿饿了么App

本文将介绍一款仿“饿了么”商家页面的App。该案例是基于 Vue2.0 Vue Router webpack ES6 等技术栈实现的一款外卖类App&#xff0c;适合初学者进行学习。 项目源码下载链接在文章末尾 1 项目概述 该项目是一款仿“饿了么”商家页面的外卖类App&#xff0c;主要有以下功能…...

冷热分离——Java全栈知识(36)

之前在面试的时候有老师问&#xff1a; 我看你使用了水平分表&#xff0c;但是如果有些 1%的数据占了访问量的 90%&#xff0c;而剩下 99%的数据只占了访问量的 10%。这种情况怎么处理。 1 、冷热分离 1.1、什么是冷热分离 冷热分离指的是在处理数据时将数据库分为冷库和热库…...

了解Selenium中的WebElement

Selenium中到处都使用WebElement来执行各种操作。什么是WebElement&#xff1f;这篇文章将详细讨论WebElement。 Selenium中的WebElement是一个表示网站HTML元素的Java接口。HTML元素包含一个开始标记和一个结束标记&#xff0c;内容位于这两个标记之间。 HTML元素的重命名 …...

OpenCV facedetect 人脸检测官方示例项目配置

运行程序。该程序会自动打开摄像头&#xff0c;识别并定位摄像头前的人脸以及眼睛部位。 输入q或者Q&#xff0c;退出程序。 或进行文本中所包含的图片路径 或 单个图片进行检测&#xff0c;自行修改代码即可 配置环境项目&#xff0c;debug 解决error C4996: ‘fopen’: This…...

自定义Laravel Artisan风格:打造个性化命令行体验

自定义Laravel Artisan风格&#xff1a;打造个性化命令行体验 引言 Laravel的Artisan命令行工具是开发过程中不可或缺的一部分&#xff0c;它提供了一个强大的接口来执行各种开发、维护、测试等任务。除了执行命令&#xff0c;Artisan还允许开发者自定义命令行输出的风格&…...

CTF之网站被黑

简单看一下网页和源码没发现什么明显漏洞 那就扫描一下目录 发现了/shell.php文件&#xff0c;访问一下&#xff0c;发现是一个后台管理登录页面 别无他法只能爆破喽&#xff0c;爆破后发现密码是hack flag{25891d9e9d377f006eda3ca7d4c34c4d}...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

JDK 17 序列化是怎么回事

如何序列化&#xff1f;其实很简单&#xff0c;就是根据每个类型&#xff0c;用工厂类调用。逐个完成。 没什么漂亮的代码&#xff0c;只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...