如何做衣服销售网站/软文营销的宗旨是什么
目录
前言
TCP通信流程
TCP通信的代码实现
tcp_server.hpp编写
tcp_server.cc服务端的编写
tcp_client.cc客户端的编写
整体代码
前言
上一章我们主要讲解了UDP之间的通信,本章我们将来讲述如何使用TCP来进行网络间通信,主要是使用socket API进行代码的实现。
我们一共讲了5个socket API接口,分别为socket,bind,listen,accept,connect.但我们在讲解UDP通信时,只使用了socket和bind这两个接口就完成了。而TCP通信会使用后面这三个接口,我们将分别讲解.
TCP通信流程
同样地,TCP通信分为服务器端和客户端,它们的流程分别如下:
服务端通信流程:
-
创建套接字:使用
socket
函数创建一个套接字,指定协议族为AF_INET(IPv4)或AF_INET6(IPv6),指定类型为SOCK_STREAM(TCP)。 -
绑定套接字:使用
bind
函数将套接字与服务器的IP地址和端口号绑定在一起。这样服务器将使用指定的IP地址和端口号进行监听。 -
监听连接请求:使用
listen
函数开始监听连接请求。指定参数backlog,表示允许在队列中等待的最大连接数。 -
接受连接请求:使用
accept
函数接受客户端的连接请求。该函数会阻塞程序,直到有客户端连接时才返回一个新的套接字,用于与客户端进行通信。(新的套接字和旧套接字区别:新套接字负责服务建立的连接,包括通信等,旧套接字则一直负责监听连接.) -
通信:使用新的套接字进行通信。可以使用
read
和write
函数进行数据的接收和发送。 -
关闭连接:当通信结束后,使用
close
函数关闭套接字,释放资源。
客户端通信流程:
-
创建套接字:使用
socket
函数创建一个套接字,指定协议族为AF_INET(IPv4)或AF_INET6(IPv6),指定类型为SOCK_STREAM(TCP)。 -
连接服务器:使用
connect
函数连接到服务器的IP地址和端口号。如果连接成功,返回0;否则返回错误码。 -
通信:使用已连接的套接字进行数据的发送和接收,可以使用
read
和write
函数。 -
关闭连接:当通信结束后,使用
close
函数关闭套接字,释放资源。
TCP通信的代码实现
依然是三个文件,分别为tcp_server.hpp(用来封装tcp socket),tcp_server.cc(服务器通信代码),tcp_client.cc(客户端通信代码).
tcp_server.hpp编写
首先我们要编写tcp_server.hpp,首先第一个接口initServer初始化服务端. 一共分为三步:
- 1.创建套接字
利用socket函数创建新的套接字,并判断是否成功:
listensock = socket(AF_INET, SOCK_STREAM, 0);if (listensock < 0){logMessage(FATAL, "%d:%s", errno, strerror(errno));exit(2);}logMessage(NORMAL, "create sock success, listensock: %d", listensock);
- 2.bind绑定
bind将套接字和特定的ip和地址绑定在一起.用法我们上一章也说了,先创建一个sockaddr_in结构体,然后填入相关的数据:sin_family(协议族 AF_INET(IPv4)或AF_INET6(IPv6)),sin_port(端口号),sin_arr.s_addr(ip地址),然后再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 = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());if (bind(listensock, (struct sockaddr *)&local, sizeof local) < 0){logMessage(FATAL, "bind error", errno, strerror(errno));exit(3);}
- 3.listen监听
listen监听是否有新的连接,TCP与UDP不同的是,当客户端和服务端正式通信的时候,需要先建立连接,而UDP直接发送数据。所以要listen来监听是否有新链接.
代码如下:
// 3.因为TCP是面向连接的,意味着当我们正式通信的时候,需要先建立连接//第二个参数我们在讲TCP协议时会详细讲解,这里先暂且设为20if (listen(listensock, gbacklog) < 0){logMessage(FATAL, "listen error", errno, strerror(errno));exit(3);}logMessage(NORMAL, "init server success");
第二个接口Start(),该接口主要负责获取连接,并进行通信.共分为两步:
- accept获取到客户端连接
这个我们同样的需要创建一个sockaddr_in结构体,用来存储客户端的连接信息,然后接收新的套接字,这个套接字是接下来我们通信要使用的。
struct sockaddr_in src;socklen_t len = sizeof src;//servicesock(未来真正进行IO) vs listensock(主要任务:获取新链接)int servicesock = accept(listensock, (struct sockaddr *)&src, &len);if (servicesock < 0){logMessage(ERROR, "accept error", errno, strerror(errno));}
- 通信流程
这里可以提供两个版本的:一个是单进程版,即每一次只能处理一个客户端.
另一个是 多进程版,通过创建子进程来实现对多个客户端处理.
- 单进程版
紧接着上面说的,我们获取到客户端的连接信息后,我们需要对其进行解析,得到其ip地址和端口号:
uint16_t client_port = ntohs(src.sin_port);//获得端口号string client_ip = inet_ntoa(src.sin_addr);//获得iplogMessage(NORMAL, "Link success, %d | %s : %d\n", servicesock, client_ip.c_str(), client_port);
然后直接执行对应的通信函数即可:
service(servicesock,client_ip,client_port);
- 多进程版:
利用fork函数实现,代码如下:后面的服务端通信和客户端通信都不用改动
pid_t id = fork();assert(id != -1);if(id == 0){//子进程close(listensock);service(servicesock,client_ip,client_port);exit(0);//僵尸状态}close(servicesock);
通信函数service的实现:我们从sock中读取消息,客户端没有发消息时,服务端会阻塞在这里等待用户的输入。
static void service(int sock,const string& clientip,const uint16_t& clientport)
{//echo serverchar buffer[1024];memset(buffer, 0, sizeof(buffer));while(true){//read && writessize_t s = read(sock,buffer,sizeof buffer-1);if(s > 0){buffer[s] = 0;//将发过来的数据当做字符串cout << clientip << " : " << clientport << "# "<< buffer << endl;}else if(s== 0)//对端链接关闭{logMessage(NORMAL,"%s : %d shutdown, me too!",clientip.c_str(),clientport);break;}else{logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));break;}write(sock,buffer,strlen(buffer));}close(sock);
}
tcp_server.cc服务端的编写
这个就很简单了,只需要调用initServer初始化和Start开始就行了.
#include "tcp_server.hpp"
#include <memory>static void usage(string proc)
{cout << "Usage: " << proc << "ServerPort\n" << endl;
}//./tcp_server port
int main(int argc, char* argv[])
{if(argc != 2){usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);unique_ptr<TcpServer> svr(new TcpServer(port));svr->initServer();svr->Start();return 0;
}
tcp_client.cc客户端的编写
- 创建套接字:
int sock = socket(AF_INET, SOCK_STREAM, 0);
- 调用connect与服务端链接:利用命令行参数,将用户输入的ip地址和port端口号获取到,然后传入sockaddr_in结构体,最后进行connect
uint16_t serverPort = atoi(argv[2]);string serverIp = argv[1]; struct sockaddr_in server;bzero(&server, 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)
- 进行通信(send和recv)
TCP的发送和接收消息不同于UDP的sendto和recvfrom,而是send和recv。我们分别看一下函数的用法:
send:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd
:发送数据的套接字描述符。即想谁发送buf
:指向要发送数据的缓冲区的指针。len
:要发送的数据的长度(以字节为单位)。flags
:附加选项,通常设为0。作用:send()
函数用于将数据从发送端发送到接收端。它返回已发送的字节数,或者在出现错误时返回-1。可以通过设置flags
参数来指定传输数据的特定选项,例如设置为MSG_DONTWAIT
非阻塞发送等。
recv:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd
:要接收数据的套接字描述符。即谁接收buf
:接收数据的缓冲区的指针。len
:接收数据的最大长度(以字节为单位)。flags
:附加选项,通常设为0。- 作用:
recv()
函数用于从套接字接收数据,并将其存储在指定的缓冲区中。它返回接收到的字节数,或者在出现错误时返回-1。可以通过设置flags
参数来指定接收数据的特定选项,例如设置为MSG_DONTWAIT
非阻塞接收等。
所以通信代码如下:
while (true){string line;cout << "Please Enter Message# ";getline(cin, line);send(sock, line.c_str(), line.size(), 0);char buffer[1024];ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s] = 0;cout << "server echo# " << buffer << endl;}else if (s == 0){break;}else{break;}}
至此我们的TCP通信就完成了.
当我们使用多进程通信时,可以有多个客户端同时向服务端发送消息:
至此,TCP的网络通信流程也完成了,这是完整的代码,可以直接 拷贝运行,可去掉logMessage相关的调试信息.
整体代码
注意运行服务器时,使用./tcp_server 端口号
运行客户端连接服务器时,使用./tcp_clinet 服务器ip 服务器端口号
tcp_server.hpp文件
#pragma once #include <iostream> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <string.h> #include <memory> #include <pthread.h> #include <signal.h> #include <cstring> #include <ctype.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>using namespace std;static void service(int sock,const string& clientip,const uint16_t& clientport) {//echo serverchar buffer[1024];memset(buffer, 0, sizeof(buffer));while(true){//read && writessize_t s = read(sock,buffer,sizeof buffer-1);if(s > 0){buffer[s] = 0;//将发过来的数据当做字符串cout << clientip << " : " << clientport << "# "<< buffer << endl;}else if(s== 0)//对端链接关闭{logMessage(NORMAL,"%s : %d shutdown, me too!",clientip.c_str(),clientport);break;}else{logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));break;}write(sock,buffer,strlen(buffer));} }class TcpServer { public:const static int gbacklog = 20;TcpServer(uint16_t port, string ip = ""): _port(port), _ip(ip), listensock(-1){}void initServer(){// 1.创建套接字listensock = socket(AF_INET, SOCK_STREAM, 0);if (listensock < 0){logMessage(FATAL, "%d:%s", errno, strerror(errno));exit(2);}logMessage(NORMAL, "create sock success, listensock: %d", listensock);// 2.bindstruct sockaddr_in local;memset(&local, 0, sizeof local);local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());if (bind(listensock, (struct sockaddr *)&local, sizeof local) < 0){logMessage(FATAL, "bind error", errno, strerror(errno));exit(3);}// 3.因为TCP是面向连接的,意味着当我们正式通信的时候,需要先建立连接if (listen(listensock, gbacklog) < 0){logMessage(FATAL, "listen error", errno, strerror(errno));exit(3);}logMessage(NORMAL, "init server success");}void Start(){//version2 :signal(SIGCHLD,SIG_IGN); //对SIGCHLD,主动忽略SIGCHLD信号,子进程退出的时候,会自动释放自己的僵尸进程while (true){// sleep(1);// 获取连接struct sockaddr_in src;socklen_t len = sizeof src;// sock(未来真正进行IO) and _sock(主要任务:获取新链接)int servicesock = accept(listensock, (struct sockaddr *)&src, &len);if (servicesock < 0){logMessage(ERROR, "accept error", errno, strerror(errno));}// 获取连接成功uint16_t client_port = ntohs(src.sin_port);string client_ip = inet_ntoa(src.sin_addr);logMessage(NORMAL, "Link success, %d | %s : %d\n", servicesock, client_ip.c_str(), client_port);// 开始进行通信服务// version 1 -- 单进程循环 -- 只能一次处理一个客户端,处理完一个,才能处理下一个// 显然是不能被直接使用的?为什么?单进程.service(servicesock,client_ip,client_port);// version 2 -- 多进程版本 -- 创建子进程,// 让子进程给新的连接提供服务,子进程能不能打开父进程曾经打开的文件fd呢? 答案是当然可以!pid_t id = fork();assert(id != -1);if(id == 0){//子进程close(listensock);service(servicesock,client_ip,client_port);exit(0);//僵尸状态}//父进程close(servicesock);}}~TcpServer(){}private:uint16_t _port;string _ip;int listensock;unique_ptr<ThreadPool<Task>> _threadpool_ptr; };
tcp_server.cc文件
#include "tcp_server.hpp" #include <memory>static void usage(string proc) {cout << "Usage: " << proc << "ServerPort\n" << endl; }//./tcp_server port int main(int argc, char* argv[]) {if(argc != 2){usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);unique_ptr<TcpServer> svr(new TcpServer(port));svr->initServer();svr->Start();return 0; }
cline.cc文件
#include <iostream> #include <string> #include <cstdio> #include <unistd.h> #include <strings.h> #include <stdlib.h>#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> using namespace std; static void usage(string proc) {cout << "Usage: " << proc << "ServerIP ServerPort" << endl; } // ./tcp_clinet IP Prot int main(int argc, char *argv[]) {if (argc != 3){usage(argv[0]);exit(-1);}uint16_t serverPort = atoi(argv[2]);string serverIp = argv[1];int sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){cerr << "sokcet error" << endl;exit(2);}// client 不需要显式的bind,OS会自动选择// 更不需要监听,但是需要连接的能力connectstruct sockaddr_in server;bzero(&server, 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){cerr << "connect error" << endl;exit(3);}cout << "connect success!" << endl;while (true){string line;cout << "Please Enter Message# ";getline(cin, line);send(sock, line.c_str(), line.size(), 0);char buffer[1024];ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s] = 0;cout << "server echo# " << buffer << endl;}else if (s == 0){break;}else{break;}}close(sock);return 0; }
相关文章:

