linux网络编程相关面试题

1、TCP 和 UDP 的区别

1.TCP是面向连接的协议,建立和释放连接需要进行三次握手和四次挥手。UDP是面向无连接的协议,无需进行三次握手和四次挥手。说明udp比TCP实时性更强。

2.TCP 是流式传输,没有边界,但保证顺序和可靠。UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。

3.TCP连接的可靠性强,UDP的可靠性不强。

4.TCP只能一对一,UDP支持一对多和多对多。

5.TCP的头部开销比UDP大。TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。UDP 首部只有 8 个字节,并且是固定不变的,开销较小。

TCP 提供了一系列的可靠传输机制来保证它这个传输是可靠的,相较而言的话,那它的传输速度就是慢的.

UDP 没有做这个可靠的控制,它只是尽力而为,所以说它的传输速度是快的,而且占用的资源也会更小一些。具体使用的话要看不同的那个业务场景来进行相关的使用。

使用场景

TCP和UDP是两种常用的传输协议,它们分别适用于不同的网络通信场景。

TCP协议可靠性较高,适用于数据传输的可靠性要求较高的场景,例如传输大文件或需要确保所有数据都能到达接收端的应用,如FTP、HTTP等应用程序。

而UDP协议则适用于对实时性要求较高的场景,例如音视频流媒体、在线游戏等 。

2、TCP 三次握手四次挥手过程

三次握手过程:第一次握手:客户端向服务器端发送连接请求报文段,包含自身数据通讯初始序号,进入SYN-SENT状态。第二次握手:服务器端收到连接请求报文段后,如果同意,发送应答,包含自身数据通讯初始序号,进入SYN-RECEIVED状态。第三次握手:客户端收到应答,最后向服务器端发送确认报文,进入ESTABLISHED状态,此时成功建立长连接。

四次挥手过程:首先第一次挥手:客户端认为数据发送完毕,需要向服务器端发送连接释放请求。第二次挥手:服务器收到连接释放请求,告诉应用层释放TCP连接。然后发送ACK包,进入CLOSE-WT状态,此时表明客户端到服务器端的连接已经释放,不再接受客户端的数据。因为TCP是全双工的,所以服务器仍可以发送数据。第三次挥手:当服务器端数据发送完毕,向客户端发送连接释放请求,进入LAST-ACK状态。第四次挥手:客户端收到连接释放请求,向服务器端发送确认应答报文,此时客户端进入TIME-WT状态,持续2倍的MSL(最长报文段寿命),若期间没有收到服务器端的数据报文,进入CLOSED状态。服务器端收到确认应答后,也进入CLOSED状态。

3、TIME_WAIT状态

产生TIME_WT的原因主要是为了实现TCP全双工连接的可靠释放,当主动发起连接释放请求的一方最后发送ACK确认数据包在网络中丢失时,由于TCP的重传机制,被动关闭的一方会重新发送FIN,在FIN到达主动关闭的一方之前,主动关闭的一方需要维持这条连接,也就是主动的一方TCP资源不可以释放,直到被动关闭一方的FIN到达之后,主动关闭方重新发送ACK确认数据包,经过2MSL时间周期没有再收到被动关闭一方的FIN之后,才会恢复到CLOSED状态,如果没有TIME_WT这个状态,当FIN到达时,主动方会用RST来响应,在被动关闭的一方看来似乎是一个错误,实际上是正常的连接释放过程。

4、TCP 的流量控制

一般都希望数据能传输得越快越好,但是如果发送方把数据发送得过快,接收方可能就来不及接受到所有的数据,中间可能会丢失数据报。而流量控制就是让发送方的发送速率不要过快,让接收方来得及接收所有的数据,利用滑动窗口这个机制可以很方便的实现在TCP连接上控制对方发送数据报的速率。

5、TCP拥塞控制机制

拥塞控制就是防止太多的数据进入到网络中,这样可以使网络中的路由器或者链路不会过载,首先要求当前的网络可以承受住现有的网络负荷,它是一个全局性的过程,拥塞控制的算法有以下四种:慢开始、拥塞避免、快重传、快恢复。

6、TCP滑动窗口

在流量控制中那些已经被客户端发送但是还未被确认的分组的许可序号范围可以被看成是一个在序号范围内长度为N的窗口,随着TCP协议的运行、数据的运输,这个窗口在序号空间向前滑动,因此这个窗口被称为滑动窗口。

