UDP套接字编程

news2024/11/24 17:46:09

文章目录

  • 一、IP地址和端口号
  • 二、网络字节序
  • 三、socket编程接口
    • 1.socket常见API
    • 2.sockaddr结构
  • 四、UDP套接字
    • 1.简单认识UDP协议
    • 2.利用UDP套接字实现网络版本大小写转换
      • (1)服务端
      • (2)客户端

一、IP地址和端口号

IP协议目前有两个版本,分别是IPV4和IPV6,IP地址是在IP协议中用来标识网络中不同主机的地址。在TCP/IP五层模型中网络IP层的IP数据包中,有两个IP地址,一个是源IP地址,一个是目的IP地址。源IP地址指的是发送信息一方的IP地址,目的IP地址指的是接收信息一方的IP地址。

然而,我们使用网络进行数据交互的时候,并不只是两台主机在信息交互,本质上是两台主机上的进程在进行信息交互。所以网络传输除了需要IP地址,还需要端口号。

端口号是传输层协议的内容,它用来标识一台主机上的一个进程的唯一性。端口号是2字节的16位整数,一个端口号只能被一个进程占用,端口号存在的意义是当对方主机将数据发送过来的时候,操作系统拿到这个数据之后通过端口号来确定要将数据交给哪一个进程来处理。传输层协议(TCP和UDP)的数据段中也有两个端口号,一个是源端口号,一个是目的端口号,它们分别用来描述数据是谁发的以及数据要发给谁。

IP地址标定互联网中一台主机的唯一性,端口号标定一台主机中进程的唯一性,所以IP地址和端口号共同标定互联网中进程的唯一性。网络通信的本质也是进程间通信。

二、网络字节序

内存中的多字节数据相对于内存地址有大端和小端之分,有些机器是大端序列(即低地址高字节),而有些机器是小端序列(即高地址低字节),由于不同机器的字节序不同,所以就会存在大端序列机器通过网络给小端序列机器发消息时出现数据错乱的问题。为了解决这个问题,TCP/IP协议规定,网络数据流应采用大端字节序

不管你的主机是大端序列机器还是小端序列机器,都要按照这个TCP/IP协议规定的网络字节序来发送或者接收数据。

如果你的主机是小端序列的机器,就需要先将数据转换成大端序列再进行网络发送,大端序列的机器可以忽略转换直接发送。

在接收数据的时候,如果你的主机是小端序列的机器,就需要先将网络接收下来的数据转成小端序列,这样你的机器才能正确读取数据。如果是大端序列的机器同样可以忽略转换直接读取。

C语言的库函数为我们提供了网络字节序和主机字节序转换功能,一共有四个函数,只需要调用这些函数就可以实现网络字节序和主机字节序之间的相互转换:

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位短整数。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

三、socket编程接口

1.socket常见API

socket就是套接字的意思,socket的API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6。socket网络编程接口分为两类,一类是用来支持网络通信的,这种叫作网络套接字;还有一类是用来支持本地通信的,这种叫做域间套接字,也被称作双向管道。

下面是几个常见的socket套接字编程接口:

创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器):
形参列表中domain表示的是socket的域,它用来标识是要进行网络通信还是本地通信;type表示的是套接字的类型,它用来标识我们通信的时候对应的报文类型,报文类型分为流式类型和用户数据报类型;protocol表示的是协议类型,网络通信时该参数设置为0即可。socket函数的返回值是int类型,如果创建socket成功则返回文件描述符 否则返回-1。

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);

2.sockaddr结构

由于网络套接字需要一套接口多种用途,既要支持网络通信,也要支持本地通信,但是网络通信的接口和本地通信的接口所需要的参数数据肯定是不一样的,所以socket这一套接口的设计者就设计了一套抽象结构,也就是sockaddr结构:

他们一共设计了三个结构体,分别是sockaddrsockaddr_insockaddr_un,其中sockaddr_in对应的是网络通信的结构,sockaddr_un对应的是本地通信的结构,它们的前16位用来标定地址类型,网络通信结构的地址类型是AF_INET,本地通信结构的地址类型是AF_UNIX

sockaddr结构是设计出来的第三个结构,它用来统一sockaddr_in结构和sockaddr_un结构,在使用的时候我们先定义sockaddr_in结构或者sockaddr_un结构,然后强转成sockaddr结构,它会通过前16位的地址类型来确定这是要进行网络通信还是本地通信。