【Linux】socket编程(二)
目录 前言 TCP通信流程 TCP通信的代码实现 tcp_server.hpp编写 tcp_server.cc服务端的编写 tcp_client.cc客户端的编写 整体代码 前言 上一章我们主要讲解了UDP之间的通信,本章我们将来讲述如何使用TCP来进行网络间通信,主要是使用socket API进…...

七大出海赛道解读,亚马逊云科技为行业客户量身打造解决方案
伴随全球化带来的新机遇和国内市场的进一步趋于饱和,近几年,中国企业出海快速升温,成为了新的创业风口和企业的第二增长曲线。从范围上看,出海市场由近及远,逐步扩张。从传统的东南亚市场,到成熟的北美、欧…...

【Maven教程】(三)基础使用篇:入门使用指南——POM编写、业务代码、测试代码、打包与运行、使用Archetype生成项目骨架~
Maven基础使用篇 1️⃣ 编写 POM2️⃣ 编写业务代码3️⃣ 编写测试代码4️⃣ 打包和运行5️⃣ 使用 Archetype生成项目骨架 1️⃣ 编写 POM 到目前为止,已经大概了解并安装好了Maven环境, 现在,我们开始创建一个最简单的 Hello World 项目。如果你是初次…...

Spring Boot + Spring Data JPA 实现数据库操作的标准示例
环境搭建 本篇使用H2内存数据库演示Spring Data JPA 的使用。 导入JPA和H2的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><…...