7、UDP 实现可靠的传输?

UDP不是面向连接的协议,因此资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。如果想要使用UDP还要保证数据的可靠传输,就只能通过应用层来做文章。实现的方式可以参考TCP的可靠传输机制,差别就是将TCP传输层功能,如确认机制、重传功能、流量控制、拥塞控制等功能实现在了应用层。 加分回答 在应用层实现可靠传输关键点有两个,从应用层角度考虑分别是:

1. 提供超时重传机制,能避免数据报丢失的问题。 

2. 提供确认序列号,保证数据拼接时候的正确排序。 请求端:首先在UDP数据报定义一个首部,首部包含确认序列号和时间戳,时间戳是用来计算RTT(数据报传输的往返时间),计算出合适的RTO(重传的超时时间)。然后以等-停的方式发送数据报,即收到对端的确认之后才发送下一个的数据报。当时间超时,本端重传数据报,同时RTO扩大为原来的两倍,重新开始计时。 响应端:接受到一个数据报之后取下该数据报首部的时间戳和确认序列号,并添加本端的确认数据报首部之后发送给对端。根据此序列号对已收到的数据报进行排序并丢弃重复的数据报。

8、select 的原理以及缺点

select 是 一种 IO 多路复用技术,它的主旨思想是:

1. 首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中,这个文件描述符的列表数据类型为 fd_set,它是一个整型数组,总共是 1024 个比特位,每一个比特位代表一个文件描述符的状态。比如当需要 select 检测时,这一位为 0 就表示不检测对应的文件描述符的事件,为 1 表示检测对应的文件描述符的事件。

2. 调用 select() 系统调用,监听该列表中的文件描述符的事件,这个函数是阻塞的,直到这些描述符中的一个或者多个进行 I/O 操作时,该函数才返回,并修改文件描述符的列表中对应的值,0 表示没有检测到该事件,1 表示检测到该事件。函数对文件描述符的检测的操作是由内核完成的。

3. select() 返回时,会告诉进程有多少描述符要进行 I/O 操作,接下来遍历文件描述符的列表进行 I/O 操作。

select 的缺点:

1. 每次调用select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大;

2. 同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大;

3. select 支持的文件描述符数量太小了,默认是 1024(由 fd_set 决定);

4. 文件描述符集合不能重用,因为内核每次检测到事件都会修改,所以每次都需要重置;

5. 每次 select 返回后,只能知道有几个 fd 发生了事件,但是具体哪几个还需要遍历文件描述符集合进一步判断。

9、 epoll 的原理

调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个 红黑树 用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件.

当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。而且,通常情况下即使我们要监控百万计的句柄,大多一次也只返回很少量的准备就绪句柄而已,所以,epoll_wait仅需要从内核态copy少量的句柄到用户态而已.

那么,这个准备就绪list链表是怎么维护的呢?

当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。

epoll相比于select并不是在所有情况下都要高效,例如在如果有少于1024个文件描述符监听,且大多数socket都是出于活跃繁忙的状态,这种情况下,select要比epoll更为高效,因为epoll会有更多次的系统调用,用户态和内核态会有更加频繁的切换。

10、 epoll 的两种工作模式

1. LT 模式(水平触发)是缺省的工作方式,并且同时支持 Block 和 Nonblock Socket。内核检测到一个文件描述符就绪了,然后可以对这个就绪的 fd 进行 IO 操作,如果不作任何操作,内核还是会继续通知。 

2. ET 模式(边沿触发)是高速工作方式,只支持 Nonblock socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过 epoll 检测到。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了

ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件描述符的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

11、TCP/IP 五层模型

1. 应用层:应用层是体系结构中的最高层,定义了应用进程间通信和交互的规则。本层任务就是通过应用进程间的信息数据流通完成特定的网络应用(软件、Web应用等)。因为不同的应用程序都需要不同的应用层协议,所以应用层协议较多,如万维网应用的HTTP协议、电子邮件的SMTP协议、文件传送的DTP协议等。请将应用层交互的数据称为报文,以免产生概念的混淆。 协议:HTTP、HTTPS、FTP、TFTP、SMTP等

2. 运输层:运输层的任务是负责向两个计算机中进程之间的通信提供一种通用的数据传输服务,应用层通过运输层可以传输报文。通用是指不会针对特定的应用层协议进行详细的划分,多种应用层协议公用同一个运输层服务,所以运输层有复用的功能。当然也有分发的功能,指将接受到的信息分别交付到应用层不同的进程中。 协议:UDP、TCP

