多个client的交互式请求(Linux,C)

Linux下C实现的聊天室 七年前写的练手程序,好不容易翻出来,回过头来看当年的代码,感慨良多。源码在文末,分享一下,谢谢。 ============================================================================================ 实现目标 一个在Linux下可以使用的聊天软件,要求至少实现如下功能: 1. 采用Client/Serv... 阅读详情

1. 多进程并发处理–fork

// wrap.h
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>

void perr_exit(const char *);
int Accept(int, struct sockaddr *, socklen_t *);
void Bind(int, const struct sockaddr *, socklen_t);
void Connect(int, const struct sockaddr *, socklen_t);
void Listen(int, int);
int Socket(int, int, int);
ssize_t Read(int, void *, size_t);
ssize_t Write(int, const void *, size_t);
void Close(int);
ssize_t Readn(int, void *, size_t);
ssize_t Writen(int, const void *, size_t);
static ssize_t my_read(int, char *);
ssize_t Readline(int, void *, size_t);
//wrap.c
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>

void perr_exit(const char *s){
	perror(s);
	exit(1);
}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr){
	int n;
	again:
		if ( (n = accept(fd, sa, salenptr)) < 0) {
			if ((errno == ECONNABORTED) || (errno == EINTR))
				goto again;
			else
				perr_exit("accept error");
		}
	return n;
}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen){
	if (bind(fd, sa, salen) < 0)
		perr_exit("bind error");
}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen){
	if (connect(fd, sa, salen) < 0)
		perr_exit("connect error");
}

void Listen(int fd, int backlog){
	if (listen(fd, backlog) < 0)
		perr_exit("listen error");
}

int Socket(int family, int type, int protocol){
	int n;
	if ((n = socket(family, type, protocol)) < 0)
		perr_exit("socket error");
	return n;
}

