国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux】IO多路轉(zhuǎn)接——poll接口

這篇具有很好參考價值的文章主要介紹了【Linux】IO多路轉(zhuǎn)接——poll接口。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

poll初識

poll函數(shù)

poll服務(wù)器

poll的優(yōu)點

poll的缺點


poll初識

poll也是系統(tǒng)提供的一個多路轉(zhuǎn)接接口。

  • poll系統(tǒng)調(diào)用也可以讓我們的程序同時監(jiān)視多個文件描述符上的事件是否就緒,和select的定位是一樣的,適用場景也是一樣的。

poll函數(shù)

poll函數(shù)

poll函數(shù)的函數(shù)原型如下:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

參數(shù)說明:

  • fds:一個poll函數(shù)監(jiān)視的結(jié)構(gòu)列表,每一個元素包含三部分內(nèi)容:文件描述符、監(jiān)視的事件集合、就緒的事件集合。
  • nfds:表示fds數(shù)組的長度。
  • timeout:表示poll函數(shù)的超時時間,單位是毫秒(ms)。

參數(shù)timeout的取值:

  • -1:poll調(diào)用后進行阻塞等待,直到被監(jiān)視的某個文件描述符上的某個事件就緒。
  • 0:poll調(diào)用后進行非阻塞等待,無論被監(jiān)視的文件描述符上的事件是否就緒,poll檢測后都會立即返回。
  • 特定的時間值:poll調(diào)用后在指定的時間內(nèi)進行阻塞等待,如果被監(jiān)視的文件描述符上一直沒有事件就緒,則在該時間后poll進行超時返回。

返回值說明:

  • 如果函數(shù)調(diào)用成功,則返回有事件就緒的文件描述符個數(shù)。
  • 如果timeout時間耗盡,則返回0。
  • 如果函數(shù)調(diào)用失敗,則返回-1,同時錯誤碼會被設(shè)置。

poll調(diào)用失敗時,錯誤碼可能被設(shè)置為:

  • EFAULT:fds數(shù)組不包含在調(diào)用程序的地址空間中。
  • EINTR:此調(diào)用被信號所中斷。
  • EINVAL:nfds值超過RLIMIT_NOFILE值。
  • ENOMEM:核心內(nèi)存不足。

struct pollfd結(jié)構(gòu)

struct pollfd結(jié)構(gòu)當(dāng)中包含三個成員:

  • fd:特定的文件描述符,若設(shè)置為負(fù)值則忽略events字段并且revents字段返回0。
  • events:需要監(jiān)視該文件描述符上的哪些事件。
  • revents:poll函數(shù)返回時告知用戶該文件描述符上的哪些事件已經(jīng)就緒。

【Linux】IO多路轉(zhuǎn)接——poll接口,Linux,linux,網(wǎng)絡(luò),服務(wù)器,運維

events和revents的取值:

【Linux】IO多路轉(zhuǎn)接——poll接口,Linux,linux,網(wǎng)絡(luò),服務(wù)器,運維

這些取值實際都是以宏的方式進行定義的,它們的二進制序列當(dāng)中有且只有一個比特位是1,且為1的比特位是各不相同的。

【Linux】IO多路轉(zhuǎn)接——poll接口,Linux,linux,網(wǎng)絡(luò),服務(wù)器,運維

  • 因此在調(diào)用poll函數(shù)之前,可以通過或運算符將要監(jiān)視的事件添加到events成員當(dāng)中。
  • 在poll函數(shù)返回后,可以通過與運算符檢測revents成員中是否包含特定事件,以得知對應(yīng)文件描述符的特定事件是否就緒。

poll服務(wù)器

poll的工作流程和select是基本類似的,這里我們也實現(xiàn)一個簡單poll服務(wù)器,該服務(wù)器也只是讀取客戶端發(fā)來的數(shù)據(jù)并進行打印。

PollServer類