在这里插入图片描述

网络通信必须有的两个数据分别是端口号和IP地址。在sockaddr_in中描述端口号的字段是sin_port,描述IP地址的字段是sin_addr。

在这里插入图片描述

端口号由程序员指定提供,但是需要注意的是端口号一定是网络通信双方都要拿到的,所以端口号一定会通过网络发送给对方,那么我们在使用的时候就要注意将该字段转换成网络字节序。另外,在指定端口号时不要指定0-1023之间的端口号,因为系统中一些网络服务都有自己的确定端口,重复的端口号必然导致服务无法启动,会造成冲突。

IP地址在sockaddr_in的sin_addr字段中,sin_addr其实也是一个结构体,它里面只封装了一个in_addr_t类型的变量,一般在设置IP地址的时候,我们将其设置成INADDR_ANY,这其实是一个宏,就是一个全0的数字,但这个宏有特殊的含义,它代表我们不关心会连接到哪一个IP地址,直接让操作系统帮我们连接到任意的IP地址,一般在写服务器的时候非常推荐将IP地址字段设置成INADDR_ANY

云服务器禁止我们连接云服务器上的所有确定的IP地址,所以云服务器上只能使用INADDR_ANY填充IP字段。

在这里插入图片描述

四、UDP套接字

1.简单认识UDP协议

UDP协议的全称是User Datagram Protocol,即用户数据报协议,它是传输层协议,它的特点是无连接的,不可靠传输,以及面向数据报的传输,在socket函数的形参type中,SOCK_DGRAM代表的就是UDP套接字。

在这里插入图片描述

2.利用UDP套接字实现网络版本大小写转换

(1)服务端

UDP套接字服务端首先要做的是解决网络的问题,然后再来扩展业务。UDP套接字建立网络通信的步骤比较简单,首先是创建套接字,然后填充端口号、IP地址等网络信息,最后bind网络信息即可完成网络通信渠道的建立。

所以我们先将UdpServer的网络通信搭建起来,首先是构造函数我们需要传递进端口号和IP地址来初始化服务器的端口号与IP地址,由于云服务器是禁止我们bind任何IP地址的,所以我们给IP地址默认为空串,使用INADDR_ANY来填充IP地址字段。

然后是创建套接字和bind网络信息,在bind网络信息之前先要填充sockaddr结构,由于我们实现的是网络通信,所以应该使用sockaddr_in,填充sockaddr_in里的端口号、协议家族和IP地址之后,再将sockaddr_in类型强转成sockaddr类型,因为bind函数接口的参数只支持sockaddr类型。

#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>

using namespace std;

static void Usage(const std::string porc)
{
    std::cout << "Usage:\n\t" << porc << " port [ip]" << std::endl;
}

class UdpServer
{
public:
    UdpServer(int port, const string& ip = "")
        : _sockFd(-1), _port(port), _ip(ip)
    {
    }

    ~UdpServer()
    {}

    // 服务器初始化
    void init()
    {
        // 1.创建套接字
        _sockFd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockFd < 0)
        {
            cerr << "socket error" << endl;
            exit(1);
        }
        cout << "socket success" << endl;

        // 2.bind
        // 2.1填充网络信息
        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()));
        
        // 2.2bind网络信息
        if(bind(_sockFd, (const sockaddr*)&local, sizeof(local)) < 0)
        {
            cerr << "bind error" << endl;
            exit(2);
        }
        cout << "bind success" << endl;

    }

    // 启动服务器
    void start()
    {
        while(true)
        {
            cout << "server running" << endl;
            sleep(1);
        }
    }
private:
    int _sockFd;    // sock文件描述符
    uint16_t _port; // 端口号
    string _ip;     // IP地址
};

// ./udpServer port [ip]
int main(int argc, char* argv[])
{
    if (argc != 2 && argc != 3) 
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
    {
        ip = argv[2];
    }

    UdpServer svr(port, ip);
    svr.init();
    svr.start();
    return 0;
}

搭建好网络通信的渠道以后,我们就可以让服务器运行起来,为客户端提供服务了:

我们要实现一个大小写转换功能的程序,客户端通过网络发送信息给服务端,服务端接收到信息之后对其进行处理,处理完成之后将转换好的信息再返回给服务端,主要是演示UDP套接字实现网络通信的过程。