3. 网络层:网络层的任务是负责为网络上不同的主机提供通信服务。在发送数据时,网络层将运输层产生的报文段或者用户数据报封装成分组或者包(packet)进行传送。由于网络层使用IP协议,所以分组或包(packet)也叫IP数据报,简称数据报。网络层还需要寻找合适的路由路线,让源主机运输层发送下来的数据报能通过路由器找到目的主机。 协议:ICMP、IGMP、IP(IPv4、IPv6)、ARP、RARP

4. 数据链路层:数据链路层简称链路层。两个节点传输数据时,链路层将网络层交下来的数据报组装成帧,在链路上传送帧。每一帧都包括数据和控制信息(同步信息、地址信息、差错控制等)。

5. 物理层:物理层上数据的单位是Bit比特,数据的传输都是通过0(或1)比特流来实现的,而0(或1)比特流与电压的高低有关。物理层中比特流的传输不再加控制信息,需要注意的是比特流应从首部开始传送。

12、阻塞/非阻塞模式下,send 和 recv 函数行为是什么样子的?

在阻塞模式下,send 和 recv 函数的行为如下:

  • send:当调用 send 函数时,它会尝试发送数据。如果网络缓冲区有足够的空间,那么 send 会立即返回并报告发送的字节数。如果网络缓冲区已满,send 会阻塞调用线程,直到有足够的空间发送数据为止。
  • recv:当调用 recv 函数时,它会尝试从网络缓冲区中接收数据。如果网络缓冲区中有数据,那么 recv 会立即返回并返回接收到的数据。如果网络缓冲区中没有数据,recv 会阻塞调用线程,直到有数据到达为止。

在非阻塞模式下,send 和 recv 函数的行为如下:

  • send:当调用 send 函数时,它会尝试发送数据。如果网络缓冲区有足够的空间,那么 send 会立即返回并报告发送的字节数。如果网络缓冲区已满,send 不会阻塞调用线程,而是会立即返回一个错误,通常是 EWOULDBLOCK 或 EAGAIN,表示操作不能立即完成。
  • recv:当调用 recv 函数时,它会尝试从网络缓冲区中接收数据。如果网络缓冲区中有数据,那么 recv 会立即返回并返回接收到的数据。如果网络缓冲区中没有数据,recv 不会阻塞调用线程,而是会立即返回一个错误,通常是 EWOULDBLOCK 或 EAGAIN,表示没有数据可接收

在非阻塞模式下,你需要自己处理这些错误,通常是通过使用某种形式的轮询或事件通知机制来重新尝试发送或接收数据。

13、 accept 函数调用时,三次握手是否已经完成?

accept函数调用时,三次握手已经完成。当服务器端的socket处于LISTEN状态时,如果收到客户端的SYN报文,服务器端会回应一个SYN+ACK报文,此时TCP连接处于半开状态,即服务器已经收到客户端的SYN报文,但是客户端还没有收到服务器的SYN+ACK报文。当客户端收到服务器的SYN+ACK报文后,会向服务器发送一个ACK报文,此时TCP连接建立成功,服务器端会从半开连接队列中取出一个连接,创建一个新的socket,并返回给accept函数。因此,当accept函数返回时,说明三次握手已经完成,客户端和服务器之间已经建立了可靠的连接。

14、socket编程如何实现半关闭状态?

在Socket编程中,半关闭状态(half-closed state)指的是TCP连接中的一方关闭了其发送通道,但接收通道仍然保持打开的状态。这通常发生在应用程序调用shutdown()函数并指定关闭发送通道(例如,shutdown(sockfd, SHUT_WR)),或者是在写入端发生错误或关闭时自动进入的状态。

要实现半关闭状态,你可以按照以下步骤操作:

  1. 创建Socket并连接:首先,你需要创建一个Socket,并通过connect()函数(对于客户端)或bind()listen()函数(对于服务器)建立连接。

  2. 发送和接收数据:在连接建立后,你可以使用send()recv()函数发送和接收数据。

  3. 关闭发送通道:当你想关闭发送通道但保持接收通道打开时,可以调用shutdown()函数并传入SHUT_WR参数。这会导致TCP发送一个FIN包给对端,告知对端本端已经完成了数据的发送。

    shutdown(sockfd, SHUT_WR);
  4. 继续接收数据:在关闭发送通道后,你仍然可以调用recv()函数从连接中接收数据,直到对端也关闭了连接或发生错误。

  5. 关闭Socket:当你完成所有操作并准备关闭整个连接时,可以调用close()函数来关闭Socket。

    close(sockfd);

