TCP通信Socket编程----传输不同数据类型。

更新记录】:2021.12.29、

TCP通信传输字符串和浮点数。

作业题目说明:“实现基于TCP的socket通信,完成字符串、浮点数据的传输。”

mine_:我之前是想直接将用户输入的传过去就行了,但这样其实就是字符串形式传输了。而题目希望我们分别传输字符串和浮点数这两种格式,最终在网络中都是以字节形式传输,但是对于数据处理的方法不同。还有一个核心考察点就是需要思考粘包问题,即需要运用封包技术。python中一个浮点数的字节是4个字节,是固定的。而字符串的字节就不固定了。
mine_下次改进】:还有哪些封包技术?如何运用?深刻理解sendall()和recv()函数底层原理。dai对于socket底层要做一下总结。

#!/usr/bin/env python3
# coding=utf-8

from socket import *
import struct


class TcpClient(object):
    """
    题目要求:实现基于TCP的socket通信,完成字符串、浮点数据的传输。
    代码功能简述:客户端输入浮点数,然后传送给服务端,服务端再返回给客户端并打印,以实现echo功能,本代码重点处在于,为了防止粘包现象,先传输4字节的包,告诉服务端将要传输过来的数据的大小,以保证它能完整接受这一次的数据,然后再返回给客户端,客户端同样要用检查已收到的数据包的大小保证不粘包。
    代码中存在的疑问点:浮点数传过去13.4 真实输出的却不是13.4,而是一个不是13.4但很接近13.4d的多位浮点数,不知为啥。
    Tcp客户端
  
    """
    def __init__(self, server_ip, server_port):
        """初始化对象"""
        self.code_mode = "utf-8"    #收发数据编码/解码格式
        self.server_ip = server_ip
        self.server_port =server_port
        self.my_socket = socket(AF_INET, SOCK_STREAM)   #创建socket
    def get_input_float(self):
        '''要求用户输入一个浮点数
        '''
        while True:
            try:
                float_message=float(input())
            except ValueError:
                print('请输入一个浮点数')
                continue
            else:
                return float_message
                break

    def tcp_send_float(self):
        while True:
            print('请输入一个浮点数(输入0退出)')
            float_message=self.get_input_float()
            if float_message==0:
                break
            self.my_socket.sendall(struct.pack('i',4))# 将数据长度发过去
            self.my_socket.sendall(struct.pack('f',float_message))
            received_bytes=0
            message_received=b''
            while received_bytes<4:
                message_received+=self.my_socket.recv(4)
                received_bytes+=len(message_received)
            echo_float=struct.unpack('f',message_received)[0]
            print("%.1f"%echo_float)


    def tcp_send_string(self):
        while True:
            string_message=input('请输入一个字符串(输入exit退出)')
            if string_message=='exit':
                break
            self.my_socket.sendall(struct.pack('i',len(string_message)))# 将数据长度发过去
            self.my_socket.sendall(string_message.encode(self.code_mode))
            print('发去的数据长度',len(string_message))
            message_received=b''
            while len(message_received)<len(string_message):
                received=self.my_socket.recv(4)
                message_received+=received
            print(message_received.decode(self.code_mode))
            


    def run(self):
        """启动"""
        self.my_socket.connect((self.server_ip, self.server_port))  #连接服务器
        print('此程序为:测试TCP传输中的浮点数传输')
        self.tcp_send_float()
        print('此程序为:测试TCP传输中的字符串传输')

        self.tcp_send_string()
        self.my_socket.close()






if __name__ == "__main__":
    #server_ip = input("请输入服务器IP:")
    #server_port = int(input("请输入服务器Port:"))
    my_socket = TcpClient('127.0.0.1', 8070)
    my_socket.run()
#!/usr/bin/env python3
# coding=utf-8

from socket import *
import struct