ssize_t Read(int fd, void *ptr, size_t nbytes){
	ssize_t n;
again:
	if ((n = read(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

ssize_t Write(int fd, const void *ptr, size_t nbytes){
	ssize_t n;
again:
	if ((n = write(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

void Close(int fd){
	if (close(fd) == -1)
	perr_exit("close error");
}

ssize_t Readn(int fd, void *vptr, size_t n){
	size_t nleft;
	ssize_t nread;
	char *ptr;
	ptr = vptr;
	nleft = n; // 还需要读取n个
	while (nleft > 0) {	// 还有需要读取的
		if ((nread = read(fd, ptr, nleft)) < 0) { // 读取异常
			if (errno == EINTR) // 错误码为EINTR(换行)
				nread = 0; // 本次读取0个字节(换行)
			else			// 错误码不为EINTR
				return -1; // 读取失败
		} 
		else if (nread == 0) // 如果本次读取0个字节(结束)
			break; 			// 退出循环
		nleft -= nread; // 还需要读取个数
		ptr += nread;
	}
	return n - nleft;
}

ssize_t Writen(int fd, const void *vptr, size_t n){
	size_t nleft;
	ssize_t nwritten;
	const char *ptr;
	ptr = vptr;
	nleft = n; // 还需要写出多少个
	while (nleft > 0) { // 还有需要写出的
		if ((nwritten = write(fd, ptr, nleft)) <= 0) { // 写出异常
			if (nwritten < 0 && errno == EINTR) // 换行
				nwritten = 0; // 本次写出0个字节
			else
				return -1; // 写出失败
		}
	
		nleft -= nwritten; // 还有需要写出的
		ptr += nwritten;
	}
	return n;
}


static ssize_t my_read(int fd, char *ptr){

	static int read_cnt;
	static char *read_ptr;

	char read_buf[100];
	if (read_cnt <= 0) {
	again:
		if ((read_cnt = read(fd, read_buf,sizeof(read_buf))) < 0) {
			if (errno == EINTR)
				goto again;
			return -1;
		} 
		else if (read_cnt == 0)
			return 0;
		read_ptr = read_buf;
	}
	read_cnt--;
	*ptr = *read_ptr++;
	return 1;
}

ssize_t Readline(int fd, void *vptr, size_t maxlen){
	ssize_t n, rc;
	char c, *ptr;
	ptr = vptr;
	for (n = 1; n < maxlen; n++) {
		if ( (rc = my_read(fd, &c)) == 1) {
				*ptr++ = c;
				if (c == '\n')
					break;
		} 
		else if (rc == 0) {
				*ptr = 0;
				return n - 1;
		} 
		else
			return -1;
	}	
	*ptr = 0;
	return n;
}
// server_ie.c
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000

int main(void){
	struct sockaddr_in servaddr, cliaddr;
	socklen_t cliaddr_len;
	int listenfd, connfd;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];
	int i, n;
	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	//socket()打开一个网络通讯端口,返回监听文件描述符listenfd
	//AF_INET -> IPV4
	//SOCK_STREAM -> TCP协议, SOCK_DGRAM -> UDP协议
	
	int opt = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	//设置socket描述符的选项SO_REUSEADDR为1,
	//表示允许创建端口号相同但IP地址不同的多个socket描述符
	
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);
	//servaddr整个结构体清零
	//设置地址类型为AF_INET
	//网络地址为INADDR_ANY,这个宏表示本地的任意IP地址
	//端口号为SERV_PORT,8000,从主机序转换成网络序,并赋值给服务器地址
	
	Bind(listenfd, (struct sockaddr *)&servaddr,sizeof(servaddr));
	//bind绑定一个固定的网络地址和端口号
	//listenfd, 服务器地址
	
	Listen(listenfd, 20);//最多允许20个客户端处于连接待状态
	
	printf("Accepting connections ...\n");
	while (1) {
		cliaddr_len = sizeof(cliaddr);
		connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
		//阻塞等待直到有客户端连接上来,返回客户端文件描述符
		n = fork(); // fork并发处理多个client的请求
		if (n == -1){
			perror("call to fork");
			exit(1);
		}
		else if (n == 0){
			close(listenfd);
			while (1){
				n = Read(connfd, buf, MAXLINE); //读取接收到的数据
				if (n == 0) {
					printf("the other side has been closed.\n");
					break;
				}
				printf("received from %d at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));
				for (i = 0; i < n; i++)
					buf[i] = toupper(buf[i]);
			
				Write(connfd, buf, n);//向客户端(文件描述符)传(写)信息
			}
			Close(connfd);
			exit(0);
		}
		else
			Close(connfd);
	}
}
// client_ie.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000

int main(int argc, char *argv[]){
	
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	int sockfd, n;
	sockfd = Socket(AF_INET, SOCK_STREAM, 0);
	//socket()打开一个网络通讯端口,返回对方文件描述符sockfd
	//AF_INET -> IPV4
	//SOCK_STREAM -> TCP协议, SOCK_DGRAM -> UDP协议
	
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	//将"127.0.0.1",转换成AF_INET(IPV4)地址,存入servaddr.sin_addr
	
	servaddr.sin_port = htons(SERV_PORT);
	//端口号从主机序转换成网络序,并赋值给服务器地址
	
	Connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr));
	//调用connect()连接服务器
	//服务器地址和端口servaddr SERV_PORT
	
	while (fgets(buf, MAXLINE, stdin) != NULL) {
		Write(sockfd, buf, strlen(buf));
		//向对方的文件描述符sockfd写一行
		n = Read(sockfd, buf, MAXLINE);
		//从对方的文件描述符sockfd读取
		if (n == 0)
			printf("the other side has been closed.\n");
		else
			Write(STDOUT_FILENO, buf, n);//读到的内容输出终端
	}
	Close(sockfd);//关闭连接
	return 0;
}
gcc server_ie.c wrap.c -o server_ie
gcc client_ie.c wrap.c -o client_ie
./server_ie
./client_ie

2. 系统调用–select

//server_ie2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000

int main(int argc, char **argv){

	int i, maxi, maxfd, listenfd, connfd, sockfd;
	int nready, client[FD_SETSIZE];
	ssize_t n;
	fd_set rset, allset;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];
	socklen_t cliaddr_len;
	struct sockaddr_in cliaddr, servaddr;
	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	//socket()打开一个网络通讯端口,返回监听文件描述符listenfd
	//AF_INET -> IPV4
	//SOCK_STREAM -> TCP协议, SOCK_DGRAM -> UDP协议
	
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);
	//servaddr整个结构体清零
	//设置地址类型为AF_INET
	//网络地址为INADDR_ANY,这个宏表示本地的任意IP地址
	//端口号为SERV_PORT,8000,从主机序转换成网络序,并赋值给服务器地址
	
	Bind(listenfd, (struct sockaddr *)&servaddr,sizeof(servaddr));
	//bind绑定一个固定的网络地址和端口号
	//listenfd, 服务器地址
	
	Listen(listenfd, 20); //最多允许20个客户端处于连接待状态
	maxfd = listenfd; //监听编号初始化
	maxi = -1; // 客户端数组索引
	
	for (i = 0; i < FD_SETSIZE; i++)
		client[i] = -1; //可用的客户端索引标记为-1
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);
	for ( ; ; ) {
		rset = allset; //构件赋值
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);//选择客户端
		if (nready < 0)
			perr_exit("select error");
			
		if (FD_ISSET(listenfd, &rset)) { //新的客户端连接
			cliaddr_len = sizeof(cliaddr);
			connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddr_len);
			printf("received from %d at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));
			for (i = 0; i < FD_SETSIZE; i++)
				if (client[i] < 0) {
					client[i] = connfd; //保存文件描述符
					break;
				}
			if (i == FD_SETSIZE) {
				fputs("too many clients\n",stderr);
				exit(1);
			}
			FD_SET(connfd, &allset); //增加新的文件描述符
			if (connfd > maxfd)
				maxfd = connfd; //选择
			if (i > maxi)
				maxi = i; // 可用的客户端
			if (--nready == 0)
				continue; //没有可读的文件描述符
		}
		
		for (i = 0; i <= maxi; i++) { //检查所有客户端的数据
			if ((sockfd = client[i]) < 0)
				continue;
			if (FD_ISSET(sockfd, &rset)) {
				if ((n = Read(sockfd, buf,MAXLINE)) == 0) { //处理已经关闭的客户端
					Close(sockfd);
					FD_CLR(sockfd, &allset);
					client[i] = -1;
				} 
				else {
					int j;
					for (j = 0; j < n; j++)
						buf[j] = toupper(buf[j]);
					Write(sockfd, buf, n);
				}
				if (--nready == 0)
					break; //没有可读的文件描述符
			}
		}
	}
}
Linux 下使用select 实现TCP server和TCP client 基于TCP(面向链接)的socket编程 Server端的流程如下: 1 创建Socket套接字 2 将套接字绑定到一个本地地址(IP)和端口(port)上 3 将套接字设置为监听模式,准备接收客户端请求(listen) 4 等待客户端请求,当请求到来后,接受链接请求,返回一个新的对应于此次链接的套接字(accept) 5 用返回的套接字和客户端进行通信(send/recv ... 阅读详情

相关推荐

Linux下使用C语言socket发送HTTP请求

本文参考Stevens的APUE和UNP1 #include &amp;amp;lt;netinet/in.h&amp;amp;gt; #include &amp;amp;lt;stdlib.h&amp;amp;gt; #include &amp;amp;lt;arpa/inet.h&amp;amp;gt; #include &amp;amp;lt;stdio.h&amp;amp;gt; #include &amp;amp;lt;sys

IT技术专栏 1716

实现server程序,支持多个client同时连接,为每一个client分配一个进程

/*server.c*/ #include #include #include #include #include #include #include #include #include #define PORT 1234 #define MAX_QUE 5 int main() {     struct sockaddr_in local_sockaddr;   

疯狂学linux 2708

linux openssl 编程 Client端

相关配置等请参看上一篇关于server端文章:http://blog.csdn.net/pingd/article/details/47805349 1.Client端源码: openssl_client.c #include #include #include #include #include #include #include #include #include #i

pingD的专栏 2160

linux网络编程-多client多server一个watcher

client.cpp-->client server.cpp-->server watcher.cpp+server.h-->watcher client.cpp #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <string> #include <unistd.h> #inclu

xiyangxiaoguo的博客 454

Linux C:Server和Client(TCP通信)

客户端不停的给服务器端发送消息,服务器端依次打印客户端发来的消息,直到客户端发送的消息为“quit”,客户端先退出,服务器端再退出。代码运行时需先运行服务器端代码,再运行客户端代码。 Server.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <arpa/inet.h> #include <s.

jingjing_Q的博客 3403

Linux C/C++ Socket 编程

null null null null

Jackey_Song的博客 1648

linux c语言socket编程实例(1server-nclient),一个服务器,多个客户端

服务器端代码: /************************************************** ##filename      : server.c ##author        : GYZ                               ##e-mail        : 1746902011@qq.com                 ##create...

天泉证道 4393

基于socket,一个server,多个client客户端间的即时通讯

网上很少这方面的资源,如果你是想开一个socket服务,进行多个client客户端之间的通讯,那就是找对了,就是这个例子,不会用可以m我。

linux C语言 socket的server、client 实现

你可以将上述代码分别保存为 server.c 和 client.c,并分别编译成可执行文件,然后在两个不同的终端窗口中运行它们。如果你使用的是不同的编程语言或开发环境,编译步骤可能会有所不同。通常,你需要指定套接字的类型(例如,SOCK_STREAM表示TCP套接字)和地址族(例如,AF_INET表示IPv4地址族)。在实际应用中,不可能像实例一中,server只接收一次,client只向server发送一次。// 关闭套接字, 只要关闭client,server端的recv就不会阻塞了,返回0!

坛城的博客 2691

不止是打字机效果:手把手教你用SpannableStringBuilder打造Android富文本AI对话界面

本文详细介绍了如何使用SpannableStringBuilder在Android平台上构建专业级AI对话界面,涵盖流式文本处理、富文本解析、交互优化等核心技术。通过实战代码示例,帮助开发者实现类似DeepSeek的AI产品交互效果,提升用户体验和应用专业度。

weixin_28358083的博客 199

基于IP2369与STM32的45W双向PD移动电源设计

USB PD(Power Delivery)是一种面向高功率、智能协商的通用快充协议,其核心在于通过BMC编码在CC线上完成供电能力(PDO)动态匹配与电压/电流精准调控。IP2369作为高度集成的PD协议管理芯片,内置PHY、协议引擎与MOSFET驱动,可卸载MCU实时通信压力;而STM32F103系列凭借低功耗STOP模式与丰富外设,成为电源状态管理与用户交互的理想协处理器。该组合显著提升系统鲁棒性与能效比,适用于便携式移动电源、嵌入式备用电源及PD协议教学验证等场景。本文围绕IP2369配置电阻精度、

weixin_42608299的博客 519

linux c dns请求器实现

linux环境下c语言实现dns请求器

yyn1019的博客 855

工程师必看:如何用定时截尾试验快速验证产品MTBF(附真实案例解析)

本文为硬件工程师详细解析了如何运用定时截尾试验快速验证产品MTBF(平均无故障工作时间)。通过七步实战法,结合工业物联网网关的真实案例,阐述了从参数设定、试验执行到结果判读的完整流程,并提供了MATLAB脚本实现自动化计算与可视化,帮助工程师在有限时间内科学评估产品可靠性。

weixin_29243063的博客 496

Linux系统---基于Pipe实现一个简单Client-Server system

本文的内容主要是基于Pipe实现一个简单Client-Server system系统。

顾得泉的博客 3345

Autosar诊断实战系列05-Dcm之0x27加解密服务配置开发详解

UDS 0x27服务实战开发介绍,对解锁尝试次数及delay时间进一步解释。

initiallizer的博客 1994

uni-app - 实现全屏自适应且不变形背景大图,设置一张图片占满全屏(根据手机或电脑尺寸自动拉伸背景图像),用于页面是一张平铺的大图做背景的情况,全端兼容

uni-app,vue.js,微信小程序-设置背景图自适应手机屏幕尺寸,页面中的背景图片高度和宽度自适应,UNI-APP里如何设置背景图自适应手机屏幕尺寸,如何实现一个图片铺满整个页面,并且可以根据用户机型进行自动修正,自适应背景大图片官方教程,一个页面如何将图像放到背景上,纯CSS实现,设置背景图片占满全屏,3种CSS实现背景图片全屏铺满自适应的方式,uni-app中如何让图片不带高度的自适应,兼容uni-app全端,支持【纯】微信小程序项目使用,uni-app-实现全屏自适应且不变形背景大图

🏆 前端领域优质创作者 1万+

基于linux 实现DNS Client请求

属于应用层的编程,对于我i们后续的协议理解是很有帮助的。上面提供的所有代码可以直接编译运行,执行的时候请检查电脑是否有网络,否则无法正常执行。

weixin_42271802的博客 814

爆肝整理:微信公众号文章HTML编写完整规范,运营必备手册

微信公众号文章排版总是出错?📱 这份完整的HTML编写规范文档帮你彻底解决!文档包含所有支持的标签、CSS属性、实用代码模板和注意事项 📋,让你快速掌握微信文章的HTML编写技巧,告别第三方编辑器依赖 ✅

XiaoqiangClub的博客 2311

C语言socket TCP/IP通讯Server/Client程序(Ubuntu Linux 24.04环境)

1. 当client连上server时,显示连接成功的信息,然后等待client的输入。2. 将client的输入在server端显示。

qiuzen的博客 962

AI在智能制造中的落地:从预测维护到自适应生产调度

从最新的研究与实践来看,“预测维护”和“自适应生产调度”正在成为智能制造中最先跑通闭环的两个核心场景:前者通过对设备健康的前瞻性预测,帮助企业减少非计划停机、优化备件与维护资源;后者通过对复杂生产系统的实时决策优化,使工厂能够更好地应对订单波动、设备故障和多目标约束。更重要的是,越来越多的研究与项目不再将二者视为孤立模块,而是尝试构建“设备健康—生产调度—供应链”三者联动的一体化架构。Nature在技术层面,深度学习、强化学习、多智能体系统、图神经网络和注意力机制等方法,为建模复杂工业系统提供了强大工具;

nmdbbzcl的博客 1290

宇树G1二次开发资料汇总

宇树科技G1是一款29自由度的商业级人形机器人,配备NVIDIA Jetson Orin NX计算平台和完整SDK生态。本文系统介绍了G1的硬件架构、29个关节配置、传感器外设、双计算单元设计等关键信息,并详细解析了软件生态体系,包括C++/Python SDK、ROS2接口、仿真工具和AI框架。开发者可通过DDS通信实现高层运动控制或底层关节级精确控制,支持快速二次开发。文档提供了网络配置、接口调用示例等实用指南,帮助开发者高效利用该平台进行人形机器人应用开发。

m0_47999117的博客 1319

Linux 下 Oracle 12C Instant Client 安装及配置

Linux 下 Oracle 12C Instant Client 安装及配置 【下载地址】Linux下Oracle12CInstantClient安装及配置分享 Linux 下 Oracle 12C Instant Client 安装及配置本仓库提供了一个资源文件的下载,该资源文件用于在Linux系统下安装和配置Ora...

gitblog_06594的博客 650

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

linux 网络编程sever接收多个client数据并返回

可以直接使用 #include<stdio.h> #include<netinet/in.h>//forsouockaddr_in #include<sys/types.h> #include<sys/socket.h> #include<errno.h> #include<stdlib.h...

HO_PE的博客 3018

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

LINUX 实现socket与多线程结合与多个TCP client实现双向通信

上一节中,我们编写了socket单播1v1的双向通讯,但socket本身可以是1v多的,而我们的思路主要是通过主线程管理连接socket,并创建多线线程对单个连接进行单独处理,这样我们实现了多个socket连接双向通讯问题。socket 在创建线程时会将参数client_sock(accept返回的id)传入线程,在线程中会对malloc申请的空间进行释放。1.server.c 主线处理连接,创建线程与对应客服端id进行双向通信。3.client_2.c 发送并接收(其实与client_1.c一样的)

qq_73391589的博客 662

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

linux 设置多个ldap client,Linux下LDAP Server/Client配置

OpenLDAP 在Linux上的配置与使用最近由于工作的原因,需要做一些DevOps和Saas相关的工作,需要使用到LDAP来搭建一个统一的账号管理认证平台,把需要用到的N台Linux Server配置好LDAP服务。××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××...

weixin_35771315的博客 281
上一篇: 基于TCP协议,简单的网络程序(Linux,C)
下一篇: UDP协议网络程序(Liunx,C)
Janton Wang
博客等级 码龄9年 44粉丝 101原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值