PollServer類當(dāng)中也只需要包含監(jiān)聽套接字和端口號兩個成員變量,在poll服務(wù)器綁定時直接將IP地址設(shè)置為INADDR_ANY盡即可。

  • 在構(gòu)造PollServer對象時,需要指明poll服務(wù)器的端口號,當(dāng)然也可以在初始化poll服務(wù)器的時候指明。
  • 在初始化poll服務(wù)器的時候調(diào)用Socket類當(dāng)中的函數(shù),依次進行套接字的創(chuàng)建、綁定和監(jiān)聽即可,這里的Socket類和之前實現(xiàn)的一模一樣。
  • 在析構(gòu)函數(shù)中可以選擇調(diào)用close函數(shù)將監(jiān)聽套接字進行關(guān)閉,但實際也可以不進行該動作,因為服務(wù)器運行后一般是不退出的。

代碼如下:

#pragma once

#include "socket.hpp"
#include <poll.h>

#define BACK_LOG 5

class PollServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	PollServer(int port)
		: _port(port)
	{}
	void InitPollServer()
	{
		_listen_sock = Socket::SocketCreate();
		Socket::SocketBind(_listen_sock, _port);
		Socket::SocketListen(_listen_sock, BACK_LOG);
	}
	~PollServer()
	{
		if (_listen_sock >= 0){
			close(_listen_sock);
		}
	}
};

?運行服務(wù)器

服務(wù)器初始化完畢后就可以開始運行了,而poll服務(wù)器要做的就是不斷調(diào)用poll函數(shù),當(dāng)事件就緒時對應(yīng)執(zhí)行某種動作即可。

  • 首先,在poll服務(wù)器開始死循環(huán)調(diào)用poll函數(shù)之前,需要定義一個fds數(shù)組,該數(shù)組當(dāng)中的每個位置都是一個struct pollfd結(jié)構(gòu),后續(xù)調(diào)用poll函數(shù)時會作為參數(shù)進行傳入。先將fds數(shù)組當(dāng)中每個位置初始化為無效,并將監(jiān)聽套接字添加到fds數(shù)組當(dāng)中,表示服務(wù)器剛開始運行時只需要監(jiān)視監(jiān)聽套接字的讀事件。
  • 此后,poll服務(wù)器就不斷調(diào)用poll函數(shù)監(jiān)視讀事件是否就緒。如果poll函數(shù)的返回值大于0,則說明poll函數(shù)調(diào)用成功,此時已經(jīng)有文件描述符的讀事件就緒,接下來就應(yīng)該對就緒事件進行處理。如果poll函數(shù)的返回值等于0,則說明timeout時間耗盡,此時直接準(zhǔn)備進行下一次poll調(diào)用即可。如果poll函數(shù)的返回值為-1,則說明poll調(diào)用失敗,此時也讓服務(wù)器準(zhǔn)備進行下一次poll調(diào)用,但實際應(yīng)該進一步判斷錯誤碼,根據(jù)錯誤碼來判斷是否應(yīng)該繼續(xù)調(diào)用poll函數(shù)。
    ?

代碼如下:

#pragma once

#include "socket.hpp"
#include <poll.h>

#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1

class PollServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	void Run()
	{
		struct pollfd fds[NUM];
		ClearPollfds(fds, NUM, DFL_FD); //清空數(shù)組中的所有位置
		SetPollfds(fds, NUM, _listen_sock); //將監(jiān)聽套接字添加到數(shù)組中,并關(guān)心其讀事件
		for (;;){
			switch (poll(fds, NUM, -1)){
			case 0:
				std::cout << "timeout..." << std::endl;
				break;
			case -1:
				std::cerr << "poll error" << std::endl;
				break;
			default:
				//正常的事件處理
				//std::cout<<"有事件發(fā)生..."<<std::endl;
				HandlerEvent(fds, NUM);
				break;
			}
		}
	}