所以我们在服务器启动之后,首先将服务器设置成死循环(因为服务器不能随便地挂机),将所有的服务写在这个死循环内。然后是从客户端读取信息,将信息的小写字母全部转换成大写字母,然后再将信息发送回给客户端。

UDP套接字的网络通信采用的读取接口是recvfrom函数,发送接口是sendto函数:

recvfrom:

  1. int sockfd:传入创建好的socket文件描述符即可。
  2. void * buf:需要将读取上来的数据保存到这个buf中。
  3. size_t len:填写一次需要读取的大小。
  4. int flags:设置为0,阻塞式读取数据。
  5. struct sockaddr * src_addr:这是一个输入输出型参数,用来接收远端的sockaddr结构信息。
  6. socklen_t * addrlen:这也是一个输入输出型参数,用来接收远端sockaddr结构体的大小。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

sendto:

  1. int sockfd:传入创建好的socket文件描述符即可。
  2. const void * buf:将buf中的内容发送到对端。
  3. size_t len:填写一次需要发送的大小。
  4. int flags:设置为0,阻塞式发送数据。
  5. struct sockaddr * src_addr:这是一个输入型参数,用来填写远端的sockaddr结构信息。
  6. socklen_t * addrlen:这也是一个输入型参数,用来填写远端sockaddr结构体的大小。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

服务端总代码

#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>

using namespace std;

static void Usage(const std::string porc)
{
    std::cout << "Usage:\n\t" << porc << " port [ip]" << std::endl;
}

class UdpServer
{
public:
    UdpServer(int port, const string &ip = "")
        : _sockFd(-1), _port(port), _ip(ip)
    {
    }

    ~UdpServer()
    {
    }

    // 服务器初始化
    void init()
    {
        // 1.创建套接字
        _sockFd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockFd < 0)
        {
            cerr << "socket error" << endl;
            exit(1);
        }
        cout << "socket success" << endl;

        // 2.bind
        // 2.1填充网络信息
        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()));

        // 2.2bind网络信息
        if (bind(_sockFd, (const sockaddr *)&local, sizeof(local)) < 0)
        {
            cerr << "bind error" << endl;
            exit(2);
        }
        cout << "bind success" << endl;
    }

    // 启动服务器
    void start()
    {
        char inbuffer[1024];
        char outbuffer[1024];
        while (true)
        {
            sleep(1);
            sockaddr_in peer; // 用来接收远端的网络信息
            memset(&peer, 0, sizeof(peer));
            socklen_t len = sizeof(peer);

            ssize_t s = recvfrom(_sockFd, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr *)&peer, &len);
            if (s == -1)
            {
                cerr << "recvfrom error" << endl;
                continue;
            }
            if (s > 0)
            {
                inbuffer[s] = '\0';
            }

            // 大小写转换
            for (int i = 0; i < strlen(inbuffer); i++)
            {
                if (isalpha(inbuffer[i]) && islower(inbuffer[i]))
                {
                    outbuffer[i] = toupper(inbuffer[i]);
                }
                else
                {
                    outbuffer[i] = inbuffer[i];
                }
            }
            

            // 将转换后的数据发送回给客户端
            sendto(_sockFd, outbuffer, strlen(outbuffer), 0, (const sockaddr *)&peer, len);
        }
    }

private:
    int _sockFd;    // sock文件描述符
    uint16_t _port; // 端口号
    string _ip;     // IP地址
};

// ./udpServer port [ip]
int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
    {
        ip = argv[2];
    }

    UdpServer svr(port, ip);
    svr.init();
    svr.start();
    return 0;
}

(2)客户端

UDP套接字客户端首先也是要解决网络问题才能进行通信,所以首先要创建套接字。然后我们需要知道服务器的IP地址和端口号,有了这两个东西我们才能知道要跟谁进行网络通信。

客户端与服务端不同的是,创建了套接字之后,客户端不需要bind网络信息,准确来说应该是不需要自己手动bind网络信息,让操作系统帮我们自动bind网络信息。非常不推荐自己手动bind网络信息,原因是如果我们的客户端在代码实现的地方就手动指定端口号,而其它客户端都采用操作系统指定的端口号,就有可能出现其它客户端先启动,并且操作系统给它指定的端口号跟我们客户端的端口号相同,那么我们的客户端就启动不了了。所以只有服务端要手动bind端口号,客户端只需要让操作系统帮我们自动bind端口号即可。

#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>
using namespace std;