class TcpServer(object):
    """
    实现基于TCP的socket通信,完成字符串、浮点数据的传输。
    题目要求:实现基于TCP的socket通信,完成字符串、浮点数据的传输。
    代码功能简述:客户端输入浮点数,然后传送给服务端,服务端再返回给客户端并打印,以实现echo功能,本代码重点处在于,为了防止粘包现象,先传输4字节的包,告诉服务端将要传输过来的数据的大小,以保证它能完整接受这一次的数据,然后再返回给客户端,客户端同样要用检查已收到的数据包的大小保证不粘包。
    代码中存在的疑问点:浮点数传过去13.4 真实输出的却不是13.4,而是一个不是13.4但很接近13.4d的多位浮点数,不知为啥。
    Tcp服务端"""
    def __init__(self, port):
        """初始化对象"""
        self.code_mode = "utf-8"    #收发数据编码/解码格式
        self.server_socket = socket(AF_INET, SOCK_STREAM)   #创建socket
        self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)   #设置端口复用
        self.server_socket.bind(("", port))     #绑定IP和Port
        self.server_socket.listen(100)  #设置为被动socket
        print("Listen...")

    def run(self):
        """运行"""
        while True:
            client_socket, client_addr = self.server_socket.accept()
            print("{} online".format(client_addr))
            while True:
                header=client_socket.recv(4)
                if not header:
                    break
                message_len=struct.unpack('i',header)[0]
                print('发来的又返回去的数据字节长度是',message_len)
                received_message=b''
                while len(received_message)<message_len:
                    received=client_socket.recv(4)
                    received_message+=received
                client_socket.sendall(received_message)
            print("{} offline".format(client_addr))
            client_socket.close()


if __name__ == "__main__":
    #port = int(input("请输入要绑定的Port:"))
    my_server = TcpServer(8070)
    my_server.run()