private:
	void ClearPollfds(struct pollfd fds[], int num, int default_fd)
	{
		for (int i = 0; i < num; i++){
			fds[i].fd = default_fd;
			fds[i].events = 0;
			fds[i].revents = 0;
		}
	}
	bool SetPollfds(struct pollfd fds[], int num, int fd)
	{
		for (int i = 0; i < num; i++){
			if (fds[i].fd == DFL_FD){ //該位置沒有被使用
				fds[i].fd = fd;
				fds[i].events |= POLLIN; //添加讀事件到events當(dāng)中
				return true;
			}
		}
		return false; //fds數(shù)組已滿
	}
};

事件處理

當(dāng)poll檢測到有文件描述符的讀事件就緒,就會在其對應(yīng)的struct pollfd結(jié)構(gòu)中的revents成員中添加讀事件并返回,接下來poll服務(wù)器就應(yīng)該對就緒事件進行處理了,事件處理過程如下:

  • 首先遍歷fds數(shù)組中的每個struct pollfd結(jié)構(gòu),如果該結(jié)構(gòu)當(dāng)中的fd有效,且revents當(dāng)中包含讀事件,則說明該文件描述符的讀事件就緒,接下來就需要進一步判斷該文件描述符是監(jiān)聽套接字還是與客戶端建立的套接字。
  • 如果是監(jiān)聽套接字的讀事件就緒,則調(diào)用accept函數(shù)將底層建立好的連接獲取上來,并將獲取到的套接字添加到fds數(shù)組當(dāng)中,表示下一次調(diào)用poll函數(shù)時需要監(jiān)視該套接字的讀事件。
  • 如果是與客戶端建立的連接對應(yīng)的讀事件就緒,則調(diào)用read函數(shù)讀取客戶端發(fā)來的數(shù)據(jù),并將讀取到的數(shù)據(jù)在服務(wù)器端進行打印。
  • 如果在調(diào)用read函數(shù)時發(fā)現(xiàn)客戶端將連接關(guān)閉或read函數(shù)調(diào)用失敗,則poll服務(wù)器也直接關(guān)閉對應(yīng)的連接,并將該連接對應(yīng)的文件描述符從fds數(shù)組當(dāng)中清除,表示下一次調(diào)用poll函數(shù)時無需再監(jiān)視該套接字的讀事件。

代碼如下:

#pragma once

#include "socket.hpp"
#include <poll.h>

#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1

class PollServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	void HandlerEvent(struct pollfd fds[], int num)
	{
		for (int i = 0; i < num; i++){
			if (fds[i].fd == DFL_FD){ //跳過無效的位置
				continue;
			}
			if (fds[i].fd == _listen_sock&&fds[i].revents&POLLIN){ //連接事件就緒
				struct sockaddr_in peer;
				memset(&peer, 0, sizeof(peer));
				socklen_t len = sizeof(peer);
				int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
				if (sock < 0){ //獲取連接失敗
					std::cerr << "accept error" << std::endl;
					continue;
				}
				std::string peer_ip = inet_ntoa(peer.sin_addr);
				int peer_port = ntohs(peer.sin_port);
				std::cout << "get a new link[" << peer_ip << ":" << peer_port << "]" << std::endl;
				
				if (!SetPollfds(fds, NUM, sock)){ //將獲取到的套接字添加到fds數(shù)組中,并關(guān)心其讀事件
					close(sock);
					std::cout << "poll server is full, close fd: " << sock << std::endl;
				}
			}
			else if (fds[i].revents&POLLIN){ //讀事件就緒
				char buffer[1024];
				ssize_t size = read(fds[i].fd, buffer, sizeof(buffer)-1);
				if (size > 0){ //讀取成功
					buffer[size] = '\0';
					std::cout << "echo# " << buffer << std::endl;
				}
				else if (size == 0){ //對端連接關(guān)閉
					std::cout << "client quit" << std::endl;
					close(fds[i].fd);
					UnSetPollfds(fds, i); //將該文件描述符從fds數(shù)組中清除
				}
				else{
					std::cerr << "read error" << std::endl;
					close(fds[i].fd);
					UnSetPollfds(fds, i); //將該文件描述符從fds數(shù)組中清除
				}
			}
		}
	}