static void Usage(std::string name)
{
    std::cout << "Usage:\n\t" << name << " server_ip server_port" << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    // 1. 根据命令行,设置要访问的服务器IP
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    // 2.创建套接字
    int sockFd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockFd < 0)
    {
        cerr << "socket error" << endl;
    }

    // 3.通信
    // 3.1填写远端信息
    sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());

    string outbuffer;
    char inbuffer[1024];
    while(true)
    {
        cout << "Please Enter #";
        getline(cin, outbuffer);

        sendto(sockFd, outbuffer.c_str(), outbuffer.size(), 0, (const sockaddr*)&server, sizeof(server));

        sockaddr_in tmp;
        memset(&tmp, 0, sizeof(tmp));
        socklen_t len = sizeof(tmp);
        ssize_t s = recvfrom(sockFd, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)&tmp, &len);
        if(s > 0)
        {
            inbuffer[s] = '\0';

            cout << "server echo# " << inbuffer << endl;
        }
    }

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【已解决】加载模型时报错:model_config = json.loads(model_config.decode(‘utf-8‘))

Author&#xff1a;AXYZdong 硕士在读 工科男 有一点思考&#xff0c;有一点想法&#xff0c;有一点理性&#xff01; 定个小小目标&#xff0c;努力成为习惯&#xff01;在最美的年华遇见更好的自己&#xff01; CSDNAXYZdong&#xff0c;CSDN首发&#xff0c;AXYZdong原创 唯…

科普 | 带你了解设备振动监测常见术语

一、前言 状态监测是工厂预测维修和主动维修的基础&#xff0c;是企业优化资源&#xff0c;提升生产运营水平和市场竞争力的前提。成功开展状态监测将保证工厂设备长周期、满负荷、安全可靠地运行&#xff0c;避免非计划停机造成的损失&#xff0c;降低维修成本和生产成本。 …

如何高效的完成域名实名认证

根据注册局和工信部要求&#xff0c;需上传实名资料的域名有&#xff1a;.cn/.com/.net/.top/.xyz/.vip/.club/.ren/.wang/.shop/.xin/.中国/.信息等&#xff0c;相关要求请查看http://www.west.cn/faq/list.asp?unid1348。 如果您帐号下的域名较少&#xff0c;可以在域名管理…

【Dom获取属性操作】JavaScript 全栈体系(九)

Web APIs 第一章 Web API 基本认知 一、变量声明 变量声明有三个 var let 和 const建议&#xff1a; const 优先&#xff0c;尽量使用const&#xff0c;原因是&#xff1a; const 语义化更好很多变量我们声明的时候就知道他不会被更改了&#xff0c;那为什么不用 const呢&am…

数据结构_第十关:二叉树的顺序结构——堆

目录 1. 二叉树的顺序结构 2.堆的概念及结构 3.堆的实现 3.1堆向下调整算法 3.2堆的创建 3.3堆的插入 3.4建堆的复杂度 3.5堆的删除 4.堆的代码实现 4.1堆的定义 4.2堆的函数实现 1&#xff09;堆的初始化 2&#xff09;堆的销毁 3&#xff09;堆的插入 4&#xf…

【0基础敲代码】如何使用使用SeaFile搭建私有云盘并公网访问

目录 1. 前言 2. SeaFile云盘设置 2.1 Owncould的安装环境设置 2.2 SeaFile下载安装 2.3 SeaFile的配置 3. cpolar内网穿透 3.1 Cpolar下载安装 3.2 Cpolar的注册 3.3 Cpolar云端设置 3.4 Cpolar本地设置 4. 公网访问测试 5. 结语 1. 前言 现在我们身边的只能设备…

VsCode SSH远程连接服务器【内网穿透公网连接】

文章目录1.前言2.VS code的安装和设置2.1 VS code的下载安装2.2 OpenSSH的启用2.3 为VS code配置ssh2.4 局域网内测试VS code的ssh连接2.5 Cpolar下载安装3.Cpolar端口设置3.1 Cpolar云端设置3.2 Cpolar本地设置4.公网访问测试5.结语1.前言 记得笔者小时候看电视&#xff0c;看…

Thinkphp常见漏洞利用

一、基础知识 1.ThinkPHP简介&#xff1a; ThinkPHP是一个开源&#xff0c;快速、简单的轻量级国产PHP开发框架&#xff0c;诞生于2006年初&#xff0c;原名FCS&#xff0c;2007年元旦正式更名为ThinkPHP。使用面向对象的开发结构和MVC模式&#xff0c;融合了Struts的思想和T…