请注意,半关闭状态是TCP协议的一个特性,它允许应用程序在不需要发送更多数据时,仍然能够接收对端发送的数据。这在某些应用场景中非常有用,比如一个客户端在发送完请求后仍然需要等待服务器的响应。

另外,要注意的是,不是所有的操作系统和网络库都支持半关闭状态。在某些情况下,关闭发送通道可能会导致整个连接被关闭。因此,在实际编程中,你需要测试你的代码以确保它在你的目标平台上按预期工作。

15、 epoll 边缘模式下,某次读取了某个 socket 上的部分数据,下次是否会出发读事件?如果此时又来了一个字节的新数据,是否会触发读事件?

在 epoll 的边缘触发(Edge Triggered, ET)模式下,事件通知的行为与水平触发(Level Triggered, LT)模式有所不同。边缘触发模式更关注于状态的变化,而不是当前的状态。

对于读事件,假设你已经在 epoll 的边缘触发模式下注册了一个 socket,并且某次从这个 socket 上读取了部分数据,但还没有读取完。

  1. 下次是否会触发读事件?

    如果 socket 上还有剩余的数据没有读取,但你没有再次调用 epoll_wait() 或类似的函数来重新检查事件,那么即使数据还在 socket 的接收缓冲区中,也不会再次触发读事件。这是因为边缘触发模式只会在状态从非就绪变为就绪时触发事件,而不会因为状态持续为就绪而反复触发。你需要主动调用 epoll_wait() 来再次检查事件。

  2. 如果此时又来了一个字节的新数据,是否会触发读事件?

    这取决于具体的操作系统和 epoll 的实现。一般来说,在边缘触发模式下,仅仅因为一个字节的新数据到达可能不会立即触发新的读事件。你需要再次调用 epoll_wait() 来检查是否有新的事件。然而,有些系统或库可能在内部实现了某种机制,当有新数据到达时能够触发新的事件,但这并不是边缘触发模式的标准行为。

总之,在 epoll 的边缘触发模式下,你需要主动调用 epoll_wait() 或类似的函数来检查事件,而不能依赖于系统在有新数据到达时自动触发新的事件。同时,你需要确保每次读取操作都尽可能多地读取数据,以减少剩余数据留在接收缓冲区中的情况,从而避免丢失事件通知。

16、epoll 边缘模式建议尽量一次把数据读完,怎样判断当前数据已经读完?

在 epoll 的边缘触发(Edge Triggered, ET)模式下,确实建议尽量一次把数据读完,以避免因为部分读取而导致的数据丢失或事件通知丢失的问题。要判断当前数据是否已经读完,你可以采取以下几种策略:

  1. 使用固定长度的协议
    如果你的应用使用的是固定长度的协议,那么每次读取固定长度的数据后,你就知道已经读取了一个完整的数据包。

  2. 使用特殊的数据结束标记
    类似于 HTTP 的 \r\n 或其他自定义的结束标记,你可以定义一个或多个字节作为消息的结束标志。读取数据时,不断检查是否遇到了这个结束标记。

  3. 使用消息头中的长度字段
    许多协议在消息头部包含一个表示消息长度的字段。你可以首先读取这个长度字段,然后根据这个长度来读取剩余的数据。

  4. 使用非阻塞 I/O 或 I/O 多路复用
    结合 epoll,你可以将 socket 设置为非阻塞模式。这样,当 recv 或 read 调用无法立即读取更多数据时,它们会立即返回而不是阻塞。你可以检查返回值来确定是否还有更多数据需要读取。如果 recv 或 read 返回 -1 并设置 EAGAIN 或 EWOULDBLOCK 错误,这表示当前没有更多数据可读,你可以稍后再试。

  5. 读取到 EOF
    当对方关闭连接时,recv 或 read 会返回 0,表示没有更多数据可读。你可以检查这个条件来确定是否所有数据都已经读取完毕。

  6. 使用超时机制
    为了避免无限期的等待,你可以为读取操作设置一个超时时间。如果在超时时间内没有读取到任何数据或没有读取到完整的数据包,你可以认为当前已经没有更多数据可读,并处理这种情况。