private:
	bool SetPollfds(struct pollfd fds[], int num, int fd)
	{
		for (int i = 0; i < num; i++){
			if (fds[i].fd == DFL_FD){ //該位置沒有被使用
				fds[i].fd = fd;
				fds[i].events |= POLLIN; //添加讀事件到events當(dāng)中
				return true;
			}
		}
		return false; //fds數(shù)組已滿
	}
	void UnSetPollfds(struct pollfd fds[], int pos)
	{
		fds[pos].fd = DFL_FD;
		fds[pos].events = 0;
		fds[pos].revents = 0;
	}
};

說明一下:文章來源地址http://www.zghlxwxcb.cn/news/detail-652158.html

  • 因為這里將fds數(shù)組的大小是固定設(shè)置的,因此在將新獲取連接對應(yīng)的文件描述符添加到fds數(shù)組時,可能會因為fds數(shù)組已滿而添加失敗,這時poll服務(wù)器只能將剛剛獲取上來的連接對應(yīng)的套接字進行關(guān)閉。

poll服務(wù)器測試

運行poll服務(wù)器時也需要先實例化出一個PollServer對象,對poll服務(wù)器進行初始化后就可以運行服務(wù)器了。

代碼如下:

#include "poll_server.hpp"
#include <string>

static void Usage(std::string proc)
{
	std::cerr << "Usage: " << proc << " port" << std::endl;
}

int main(int argc, char* argv[])
{
	if (argc != 2){
		Usage(argv[0]);
		exit(1);
	}
	int port = atoi(argv[1]);
	PollServer* svr = new PollServer(port);
	svr->InitPollServer();
	svr->Run();
	
	return 0;
}

因為我們編寫的poll服務(wù)器在調(diào)用poll函數(shù)時,將timeout的值設(shè)置成了-1,因此運行服務(wù)器后如果沒有客戶端發(fā)來連接請求,那么服務(wù)器就會在調(diào)用poll函數(shù)后進行阻塞等待。

【Linux】IO多路轉(zhuǎn)接——poll接口,Linux,linux,網(wǎng)絡(luò),服務(wù)器,運維

當(dāng)我們用telnet工具連接poll服務(wù)器后,poll服務(wù)器調(diào)用的poll函數(shù)在檢測到監(jiān)聽套接字的讀事件就緒后就會調(diào)用accept獲取建立好的連接,并打印輸出客戶端的IP和端口號,此時客戶端發(fā)來的數(shù)據(jù)也能夠成功被poll服務(wù)器收到并進行打印輸出。

【Linux】IO多路轉(zhuǎn)接——poll接口,Linux,linux,網(wǎng)絡(luò),服務(wù)器,運維

此外,poll服務(wù)器也是一個單進程服務(wù)器,但是它也可以同時為多個客戶端提供服務(wù)。

【Linux】IO多路轉(zhuǎn)接——poll接口,Linux,linux,網(wǎng)絡(luò),服務(wù)器,運維

當(dāng)服務(wù)器端檢測到客戶端退出后,也會關(guān)閉對應(yīng)的連接,并將對應(yīng)的套接字從fds數(shù)組當(dāng)中清除。

【Linux】IO多路轉(zhuǎn)接——poll接口,Linux,linux,網(wǎng)絡(luò),服務(wù)器,運維

poll的優(yōu)點

  • struct pollfd結(jié)構(gòu)當(dāng)中包含了events和revents,相當(dāng)于將select的輸入輸出型參數(shù)進行分離,因此在每次調(diào)用poll之前,不需要像select一樣重新對參數(shù)進行設(shè)置。
  • poll可監(jiān)控的文件描述符數(shù)量沒有限制。
  • 當(dāng)然,poll也可以同時等待多個文件描述符,能夠提高IO的效率。