【创建者模式】建造者模式

文章目录1、简介2、结构3、实现3.1、需求场景3.2、产品类3.3、抽象建造者类3.4、具体建造者类3.5、指挥者类3.6、测试类3.7、演示结果4、应用场景5、实操举例6、优缺点分析7、抽象工厂模式区别1、简介 建造者模式(Builder Pattern)旨在将一个复杂对象的构建与表示分离&#xf…

SpringCloud:ElasticSearch之索引库操作

ElasticSearch索引库就类似数据库表&#xff0c;mapping映射就类似表的结构。 我们要向ElasticSearch中存储数据&#xff0c;必须先创建“库”和“表”。 1.mapping映射属性 mapping是对索引库中文档的约束&#xff0c;常见的mapping属性包括&#xff1a; type&#xff1a;…

Hyperledger Fabric 2.2版本环境搭建

前言 部署环境: CentOS7.9 提前安装好以下工具 git客户端golangdockerdocker-composecurl工具 以下是个人使用的版本 git: 2.39.2golang: 1.18.6docker: 23.0.3dockkekr-compose: v2.17.2curl: 7.29.0 官方文档参考链接&#xff1a;跳转链接&#xff0c;不同的版本对应的官…

008:Mapbox GL添加比例尺scale功能

第008个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加比例尺scale功能 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共66行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https…

Linux0.11 管道(十一)

系列文章目录 Linux 0.11启动过程分析&#xff08;一&#xff09; Linux 0.11 fork 函数&#xff08;二&#xff09; Linux0.11 缺页处理&#xff08;三&#xff09; Linux0.11 根文件系统挂载&#xff08;四&#xff09; Linux0.11 文件打开open函数&#xff08;五&#xff09…

fMRI研究 | 社交情境下的混合情绪

导读 背景&#xff1a;神经科学通常都是单独研究各种情绪&#xff0c;而混合的情绪状态&#xff08;例如愉悦和厌恶、悲伤和快乐的共存&#xff09;在日常生活中很常见。心理生理学和行为学证据表明&#xff0c;混合情绪可能具有不同于其组成情绪的反应特征。然而&#xff0c;…

什么是JWT?

起源 需要了解一门技术&#xff0c;首先从为什么产生开始说起是最好的。JWT 主要用于用户登录鉴权&#xff0c;所以我们从最传统的 session 认证开始说起。 session认证 众所周知&#xff0c;http 协议本身是无状态的协议&#xff0c;那就意味着当有用户向系统使用账户名称和…

RocketMQ源码分析之监控指标分析

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 Rocketmq版本 version: 5.1.0 背景 继续上次的高可用topic二开已经有了一段时间&#xff0c;现在我们需要对我们的限流数据进行监控&#xff0c;所以现在我们来…

Qt中英文切换(涉及多种场景)

qt中英文切换涉及到一个软件两个文件&#xff0c;分别是QtLinguist、.ts文件和.qm文件。 1、在Pro中添加 TRANSLATIONS en.ts \ch.ts添加这个文件后qmake&#xff0c;然后如下操作点击更新&#xff1a; 这个时候会生成2两个文件en.ts和ch.ts。 2、将这两个文件添加到项目中…

C++ : 构造函数 析构函数

&#x1f535;前提引入 &#xff1a; 1如果一个类中什么成员都没有&#xff0c;称为空类&#xff0c;但空类并非什么都没有&#xff0c;在我们没有写任何东西时&#xff0c;编译器会自动生成6个默认成员函数。 2.默认成员函数 &#xff1a; 用户没有显式实现&#xff0c;编译器…

Redis快速上手

Redis快速上手 OVERVIEWRedis快速上手1.redis数据类型2.redis常用命令StringListSetSortedSetHashKey相关3.redis配置文件4.redis数据持久化5.hiredis使用连接数据库执行redis命令函数释放资源程序实例1.redis数据类型 key: 必须是字符串 - “hello” value: 可选的 String类型…

核心业务5:充值业务实现

核心业务5:我要充值 1.充值业务流程图 2.充值业务流程逻辑 3.数据库表 4.前端逻辑代码 5.汇付宝代码逻辑 6.尚融宝代码逻辑 7.幂等性判断原理和解决方案 8.代码规范和原理了解 核心业务5:我要充值 1.充值业务流程图