17、进程和线程的区别

1. 进程有独立的地址空间,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间;

2. 进程和线程切换时,需要切换进程和线程的上下文,进程的上下文切换时间开销远远大于线程上下文切换时间,耗费资源较大,效率要差一些; 、

3. 进程的并发性较低,线程的并发性较高;

4. 每个进程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制;

5. 系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了 CPU 外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源;

6. 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

18、进程通信的方式有哪些

管道、命名管道、信号、消息队列、共享内存、内存映射、信号量、Socket

3. 信号 信号是 Linux 进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。

4. 消息队列 消息队列就是一个消息的链表,可以把消息看作一个记录,具有特定的格式以及特定的优先级,对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程则可以从消息队列中读走消息,消息队列是随内核持续的。

5. 共享内存 共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称为一个进程用户空间的一部分,因此这种 IPC 机制无需内核介入。所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。与管道等要求发送进程将数据从用户空间的缓冲区复制进内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种 IPC 技术的速度更快。

6. 内存映射 内存映射(Memory-mapped I/O)是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。

7. 信号量 信号量主要用来解决进程和线程间并发执行时的同步问题,进程同步是并发进程为了完成共同任务采用某个条件来协调它们的活动。对信号量的操作分为 P 操作和 V 操作,P 操作是将信号量的值减 1,V 操作是将信号量的值加 1。当信号量的值小于等于 0 之后,再进行 P 操作时,当前进程或线程会被阻塞,直到另一个进程或线程执行了 V 操作将信号量的值增加到大于 0 之时。

8. Socket 套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。Socket 一般用于网络中不同主机上的进程之间的通信。

19、线程的通信方式

1. 信号 Linux 中使用 pthread_kill() 函数对线程发信号

2. 互斥锁、读写锁、自旋锁

互斥锁确保同一时间只能有一个线程访问共享资源,当锁被占用时试图对其加锁的线程都进入阻塞状态(释放 CPU 资源使其由运行状态进入等待状态),当锁释放时哪个等待线程能获得该锁取决于内核的调度。

读写锁当以写模式加锁而处于写状态时任何试图加锁的线程(不论是读或写)都阻塞,当以读状态模式加锁而处于读状态时“读”线程不阻塞,“写”线程阻塞。读模式共享,写模式互斥。

自旋锁上锁受阻时线程不阻塞而是在循环中轮询查看能否获得该锁,没有线程的切换因而没有切换开销,不过对 CPU 的霸占会导致 CPU 资源的浪费。 所以自旋锁适用于并行结构(多个处理器)或者适用于锁被持有时间短而不希望在线程切换产生开销的情况。

3. 条件变量 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的,条件变量始终与互斥锁一起使用。 4. 信号量 信号量实际上是一个非负的整数计数器,用来实现对公共资源的控制。在公共资源增加的时候,信号量就增加;公共资源减少的时候,信号量就减少;只有当信号量的值大于0的时候,才能访问信号量所代表的公共资源。

  • 40
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一些常见的 Linux 网络编程面试: 1. 什么是 socket?它在网络编程中有什么作用? Socket 是一种抽象的概念,用于表示一个端点,即发送或接收数据的进程与网络之间的连接点。在网络编程中,socket 可以用于实现不同机器之间的通信,同时也可以在同一台机器上的不同进程之间进行通信。 2. 请解释 TCP 和 UDP 协议的区别? TCP 和 UDP 都是在传输层上工作的协议。TCP 是一种面向连接的协议,它提供了可靠的数据传输和错误纠正机制,适用于需要确保数据可靠传输的场景。UDP 是一种无连接的协议,它不提供可靠性保证,但是具有低延迟和高效的特点,适用于需要快速传输数据的场景。 3. 什么是 select 函数?它在网络编程中有什么作用? select 是一种 I/O 多路复用机制,它可以同时监视多个文件描述符的读写情况,并在有数据可读或可写时通知相应的进程。在网络编程中,select 函数可以用于监听多个套接字,从而实现并发处理多个客户端请求的目的。 4. 什么是 epoll?它与 select 的区别是什么? epoll 是一种 Linux 特有的 I/O 多路复用机制,它可以监视大量的文件描述符,支持边缘触发和水平触发两种模式,并且可以避免 select 函数在大量文件描述符时的效率问。与 select 相比,epoll 更加高效、稳定,适用于高并发场景。 希望这些回答能对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值