代码测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
目 录 第1篇 Linux网络开发基础 第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 POSIX 标准 4 1.3 Linux与UNIX的异同 5 1.4 操作系统类型选择和内核版本的选择 5 1.4.1 常见的不同公司发行的Linux异同 6 1.4.2 内核版本的选择 6 1.5 Linux的系统架构 7 1.5.1 Linux内核的主要模块 7 1.5.2 Linux的文件结构 9 1.6 GNU通用公共许可证 10 1.6.1 GPL许可证的历史 10 1.6.2 GPL的自由理念 10 1.6.3 GPL的基本条款 11 1.6.4 关于GPL许可证的争议 12 1.7 Linux软件开发的可借鉴之处 12 1.8 小结 13 第2章 Linux编程环境 14 2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的GCC编译器工具集 19 2.2.1 GCC简介 19 2.2.2 编译程序的基本知识 21 2.2.3 单个文件编译成执行文件 22 2.2.4 编译生成目标文件 22 2.2.5 多文件编译 23 2.2.6 预处理 24 2.2.7 编译成汇编语言 24 2.2.8 生成和使用静态链接库 25 2.2.9 生成动态链接库 26 2.2.10 动态加载库 29 2.2.11 GCC常用选项 31 2.2.12 编译环境的搭建 33 2.3 Makefile文件简介 34 2.3.1 一个多文件的工程例子 34 2.3.2 多文件工程的编译 36 2.3.3 Makefile的规则 37 2.3.4 Makefile中使用变量 39 2.3.5 搜索路径 43 2.3.6 自动推导规则 44 2.3.7 递归make 44 2.3.8 Makefile中的函数 46 2.4 用GDB调试程序 47 2.4.1 编译可调试程序 48 2.4.2 使用GDB调试程序 49 2.4.3 GDB常用命令 52 2.4.4 其他的GDB 59 2.5 小结 60 第3章 文件系统简介 61 3.1 Linux下的文件系统 61 3.1.1 Linux下文件的内涵 61 3.1.2 文件系统的创建 62 3.1.3 挂接文件系统 64 3.1.4 索引节点inode 65 3.1.5 普通文件 66 3.1.6 设备文件 66 3.1.7 虚拟文件系统VFS 68 3.2 文件的通用操作方法 72 3.2.1 文件描述符 72 3.2.2 打开创建文件open()、create()函数 72 3.2.3 关闭文件close()函数 76 3.2.4 读取文件read()函数 77 3.2.5 写文件write()函数 79 3.2.6 文件偏移lseek()函数 80 3.2.7 获得文件状态fstat()函数 83 3.2.8 文件空间映射mmap()函数 85 3.2.9 文件属性fcntl()函数 88 3.2.10 文件输入输出控制ioctl()函数 92 3.3 socket文件类型 93 3.4 小结 93 第4章 程序、进程和线程 94 4.1 程序、进程和线程的概念 94 4.1.1 程序和进程的差别 94 4.1.2 Linux环境下的进程 95 4.1.3 进程和线程 96 4.2 进程产生的方式 96 4.2.1 进程号 96 4.2.2 进程复制fork() 97 4.2.3 system()方式 98 4.2.4 进程执行exec()函数系列 99 4.2.5 所有用户态进程的产生进程init 100 4.3 进程间通信和同步 101 4.3.1 半双工管道 101 4.3.2 命名管道 107 4.3.3 消息队列 108 4.3.4 消息队列的一个例子 114 4.3.5 信号量 116 4.3.6 共享内存 121 4.3.7 信号 124 4.4 Linux下的线程 127 4.4.1 多线程编程实例 127 4.4.2 Linux下线程创建函数pthread_create() 129 4.4.3 线程的结束函数pthread_join()和pthread_exit() 129 4.4.4 线程的属性 130 4.4.5 线程间的互斥 132 4.4.6 线程中使用信号量 133 4.5 小结 136 第2篇 Linux用户层网络编程 第5章 TCP/IP协议族简介 138 5.1 OSI网络分层介绍 138 5.1.1 OSI网络分层结构 138 5.1.2 OSI的7层网络结构 139 5.1.3 OSI参考模型中的数据传输 140 5.2 TCP/IP协议栈 141 5.2.1 TCP/IP协议栈参考模型 141 5.2.2 主机到网络层协议 143 5.2.3 IP协议 144 5.2.4 网际控制报文协议(ICMP) 146 5.2.5 传输控制协议(TCP) 150 5.2.6 用户数据报文协议(UDP) 154 5.2.7 地址解析协议(ARP) 156 5.3 IP地址分类与TCP/UDP端口 158 5.3.1 因特网中IP地址的分类 159 5.3.2 子网掩码(subnet mask address) 161 5.3.3 IP地址的配置 162 5.3.4 端口 163 5.4 主机字节序和网络字节序 163 5.4.1 字节序的含义 164 5.4.2 网络字节序的转换 164 5.5 小结 166 第6章 应用层网络服务程序简介 167 6.1 HTTP协议和服务 167 6.1.1 HTTP协议概述 167 6.1.2 HTTP协议的基本过程 168 6.2 FTP协议和服务 170 6.2.1 FTP协议概述 170 6.2.2 FTP协议的工作模式 172 6.2.3 FTP协议的传输方式 172 6.2.4 一个简单的FTP过程 173 6.2.5 常用的FTP工具 173 6.3 TELNET协议和服务 174 6.3.1 远程登录的基本概念 174 6.3.2 使用TELNET协议进行远程登录的工作过程 174 6.3.3 TELNET协议 174 6.4 NFS协议和服务 176 6.4.1 安装NFS服务器和客户端 176 6.4.2 服务器端的设定 176 6.4.3 客户端的操作 177 6.4.4 showmount命令 177 6.5 自定义网络服务 177 6.5.1 xinetd/inetd 178 6.5.2 xinetd服务配置 178 6.5.3 自定义网络服务 179 6.6 小结 180 第7章 TCP网络编程基础 181 7.1 套接字编程基础知识 181 7.1.1 套接字地址结构 181 7.1.2 用户层和内核层交互过程 183 7.2 TCP网络编程流程 184 7.2.1 TCP网络编程架构 184 7.2.2 创建网络插口函数socket() 186 7.2.3 绑定一个地址端口对bind() 189 7.2.4 监听本地端口listen 192 7.2.5 接受一个网络请求accept() 194 7.2.6 连接目标网络服务器connect() 199 7.2.7 写入数据函数write() 200 7.2.8 读取数据函数read() 201 7.2.9 关闭套接字函数close() 201 7.3 服务器/客户端的简单例子 202 7.3.1 例子功能描述 202 7.3.2 服务器网络程序 203 7.3.3 服务器读取和显示字符串 205 7.3.4 客户端的网络程序 205 7.3.5 客户端读取和显示字符串 206 7.3.6 编译运行程序 206 7.4 截取信号的例子 207 7.4.1 信号处理 207 7.4.2 信号SIGPIPE 208 7.4.3 信号SIGINT 208 7.5 小结 208 第8章 服务器和客户端信息的获取 210 8.1 字节序 210 8.1.1 大端字节序和小端字节序 210 8.1.2 字节序转换函数 212 8.1.3 一个字节序转换的例子 214 8.2 字符串IP地址和二进制IP地址的转换 217 8.2.1 inet_xxx()函数 217 8.2.2 inet_pton()和inet_ntop()函数 219 8.2.3 使用8.2.1节地址转换函数的例子 220 8.2.4 使用函数inet_pton()和函数inet_ntop()的例子 223 8.3 套接字描述符判定函数issockettype() 223 8.3.1 进行文件描述符判定的函数issockettype() 224 8.3.2 main()函数 224 8.4 IP地址与域名之间的相互转换 225 8.4.1 DNS原理 225 8.4.2 获取主机信息的函数 226 8.4.3 使用主机名获取主机信息的例子 228 8.4.4 函数gethostbyname()不可重入的例子 230 8.5 协议名称处理函数 232 8.5.1 xxxprotoxxx()函数 232 8.5.2 使用协议族函数的例子 233 8.6 小结 236 第9章 数据的IO和复用 237 9.1 IO函数 237 9.1.1 使用recv()函数接收数据 237 9.1.2 使用send()函数发送数据 239 9.1.3 使用readv()函数接收数据 240 9.1.4 使用writev()函数发送数据 240 9.1.5 使用recvmsg()函数接收数据 242 9.1.6 使用sendmsg()函数发送数据 244 9.1.7 IO函数的比较 246 9.2 使用IO函数的例子 246 9.2.1 客户端处理框架的例子 246 9.2.2 服务器端程序框架 248 9.2.3 使用recv()和send()函数 249 9.2.4 使用readv()和write()函数 251 9.2.5 使用recvmsg()和sendmsg()函数 253 9.3 IO模型 256 9.3.1 阻塞IO模型 256 9.3.2 非阻塞IO模型 257 9.3.3 IO复用 257 9.3.4 信号驱动IO模型 258 9.3.5 异步IO模型 258 9.4 select()函数和pselect()函数 259 9.4.1 select()函数 259 9.4.2 pselect()函数 261 9.5 poll()函数和ppoll()函数 262 9.5.1 poll()函数 263 9.5.2 ppoll()函数 264 9.6 非阻塞编程 264 9.6.1 非阻塞方式程序设计介绍 264 9.6.2 非阻塞程序设计的例子 264 9.7 小结 266 第10章 基于UDP协议的接收和发送 267 10.1 UDP编程框架 267 10.1.1 UDP编程框图 267
网络编程-PING程序设计实验指导书 一.实验目的 (1)熟悉原始套接字编程。 (2)了解网络的结构。 (3)了解网络传输底层协议。 二.实验要求 PING程序是用于测试网络连通性的程序。要求在WINDOWS环境下实现基本的PING程序 功能. 在命令提示符下输入: PING ***.***.***.*** 其中***为目的主机的IP地址,不要求支持域名,对是否带有开关变量也不做要求。 不带开关变量时,要求返回4次响应。 返回信息的格式: REPLY FROM ***.***.***.*** 或 REQUEST TimeOut (无法PING通的情况) 三.实验原理 1、PING的工作原理 ping 程序是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这 台主机建立连接。ping 使用的是ICMP协议,它发送ICMP回送请求消息给目的主机。ICMP协议规定:目的主机必 须返回ICMP回送应答消息给源主机。如果源主机在一定时间内收到应答,则认为主机可 达。 ICMP协议通过IP协议发送的,IP协议是一种无连接的,不可靠的数据包协议。因此, 保证数据送达的工作应该由其他的模块来完成。其中一个重要的模块就是ICMP(网络控制 报文)协议。 当传送IP数据包发生错误--比如主机不可达,路由不可达等等,ICMP协议将会把错 误信息封包,然后传送回给主机。给主机一个处理错误的机会,这也就是为什么说建立 在IP层以上的协议是可能做到安全的原因。ICMP数据包由8bit的错误类型和8bit的代码 和16bit的校验和组成。而前 16bit就组成了ICMP所要传递的信息。 PING利用ICMP协议包来侦测另一个主机是否可达。原理是用类型码为0的ICMP发请求 ,受到请求的主机则用类型码为8的ICMP回应。ping程序来计算间隔时间,并计算有多少 个包被送达。用户就可以判断网络大致的情况。 IP数据报 TCP/IP协议定义了一个在因特网上传输的包, 称为IP数据报(IP datagram). 这是一个与硬件无关的虚拟包, 由包头和数据两部分组成, 包头中的源地址和目的地址都是IP协议地址. ICMP TCP/IP组件包括一个ICMP(Internet Control Message Protocol)协议, 该协议定义了的报文类型: Echo,Echo Reply,用于ping程序的基本实现 下图是ICMP报文的传送: 2、RAW模式的SOCKET编程 PING程序是面向用户的应用程序,该程序使用ICMP的封装机制,通过IP协议来工作。为 了实现直接对IP和ICMP包进行操作,实验中使用RAW模式的SOCKET编程。 熟悉SOCKET的编程,包括基本的系统调用如SOCKET、BIND等. 3、具体内容 (1) 定义数据结构 需要定义好IP数据报、ICMP包等相关的数据结构 (2) 程序实现 在WINDOWS环境下实现PING程序 四. 实验步骤和注意事项 1、 熟悉IP以及ICMP协议的工作机制 2、 熟悉RAW模式的SOCKET编程 3、编写PING的实现程序 4、编译环境中需要包括SOCKET库 WS2_32.lib 5、 在模拟实现环境下调试并运行自己编写的PING程序 6、最后提交源程序,撰写实验报告 初步流程图 具体步骤 1、定义IP头和ICMP头 该程序定义自己的IP头和ICMP头数据结构,代码如下: //IP首部数据结构 typedef struct iphdr { unsigned int h_len : 4 ; //首部长度 unsigned int version : 4 ; //版本 unsigned char tos ; //服务类型 unsigned short total_len ; //报文总长度 unsigned short ident ; //标识 unsigned short frag_and_flags ; //偏移量 unsigned char ttl ; //寿命 unsigned char proto ; //协议 unsigned short checksum ;// 首部校验和 unsigned int sourceIP ;// 源站IP unsigned int destIP ;// 目的站IP }; //ICMP首部数据结构 typedef struct icmphdr { BYTE i_type ; //类型 BYTE i_code ; //代码 USHORT i_cksum ; //首部校验和 USHORT i_id ; //标识 USHORT i_seq ; //序列号 ULONG timestamp ; //时间戳 }; 2.定义变量 WSADATA wsaDa
目 录 第1篇 Linux网络开发基础 第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 3 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 POSIX 标准 4 1.3 Linux与UNIX的异同 5 1.4 操作系统类型选择和内核版本的选择 5 1.4.1 常见的不同公司发行的Linux异同 6 1.4.2 内核版本的选择 6 1.5 Linux的系统架构 7 1.5.1 Linux内核的主要模块 7 1.5.2 Linux的文件结构 9 1.6 GNU通用公共许可证 10 1.6.1 GPL许可证的历史 10 1.6.2 GPL的自由理念 10 1.6.3 GPL的基本条款 11 1.6.4 关于GPL许可证的争议 12 1.7 Linux软件开发的可借鉴之处 12 1.8 小结 13 第2章 Linux编程环境 14 2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的GCC编译器工具集 19 2.2.1 GCC简介 19 2.2.2 编译程序的基本知识 21 2.2.3 单个文件编译成执行文件 22 2.2.4 编译生成目标文件 22 2.2.5 多文件编译 23 2.2.6 预处理 24 2.2.7 编译成汇编语言 24 2.2.8 生成和使用静态链接库 25 2.2.9 生成动态链接库 26 2.2.10 动态加载库 29 2.2.11 GCC常用选项 31 2.2.12 编译环境的搭建 33 2.3 Makefile文件简介 34 2.3.1 一个多文件的工程例子 34 2.3.2 多文件工程的编译 36 2.3.3 Makefile的规则 37 2.3.4 Makefile中使用变量 39 2.3.5 搜索路径 43 2.3.6 自动推导规则 44 2.3.7 递归make 44 2.3.8 Makefile中的函数 46 2.4 用GDB调试程序 47 2.4.1 编译可调试程序 48 2.4.2 使用GDB调试程序 49 2.4.3 GDB常用命令 52 2.4.4 其他的GDB 59 2.5 小结 60 第3章 文件系统简介 61 3.1 Linux下的文件系统 61 3.1.1 Linux下文件的内涵 61 3.1.2 文件系统的创建 62 3.1.3 挂接文件系统 64 3.1.4 索引节点inode 65 3.1.5 普通文件 66 3.1.6 设备文件 66 3.1.7 虚拟文件系统VFS 68 3.2 文件的通用操作方法 72 3.2.1 文件描述符 72 3.2.2 打开创建文件open()、create()函数 72 3.2.3 关闭文件close()函数 76 3.2.4 读取文件read()函数 77 3.2.5 写文件write()函数 79 3.2.6 文件偏移lseek()函数 80 3.2.7 获得文件状态fstat()函数 83 3.2.8 文件空间映射mmap()函数 85 3.2.9 文件属性fcntl()函数 88 3.2.10 文件输入输出控制ioctl()函数 92 3.3 socket文件类型 93 3.4 小结 93 第4章 程序、进程和线程 94 4.1 程序、进程和线程的概念 94 4.1.1 程序和进程的差别 94 4.1.2 Linux环境下的进程 95 4.1.3 进程和线程 96 4.2 进程产生的方式 96 4.2.1 进程号 96 4.2.2 进程复制fork() 97 4.2.3 system()方式 98 4.2.4 进程执行exec()函数系列 99 4.2.5 所有用户态进程的产生进程init 100 4.3 进程间通信和同步 101 4.3.1 半双工管道 101 4.3.2 命名管道 107 4.3.3 消息队列 108 4.3.4 消息队列的一个例子 114 4.3.5 信号量 116 4.3.6 共享内存 121 4.3.7 信号 124 4.4 Linux下的线程 127 4.4.1 多线程编程实例 127 4.4.2 Linux下线程创建函数pthread_create() 129 4.4.3 线程的结束函数pthread_join()和pthread_exit() 129 4.4.4 线程的属性 130 4.4.5 线程间的互斥 132 4.4.6 线程中使用信号量 133 4.5 小结 136 第2篇 Linux用户层网络编程 第5章 TCP/IP协议族简介 138 5.1 OSI网络分层介绍 138 5.1.1 OSI网络分层结构 138 5.1.2 OSI的7层网络结构 139 5.1.3 OSI参考模型中的数据传输 140 5.2 TCP/IP协议栈 141 5.2.1 TCP/IP协议栈参考模型 141 5.2.2 主机到网络层协议 143 5.2.3 IP协议 144 5.2.4 网际控制报文协议(ICMP) 146 5.2.5 传输控制协议(TCP) 150

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abner_iii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值