說明一下:

  • 雖然代碼中將fds數(shù)組的元素個數(shù)定義為1024,但fds數(shù)組的大小是可以繼續(xù)增大的,poll函數(shù)能夠幫你監(jiān)視多少個文件描述符是由傳入poll函數(shù)的第二個參數(shù)決定的。
  • 而fd_set類型只有1024個比特位,因此select函數(shù)最多只能監(jiān)視1024個文件描述符。

poll的缺點

  • 和select函數(shù)一樣,當(dāng)poll返回后,需要遍歷fds數(shù)組來獲取就緒的文件描述符。
  • 每次調(diào)用poll,都需要把大量的struct pollfd結(jié)構(gòu)從用戶態(tài)拷貝到內(nèi)核態(tài),這個開銷也會隨著poll監(jiān)視的文件描述符數(shù)目的增多而增大。
  • 同時每次調(diào)用poll都需要在內(nèi)核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大。

到了這里,關(guān)于【Linux】IO多路轉(zhuǎn)接——poll接口的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【網(wǎng)絡(luò)】多路轉(zhuǎn)接——poll | epoll

    【網(wǎng)絡(luò)】多路轉(zhuǎn)接——poll | epoll

    ??作者:一只大喵咪1201 ??專欄:《網(wǎng)絡(luò)》 ??格言: 你只管努力,剩下的交給時間! 書接上文五種IO模型 | select。 poll 也是一種多路轉(zhuǎn)接的方案,它專門用來解決 select 的兩個問題: 等待fd有上限的問題。 每次調(diào)用都需要重新設(shè)置 fd_set 的問題。 如上圖所示便是 poll 系統(tǒng)調(diào)

    2024年02月10日
    瀏覽(24)
  • 【Linux】IO多路轉(zhuǎn)接技術(shù)Epoll的使用

    【Linux】IO多路轉(zhuǎn)接技術(shù)Epoll的使用

    ? 在學(xué)習(xí) epoll 之前,我們首先了解一下Linux中的多路復(fù)用技術(shù): 在Linux系統(tǒng)中, IO多路復(fù)用 是一種重要的技術(shù),它允許一個進程同時監(jiān)視多個文件描述符,一旦某個描述符準(zhǔn)備好進行讀?。ㄍǔJ亲x就緒或?qū)懢途w),內(nèi)核會通知該進程進行相應(yīng)的讀寫操作。這樣,我們可以

    2024年04月27日
    瀏覽(15)
  • 02-Linux-IO多路復(fù)用之select、poll和epoll詳解

    02-Linux-IO多路復(fù)用之select、poll和epoll詳解

    前言: 在linux系統(tǒng)中,實際上所有的 I/O 設(shè)備都被抽象為了文件這個概念,一切皆文件,磁盤、網(wǎng)絡(luò)數(shù)據(jù)、終端,甚至進程間通信工具管道 pipe 等都被當(dāng)做文件對待。 在了解多路復(fù)用 select、poll、epoll 實現(xiàn)之前,我們先簡單回憶復(fù)習(xí)以下兩個概念: 一、什么是多路復(fù)用: 多路

    2024年02月10日
    瀏覽(25)
  • 【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實現(xiàn)(IO多路復(fù)用select)

    【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實現(xiàn)(IO多路復(fù)用select)

    服務(wù)器模型主要分為兩種, 循環(huán)服務(wù)器 和 并發(fā)服務(wù)器 。 循環(huán)服務(wù)器 : 在同一時間只能處理一個客戶端的請求。 并發(fā)服務(wù)器 : 在同一時間內(nèi)能同時處理多個客戶端的請求。 TCP的服務(wù)器默認(rèn)的就是一個循環(huán)服務(wù)器,原因是有兩個阻塞 accept函數(shù) 和recv函數(shù) 之間會相互影響。

    2024年02月03日
    瀏覽(100)
  • 【網(wǎng)絡(luò)】多路轉(zhuǎn)接——五種IO模型 | select

    【網(wǎng)絡(luò)】多路轉(zhuǎn)接——五種IO模型 | select

    ??作者:一只大喵咪1201 ??專欄:《網(wǎng)絡(luò)》 ??格言: 你只管努力,剩下的交給時間! 在學(xué)習(xí)系統(tǒng)部分的時候,本喵就講解過IO,當(dāng)時我們學(xué)習(xí)的IO就是從文件中讀數(shù)據(jù)和寫數(shù)據(jù),到了后來學(xué)習(xí)網(wǎng)絡(luò)的時候,我們知道,從網(wǎng)絡(luò)中讀取和寫入數(shù)據(jù)也是IO,那么IO到底是什么呢?今

    2024年02月10日
    瀏覽(24)
  • 多路IO—POll函數(shù),epoll服務(wù)器開發(fā)流程

    多路IO—POll函數(shù),epoll服務(wù)器開發(fā)流程

    \\\"在計算機網(wǎng)絡(luò)編程中,多路IO技術(shù)是非常常見的一種技術(shù)。其中,Poll函數(shù)和Epoll函數(shù)是最為常用的兩種多路IO技術(shù)。這兩種技術(shù)可以幫助服務(wù)器端處理多個客戶端的并發(fā)請求,提高了服務(wù)器的性能。本文將介紹Poll和Epoll函數(shù)的使用方法,并探討了在服務(wù)器開發(fā)中使用這兩種技

    2024年02月06日
    瀏覽(18)
  • IO多路復(fù)用中select的TCP服務(wù)器模型和poll服務(wù)模型

    服務(wù)器端 客戶端 poll客戶端

    2024年02月12日
    瀏覽(32)
  • 多路轉(zhuǎn)接方案:select poll epoll 介紹和對比

    多路轉(zhuǎn)接方案:select poll epoll 介紹和對比

    內(nèi)存和外設(shè)的交互叫做IO,網(wǎng)絡(luò)IO就是將數(shù)據(jù)在內(nèi)存和網(wǎng)卡間拷貝。 IO本質(zhì)就是等待和拷貝,一般等待耗時往往遠(yuǎn)高于拷貝耗時。所以提高IO效率就是盡可能減少等待時間的比重。 IO模型 簡單對比解釋 阻塞IO 阻塞等待數(shù)據(jù)到來 非阻塞IO 輪詢等待數(shù)據(jù)到來 信號驅(qū)動 信號遞達(dá)時

    2024年02月08日
    瀏覽(17)
  • linux并發(fā)服務(wù)器 —— IO多路復(fù)用(八)

    linux并發(fā)服務(wù)器 —— IO多路復(fù)用(八)

    半關(guān)閉只能實現(xiàn)數(shù)據(jù)單方向的傳輸;當(dāng)TCP 接中A向 B 發(fā)送 FIN 請求關(guān)閉,另一端 B 回應(yīng)ACK 之后 (A 端進入 FIN_WAIT_2 狀態(tài)),并沒有立即發(fā)送 FIN 給 A,A 方處于半連接狀態(tài) (半開關(guān)),此時 A 可以接收 B 發(fā)送的數(shù)據(jù),但是 A 已經(jīng)不能再向 B 發(fā)送數(shù)據(jù) close不會影響到其他進程,shutdown會

    2024年02月09日
    瀏覽(22)
  • 計算機網(wǎng)絡(luò)編程 | 多路I/O轉(zhuǎn)接服務(wù)器

    計算機網(wǎng)絡(luò)編程 | 多路I/O轉(zhuǎn)接服務(wù)器

    歡迎關(guān)注博主 Mindtechnist 或加入【Linux C/C++/Python社區(qū)】一起學(xué)習(xí)和分享Linux、C、C++、Python、Matlab,機器人運動控制、多機器人協(xié)作,智能優(yōu)化算法,濾波估計、多傳感器信息融合,機器學(xué)習(xí),人工智能等相關(guān)領(lǐng)域的知識和技術(shù)。 專欄:《網(wǎng)絡(luò)編程》 多路IO轉(zhuǎn)接服務(wù)器也叫做多

    2024年02月12日
    瀏覽(30)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包