JVM——类加载器
回顾一下类加载过程 类加载过程:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。 一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,这一步我们可以去完成还可以自定义…...

async/await 编程理解
博客主要是参考 Asynchronous Programming in Rust ,会结合简单的例子,对 async 和 await 做比较系统的理解,如何使用 async 和 await 是本节的重点。 async 和 await 主要用来写异步代码,async 声明的代码块实现了 Future 特性&a…...

1. Java基础知识介绍
文章目录 1. Java基础知识介绍1. 简单输入输出1.1 输入1.2 输出 2. 数据类型2.1 基本数据类型2.2 引用数据类型 3. 函数4. 类基础知识5. 小结 1. Java基础知识介绍 1. 简单输入输出 在Java编程中,我们经常需要与用户进行交互,以获取输入并输出结果。Ja…...

基础shell小技巧01
1. 命令替换 shell脚本中最有用的特性之一是可以从命令输出中提取信息并将其赋给变量。把输出赋给变量之后,就可以随意在脚本中使用了。在脚本中处理数据时,这个特性显得尤为方便。 有两种方法可以将命令输出赋给变量。 反引号(࿰…...

微信小程序--data的赋值与取值的几种方式
通过小程序官方文档可知: 赋值一定需要注意。需要setData的使用,这样页面才刷新,数据才会改变,并且分清that和this的使用 Page() 函数用来注册一个页面。接受一个 object 参数,其指定页面的初始数据、生命周期函数、事…...

条码通讯配置
由Leuze(劳易测) LSIS 222 M5M-R1/MA208i系统组成 连接说明及器件明细: 打开条码调试软件BPS Configuration tool对M208i进行ip 和端口号进行配置,条码选择作为客户端,输入ip和端口号,选择串口通讯波特…...

知网G4期刊《高考》简介及投稿要求
知网G4期刊《高考》简介及投稿要求 一、《高考》期刊简介: 主管单位:长春市委宣传部 主办单位:长春出版社 国内刊号22-1372/G4 国际刊号1673-6265 代号12-240 编辑单位:《高考》杂志社 出版周期:旬刊 类 …...

第7章 CPU前端优化
接下来讨论如何使用CPU监控特性寻找CPU上运行的代码中可被调优的位置。 标准的算法和数据结构在性能敏感型工作负载并不总能表现的很好。例如,在“扁平化”数据结构的冲击下,链表基本上快被放弃了。传统链表中的每个节点都是动态分配的,除了…...

idea新建Java-maven项目时,出现Dependency‘xxx(jar包名)‘ not found的解决方案
项目场景: 项目场景:使用idea创建maven项目时,导入简单依赖时(本文以mysql-connector-java为例)。 问题描述 问题: 首先,在创建新的maven项目中,出现下列两种情况: &am…...

STM32--USART串口
文章目录 通信接口串口通信硬件电路电平标准参数时序 USART主要特性框图 数据帧发送器 波特率发生器SWART串口发送与接收工程串口收发数据包 通信接口 通信接口是指连接中央处理器(CPU)和标准通信子系统之间的接口,用于实现数据和控制信息在不…...

2023年Java毕业设计题目推荐,怎样选题?500道毕业设计题目推荐
大家好,我是程序员徐师兄,最近有很多同学咨询,说毕业设计了,不知道选怎么题目好,有哪些是想需要注意的。 今天,我整理了一些Java毕业设计的题目,可以参考一下,希望对大家有所帮助 文章目录 一、…...

基于数据湖的多流拼接方案-HUDI概念篇
目录 一、为什么需要HUDI? 1. 传统技术选型存在哪些问题? 2. Hudi有什么优点? 基于 Hudi Payload 机制的多流拼接方案: 二、HUDI的应用场景 1. 什么场景适合使用hudi? 2. 什么场景不适合使用hudi? …...

OpenCV基础知识(5)— 几何变换
前言:Hello大家好,我是小哥谈。OpenCV中的几何变换是指改变图像的几何结构,例如大小、角度和形状等,让图像呈现出缩放、翻转、旋转和透视效果。这些几何变换操作都涉及复杂、精密的计算。OpenCV将这些计算过程都封装成了非常灵活的…...

Linux下源码安装MySQL 8.0
MySQL 8.0源码安装 环境准备步骤 环境准备 Linux环境,本文基于CentOS 8 MySQL安装包,本文基于MySQL 8.1,以下为带boost MySQL 8.1源码下载地址: https://dev.mysql.com/get/Downloads/MySQL-8.1/mysql-boost-8.1.0.tar.gz 步骤…...

大聪明教你学Java | 深入浅出聊 Java 内存模型
前言 🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。 🍊支持作者: 点赞👍、关注💖、留言💌~ 在多线程环境下,多个线程同时访问共享数据可能导致一系列问题,如数据不一致、竞态条件和死锁等…...

SAP ABAPG开发屏幕自动生成日期的搜索帮助
代码如下: REPORT z_jason_test_f4 . TABLES: s031. PARAMETER p_spmon TYPE spmon DEFAULT sy-datum0(6) OBLIGATORY. SELECT-OPTIONS s_spmon FOR s031-spmon DEFAULT sy-datum0(6) OBLIGATORY. AT SELECTION-SCREEN ON VALUE-REQUEST…...

leetcode 674. 最长连续递增序列
2023.8.24 与最长递增子序列 类似,不同的是, 本题要求连续序列,所以不需要第二层遍历比较之前所有的元素了,只需要比较上一个元素i-1。 dp[i]的含义为:以nums[i]元素为结尾的序列的最长递增子序列。 注意这里是以i为结…...

Mysql简短又易懂
MySql 连接池:的两个参数 最大连接数:可以同时发起的最大连接数 单次最大数据报文:接受数据报文的最大长度 数据库如何存储数据 存储引擎: InnoDB:通过执行器对内存和磁盘的数据进行写入和读出 优化SQL语句innoDB会把需要写入或者更新的数…...

vue 简单实验 v-model 变量和htm值双向绑定
1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"two-way-binding"><p>{{ message }}</p><input v-model"message" /> </div> <script>…...

测试框架pytest教程(8)失败重试-pytest-rerunfailures
pytest-rerunfailures是一个pytest插件,用于重新运行失败的测试用例。当测试用例在第一次运行时失败,该插件会自动重新运行指定次数的失败用例,以提高稳定性和减少偶发性错误的影响。 要使用pytest-rerunfailures插件,需要按照以…...

6个主流的工业3D管道设计软件
3D 管道设计软件是大多数行业工程工作的主要部分,例如: 电力、石油和天然气、石化、炼油厂、纸浆和造纸、化学品和加工业。 全球各工程公司使用了近 50 种工厂或管道设计软件。 每个软件都有优点和缺点,包括价格点。 EPC 和业主部门当前的趋势…...

基于微信小程序的垃圾分类系统设计与实现(2.0 版本,附前后端代码)
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 1 简介 视频演示地址: 基于微信小程序的智能垃圾分类回收系统,可作为毕业设计 小…...

基础论文学习(4)——CLIP
《Learning Transferable Visual Models From Natural Language Supervision》 CLIP的英文全称是Contrastive Language-Image Pre-training,即一种基于对比文本-图像对的预训练模型。CLIP是一种基于对比学习的多模态模型,与CV中的一些对比学习方法如moc…...

SpringBoot利用ConstraintValidator实现自定义注解校验
一、前言 ConstraintValidator是Java Bean Validation(JSR-303)规范中的一个接口,用于实现自定义校验注解的校验逻辑。ConstraintValidator定义了两个泛型参数,分别是注解类型和被校验的值类型。在实现ConstraintValidator接口时&…...

十、pikachu之php反序列化
文章目录 1、php反序列化概述2、实战3、关于Magic function4、__wakeup()和destruct() 1、php反序列化概述 在理解这个漏洞前,首先搞清楚php中serialize(),unserialize()这两个函数。 (1)序列化serialize():就是把一个…...

PHP“牵手”拼多多商品详情数据获取方法,拼多多API接口批量获取拼多多商品详情数据说明
拼多多商品详情接口 API 是开放平台提供的一种 API 接口,它可以帮助开发者获取拼多多商品的详细信息,包括商品的标题、描述、图片等信息。在拼多多电商平台的开发中,拼多多详情接口 API 是非常常用的 API,因此本文将详细介绍拼多多…...