linux send 失敗_Epoll學習服務器的實現-Linux內核原始Epoll結構

1.Begins~

有的人學習linux編程很久,只知道網絡編程是socket,bind, listen。。。,然而這些都是網絡通信軟件最基本的接口。在某網絡公司待了y,也了解到公司的基礎就是網絡轉發 ,然而網絡轉發實現并非我們平時所見的簡單的send,recv。公司設備的轉發都是建立在穩定并且高效的內部業務的基礎上的,例如一個可靠性的服務:BFD(鏈路雙向快速檢測),進程內業務就是建立在內核與用戶態,后臺處理與前臺配置交互的過程上實現。在做項目的日子里,漸漸的對公司BFD模塊有一定了解,特別是涉及到框架的地方,仔細看發現,一些技巧其實就是我們平日所用的技能外加一些優化。對于單進程用戶態來說,處理前臺進程交互與內核上送消息,如果沒有一個高效的機制,是很難處理上以千計甚至萬計的消息的。在參看公司代碼的初始化階段,發現一共用了至少3 個epoll回調機制,一個是處理前臺發送的配置及查詢,一個是處理與其他應用模塊的交互,另一個是向其他應用通報狀態變化的機制。公司的epoll_event 機制不知道是不是修改了內核,結構大致是這樣:

typedef struct epoll_event { uint32_t events;      // Epoll events  epoll_data_t data;      // User data variable ep_callback  callback;    /* 這跟linux內核不一樣 ? */}EPOLL_PACK; /*在添加epoll事件的時候就多了一個回調函數注冊,這個十分方便了,回調函數可以用一個全局變量搞定,一個框架搭好,隨后添加就大大的簡單多了*/

實際內核提供給我們使用的用戶態結構是這樣:


typedef union epoll_data {  void *ptr;  int fd;  uint32_t u32;  uint64_t u64;  } epoll_data_t;struct epoll_event { uint32_t events;      // Epoll events  epoll_data_t data;      // User data variable  };  

不過我們也可以將epoll_data_t這個聯合體封裝在一個結構,只需要用指針*ptr指向封裝的帶fd及回調函數就可以。打算自己學習一些必要的技能,暫且就記錄一下吧

2.Epoll API


extern int epoll_create(int  size);extern int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);extern int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

① int epoll_create(int size);
創建一個epoll的句柄。自從linux2.6.8之后,size參數是被忽略的。需要注意的是,當創建好epoll句柄后,
它就是會占用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所以在使用完epoll
后,必須調用close()關閉,否則可能導致fd被耗盡。
②int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注冊函數,它不同于select()是在監聽事件時告訴內核要監聽什么類型的事件,而是在這里先
注冊要監聽的事件類型。
參數:
第一個參數是epoll_create()的返回值。
第二個參數表示動作,用三個宏來表示:
EPOLL_CTL_ADD:注冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
第三個參數是需要監聽的fd。
第四個參數是告訴內核需要監聽什么事,struct epoll_event結構如下:

enum EPOLL_EVENTS{

EPOLLIN = 0x001,

#define EPOLLIN EPOLLIN

EPOLLPRI = 0x002,

#define EPOLLPRI EPOLLPRI

EPOLLOUT = 0x004,

#define EPOLLOUT EPOLLOUT

EPOLLRDNORM = 0x040,

#define EPOLLRDNORM EPOLLRDNORM

EPOLLRDBAND = 0x080,

#define EPOLLRDBAND EPOLLRDBAND

EPOLLWRNORM = 0x100,

#define EPOLLWRNORM EPOLLWRNORM

EPOLLWRBAND = 0x200,

#define EPOLLWRBAND EPOLLWRBAND

EPOLLMSG = 0x400,

#define EPOLLMSG EPOLLMSG

EPOLLERR = 0x008,

#define EPOLLERR EPOLLERR

EPOLLHUP = 0x010,

#define EPOLLHUP EPOLLHUP

EPOLLRDHUP = 0x2000,

#define EPOLLRDHUP EPOLLRDHUP

EPOLLWAKEUP = 1u << 29,

#define EPOLLWAKEUP EPOLLWAKEUP

EPOLLONESHOT = 1u << 30,

#define EPOLLONESHOT EPOLLONESHOT

EPOLLET = 1u << 31

#define EPOLLET EPOLLET

};

(3)epoll_wait();

參數events是分配好的epoll_event結構體數組;
epoll將會把發生的事件復制到events數組中(events不可以是空指針,內核只負責把數據復制到)
參數timeout 是超時時間(毫秒,0表示立即返回,-1表示永久阻塞);
如果函數調用成功,返回對應IO上已經準備好的文件描述符數目;如果返回0,表示已超時;如果返回值小于0,表示函數失敗。

Linuxc/c++服務器開發高階視頻學習資料+主頁群獲取

30b3471d5199dd42de917a34f9cfdf78.png

3.epoll mode

epoll的使用也很方便,基本都會用到以下模型,取自linux幫助手冊:

/*Example for suggested usageWhile  the  usage  of  epoll when employed as a level-triggered interface does have the same semantics as poll(2), the edge-triggered usage requires moreclarification to avoid stalls in the application event loop.  In this example, listener is a nonblocking socket on which listen(2) has been called.   Thefunction  do_use_fd()  uses the new ready file descriptor until EAGAIN is returned by either read(2) or write(2).  An event-driven state machine applica‐tion should, after having received EAGAIN, record its current state so that at the next call to do_use_fd() it will continue to read(2) or write(2)  fromwhere it stopped before.*/#define MAX_EVENTS 10struct epoll_event ev, events[MAX_EVENTS];int listen_sock, conn_sock, nfds, epollfd;/* Code to set up listening socket, 'listen_sock',(socket(), bind(), listen()) omitted */static Epoll_Case(){epollfd = epoll_create1(0);if (epollfd == -1) {perror("epoll_create1");exit(EXIT_FAILURE);}ev.events = EPOLLIN;ev.data.fd = listen_sock;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {perror("epoll_ctl: listen_sock");exit(EXIT_FAILURE);}for (;;) {nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);if (nfds == -1) {perror("epoll_wait");exit(EXIT_FAILURE);}for (n = 0; n < nfds; ++n) {if (events[n].data.fd == listen_sock) {conn_sock = accept(listen_sock,(struct sockaddr *) &local, &addrlen);if (conn_sock == -1) {perror("accept");exit(EXIT_FAILURE);}setnonblocking(conn_sock);ev.events = EPOLLIN | EPOLLET;ev.data.fd = conn_sock;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,&ev) == -1) {perror("epoll_ctl: conn_sock");exit(EXIT_FAILURE);}} else {do_use_fd(events[n].data.fd);}}}}/*           When  used  as an edge-triggered interface, for performance reasons, it is possible to add the file descriptor inside the epoll interface (EPOLL_CTL_ADD)once by specifying (EPOLLIN|EPOLLOUT).  This allows you  to  avoid  continuously  switching  between  EPOLLIN  and  EPOLLOUT  calling  epoll_ctl(2)  withEPOLL_CTL_MOD.*/

下面給出一個epoll 服務器的簡單例子,參考了網上不少方式,自己寫了一個,算不上原創,把BIT_TEST部分注釋打開,可以忽略basetype.h,可以編譯通過。創建socket的地方可以自己優化省略,我實現的時候覺得這種方式比較容易看清楚socket的獲取方式。


#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <string.h>#include <netinet/in.h>#include <sys/epoll.h>#include <poll.h>#include <assert.h>#include <unistd.h>#include <fcntl.h>#include "basetype.h"static int g_Main_Epoll_Fd = -1;#define MAX_EPOLL_EVENT_COUNT 64#define MAX_LISTEN_NUM  10#define MAX_BUFF_LEN 512/*#define BIT_TEST(a, b)          ((a) & (b))#define BIT_RESET(a, b)         ((a) ~= (b))#define BIT_SET(a, b)           ((a) |= (b))#define BIT_MATCH(a,b)          ((a)&(b) = (b))#define BIT_COMPARE(a, b, c)    ((a)&(b) == (c))*/static void print_socket_info(struct addrinfo *ai){char ipstr[INET6_ADDRSTRLEN];uint16_t port;void *addr  = NULL;char *ipver = NULL;struct sockaddr_in *ipv4 = NULL;struct sockaddr_in6 *ipv6 = NULL;assert(NULL != ai);if (ai->ai_family == AF_INET) { // IPv4ipv4 = (struct sockaddr_in *)ai->ai_addr;addr = &(ipv4->sin_addr);ipver = "IPv4";port =ntohs(((struct sockaddr_in*)ai->ai_addr)->sin_port);} else { // IPv6ipv6 = (struct sockaddr_in6 *)ai->ai_addr;addr = &(ipv6->sin6_addr);ipver = "IPv6";port =ntohs(((struct sockaddr_in6*)ai->ai_addr)->sin6_port);}// convert the IP to a string and print it:(void)inet_ntop(ai->ai_family, addr, ipstr, sizeof ipstr);printf(" server initing... AF: %s IP: %s,PORT: %un", ipver, ipstr,port);return ;}static int SetSocketNoblocking(const int sockfd){int flags = 0;int iret  = 0;flags = fcntl (sockfd, F_GETFL, 0);if (flags == -1){perror ("fcntl");return -1;}flags |= O_NONBLOCK;iret = fcntl (sockfd, F_SETFL, flags);if (iret == -1){perror ("fcntl");return -1;}return 0;}static int Epoll_Control(const int sockfd, const int oper, const int events){struct epoll_event ep_event;int iret = 0;memset(&ep_event, 0, sizeof(ep_event));ep_event.events = events;ep_event.data.fd = sockfd;iret = epoll_ctl(g_Main_Epoll_Fd, oper, sockfd, &ep_event);if (0 > iret){perror("epoll_ctl error.");printf("epoll_ctl error opt:%dn", oper);return -1;}return 0;}static int CreateServerScoket(const char *host, const char *port){struct addrinfo hints;struct addrinfo*ailist= NULL; struct addrinfo *ai= NULL;int iret;int sockfd = 0;int sockoptval = 1;memset(&hints, 0, sizeof(hints));hints.ai_family = AF_UNSPEC; // AF_INET 或 AF_INET6 hints.ai_socktype = SOCK_STREAM;hints.ai_flags = AI_PASSIVE;     /* All interfaces */iret = getaddrinfo(host, port, &hints, &ailist);if (0 != iret){fprintf(stderr, "getaddrinfo: %sn", gai_strerror(iret));return -1;}for(ai = ailist;ai != NULL; ai = ai->ai_next) {sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);if (0 > sockfd){continue;}/* set reuse addr */iret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(sockoptval));if (0 > iret){perror("setsockopt errorn");freeaddrinfo(ailist);return -1;}iret = bind(sockfd, ai->ai_addr, ai->ai_addrlen);/*sizeof(struct sockaddr)*/if (0 == iret){print_socket_info(ai);break;}else{close(sockfd);}}if (NULL == ai){fprintf(stderr, "get server socket error.n");return -1;}freeaddrinfo(ailist); /* set no-block mode */iret = SetSocketNoblocking(sockfd);if (0 != iret){return -1;}/* listen socket */iret = listen(sockfd, MAX_LISTEN_NUM);if (0  > iret){perror("listen socket error.n");return -1;}printf("server socket init success. listenning...n");return sockfd;}static void HandleAccept(const int listen_scok, int Event){struct sockaddr_in addr;socklen_t  len = sizeof(addr);int client_sock  = -1;if (BIT_TEST(EPOLLIN, Event)){client_sock = accept(listen_scok, (struct sockaddr *)&addr, &len);if(client_sock < 0 ){perror("accept infd error");return;}(void)Epoll_Control(client_sock, EPOLL_CTL_ADD, EPOLLIN);}return ;}static void ProcRecvMsg(int sockfd){ssize_t recvlen = 0;char recvBuf[MAX_BUFF_LEN];//read ev  ready;recvlen = recv(sockfd, recvBuf, sizeof(recvBuf), 0);if(recvlen < 0){perror("recv error.");return ;}else if(recvlen == 0){printf("client quitn");	(void)Epoll_Control(sockfd, EPOLL_CTL_DEL, 0);close(sockfd);}else{(void)Epoll_Control(sockfd, EPOLL_CTL_MOD, EPOLLOUT);printf("recv msg fprom client:msg#:%sn",recvBuf);}return ;}static void SendMsg2Client(const int sockfd){ssize_t sendlen = 0;char sendBuf[MAX_BUFF_LEN];int event = 0;sprintf(sendBuf, "HTTP/1.0 200 OKrnrn<html><h1>Hello Epoll! [client_fd:%d]</h1></html>", sockfd);  int sendsize = send(sockfd, sendBuf, strlen(sendBuf)+1, 0);  if(sendsize <= 0)  {  perror("send error.");(void)Epoll_Control(sockfd, EPOLL_CTL_DEL, 0);close(sockfd);}  else {  printf("Server reply msg ok! data: %sn", sendBuf);  event = EPOLLIN | EPOLLERR | EPOLLHUP;(void)Epoll_Control(sockfd, EPOLL_CTL_MOD, event);}  return ;}int main(int argc, char *argv[]){int listenfd =0;int epfd = 0;int iret = 0;int event = 0;struct epoll_event ep_events[MAX_EPOLL_EVENT_COUNT];int ev_num = 0;int in_sockfd = 0;int loop = 0;if (3 > argc){printf("Usage: %s [ip_addr] [port]n",argv[0]);return -1;}listenfd = CreateServerScoket(argv[1], argv[2]);if (0 > listenfd){return -1;}epfd = epoll_create(1);if (-1 == epfd){perror("create epoll error.n");return -1;}else{g_Main_Epoll_Fd = epfd;}event = EPOLLIN;iret = Epoll_Control(listenfd, EPOLL_CTL_ADD, event);if (0 != iret){return -1;}/* proc epoll event regined */printf("server init success, epoll wait now.n");for(;;){memset(ep_events, 0, sizeof(ep_events));ev_num = epoll_wait(g_Main_Epoll_Fd, ep_events, MAX_EPOLL_EVENT_COUNT, -1);switch (ev_num){case 0:{printf("epoll_wait timeout.n");break;}case -1:{perror("epoll_wait error.n");break;}default:{for (loop = 0; loop < ev_num; loop++){in_sockfd = ep_events[loop].data.fd;event = ep_events[loop].events;if (in_sockfd == listenfd){HandleAccept(in_sockfd, event);}else{if (BIT_TEST(EPOLLIN, event)){/* have data to read */ProcRecvMsg(in_sockfd);}else if (BIT_TEST(EPOLLOUT, event)){SendMsg2Client(in_sockfd);}}}break;}}}return 0;}

結果如下,客戶端任意都可以:注意,運行時需要手動輸入服務器主機名及端口號。

5f13c51a4ea7659327e3d034d59b1e92.png

原文鏈接:Epoll學習服務器的簡單實現-Linux內核原始Epoll結構_li-CSDN博客

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/529447.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/529447.shtml
英文地址,請注明出處:http://en.pswp.cn/news/529447.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

衛生系統計算機考試內容,2021年衛生資格考試題型是什么樣的?一篇搞懂!

根據中國衛生人才網發布的考試通知可知&#xff0c;2021年衛生資格考試除護理學初級(師)專業采用紙筆作答方式進行考試外&#xff0c;其余116個專業均采用人機對話方式進行。那么&#xff0c;什么是人機考試呢&#xff1f;能提前練習嗎&#xff1f;為了解決大家的疑惑&#xff…

市面上有哪幾種門_選擇 low-e門窗玻璃,你想知道的都在這了

最近系統門窗群里&#xff0c;不少讀者粉絲在咨詢關于Low-e玻璃的問題&#xff0c;大家越來越重視好的門窗對于隔熱、隔音、隔輻射的作用。我匯總了群里大家咨詢最多的20個問題&#xff0c;希望對大家在挑選門窗玻璃時起到一些幫助。在介紹low-e玻璃前&#xff0c;大家先跟著我…

計算機進制簡稱,NO.A.0007——二進制;計算機容量單位B、KB、MB、GB和TB關系

一、字節(Byte,簡稱B)與K、KB、M、MB的關系1、計算機中各種存儲容量的單位都是用字節(Byte簡為B)來表示&#xff0c;此外還有KB、MB、GB和TB&#xff0c;他們的關系是&#xff1a;1KB1024Bytes2的10次方Bytes1MB1024KB2的20次方Bytes1GB1024MB2的30次方Bytes1TB1024GB2的40次方…

arm中斷保護和恢復_嵌入式ARM系統異常和中斷處理知識總結

關于異常處理&#xff0c;分為三部分&#xff1a; 1. ARM異常和模式&#xff1a;core處理異常時的操作&#xff0c;幾種模式介紹。 2. Vector table&#xff1a; 3. 異常優先級 4. lr偏移&#xff1a;幾種異常如何返回異常和中斷處理簡介在嵌入式系統中異常處理是核心之一。高效…

計算機工作站 曙光,曙光桌面工作站解析

曙光桌面工作站內部架構從整個結構設計來看&#xff0c;整機采用10升小機箱設計&#xff0c;尺寸為326x90x333.4mm&#xff0c;重量在5KG左右&#xff1b;支持垂直放置或橫置&#xff0c;節省辦公空間。接口方面&#xff0c;整機在前面板提供了4個USB 3.0接口&#xff0c;后置接…

查看網卡命令 uefi_大神教你開機進入UEFI模式的方法,非常簡單

啟動電腦時&#xff0c;如果要更改啟動優先級、啟用或禁用安全啟動或更改任何其他低級設置&#xff0c;則需要啟動到UEFI模式。根據主板制造商的不同&#xff0c;必須按鍵盤上的特定鍵或組合鍵才能進入UEFI模式。盡管初始屏幕顯示了需要按下的鍵&#xff0c;但對于初學者來說卻…

計算機網絡拓撲結構說課稿,計算機網絡應用基礎說課稿修稿稿

計算機網絡應用基礎說課稿修稿稿 (6頁)本資源提供全文預覽&#xff0c;點擊全文預覽即可全文預覽,如果喜歡文檔就下載吧&#xff0c;查找使用更方便哦&#xff01;19.90 積分計算機網絡的分類說課稿第1頁共6頁計算機網絡的分類說課稿蓬溪縣中等職業技術學校唐湘各位評委老師大家…

擴展源_瑞薩電子推出具備反向充電WattShare TRx模式的 15W無線充電電源P9415R接收器,擴展無線電源產品線...

News全球領先的半導體解決方案供應商瑞薩電子集團(TSE&#xff1a;6723)日前宣布推出采用瑞薩獨有WattShareTM技術的P9415-R無線電源接收器&#xff0c;作為其無線電源解決方案的最新成員。新型15W無線電源接收器使智能手機、移動電源以及便攜式工業和醫療設備能夠對其它具有無…

香港大學計算機碩士一定要面試嗎,大干貨!!!香港碩士有哪些專業需要筆試面試...

和其它英聯邦地區比&#xff0c;香港碩士申請復雜程度更高&#xff0c;除了香港地區不同專業在申請時間、流程及錄取要求方面的復雜信息外&#xff0c;很多專業在錄取時都有面試筆試也是很重要的原因。很多同學在收到面試筆試后也都會擔心自己表現不好被拒。今天就來跟大家說說…

計算機上沒有office2010,《我安裝了office2010,為什么桌面-右鍵-新建中沒有excel呢?》 excel文件找不到...

為什么在excel2007中新建文檔&#xff0c;我的模板里找不到剛剛保存的模板&#xff1f;win10系統/華碩S CA400&#xff1a;C:\Users\Admin\AppData\Roaming\Microsoft\Templates我安裝了office2010&#xff0c;為什么桌面-右鍵-新建中沒有excel呢&#xff1f;這個office2010無關…

postgresql存圖片字段類型_PostgreSQL 入門

安裝、設置、創建和開始使用 PostgreSQL 數據庫。-- Greg Pittman&#xff08;作者&#xff09;每個人或許都有需要在數據庫中保存的東西。即使你執著于使用紙質文件或電子文件&#xff0c;它們也會變得很麻煩。紙質文檔可能會丟失或混亂&#xff0c;你需要訪問的電子信息可能會…

ibm服務器 p5 硬盤,IBM虛擬化實戰之p5服務器邏輯分區解讀

創建 AIX 分區現在您將創建一個 AIX 分區&#xff0c;它使用 VIO Server 的 SEA 來訪問網絡。它還將其 rootvg 寄宿在 VIO LPAR 上。創建 AIX 服務器分區和概要除了下面的變化以外&#xff0c;創建 AIX 分區和概要的步驟與創建VIO Server 分區和概要部分中的步驟完全相同。1. 在…

cad動態塊制作翻轉_裝X必學,手把手教你做CAD動態屬性塊

最近很多粉絲后臺問熊大&#xff0c;如何制作CAD動態屬性塊&#xff0c;這個問題把我難到了。因為這個不是一句兩句話能夠說得清楚的&#xff0c;于是我拿出珍藏的“西蜀鄙夫”寫的教程&#xff0c;并通過與我們設計過程的實際需要給大家說說。干貨文章&#xff0c;大家耐心看完…

云服務器怎么執行sql文件在哪里,總結帝國CMS下在PHP文件中怎么調用數據庫類執行SQL語句實例...

總結帝國CMS下在PHP文件中怎么調用數據庫類執行SQL語句實例發布時間&#xff1a;2020-10-19 14:58:08來源&#xff1a;億速云閱讀&#xff1a;83作者&#xff1a;小新這篇文章將為大家詳細講解有關總結帝國CMS下在PHP文件中怎么調用數據庫類執行SQL語句實例&#xff0c;小編覺得…

網和aoe網的區別_運動內衣與普通內衣有什么區別?運動內衣里面還需要穿文胸嗎? 小家生活網20201002 11:03:04...

閱讀本文前&#xff0c;請您先點擊上面的“藍色字體可以說愛情就是他們最大的軟肋。不會在雙魚女那出現。在對于周邊的親戚時。她們那種溫柔大方的氣勢。更符合媽媽的需求。整雙腿沒有視覺連貫性。但唯獨感情這件事一直是水瓶們的知識盲區。他們雙商超高。可能最開始還是會去挽…

機器人無限火力無限e符文_LOL:無限火力開黑指南 三大玩法讓你快樂加倍

無限火力上線以來受到大家的熱烈追捧&#xff0c;今天給大家介紹幾個主流的系列&#xff0c;讓你在峽谷能夠感受到雙倍的快樂&#xff01;超強控制流這一類英雄都是帶有強力控制的&#xff0c;無限火力的80%減CD&#xff0c;能夠讓女坦等英雄可以打出長達幾秒的完美控制技能。首…

ps3無線無法與服務器通信,psp上網出現無法與服務器通信(8001006F)

滿意答案引用:一&#xff1a;自己網絡配置&#xff1a;大亞db102 adsl撥號上網。INTEL網卡。神卡&#xff1a;Zydas 54M 同時支持PSP 和NDS的無線USB網卡三&#xff1a;安裝過程中出現的問題DNS錯誤(8041040F)四、總結出現問題的關鍵&#xff1a;并非DNS設置錯誤&#xff0c;主…

bootstrap-table toolbar圖標換文字_iPhone 也能隨意換字體啦~

呔咯昨天從老大手里喜提一臺 iPhone XS Max &#xff0c;終于讓表妹從安卓陣營轉入蘋果&#xff0c;嘻嘻~當她拿著 iPhone 的時候&#xff0c;一臉懵的問我&#xff1a;iPhone 自帶字體也太丑了吧&#xff0c;還我的輸入法&#xff01;&#xff01;&#xff01;呔咯&#xff1a…

網絡服務器分為文件服務器通信服務器和,近代中國落后、貧困的根本原因是()...

近代far, find, lights, minutes, to, right, tellA: Excuse me. Could you ____ me the way ____ the bookshop?B: Ok. Go along this road and turn____ at the traffic ____. Then go straight on. Youll ____ it next to the post office.A: Is it ____ from here?B: No,…

搭建bitwarden_Docker輕松部署Bitwarden私有密碼管理系統服務

0x01. 什么是Bitwarden?開源免費的密碼管理器&#xff0c;Bitwarden的原理是使用高強度的AES256 算法對你的個人數據進行本地加密&#xff0c;然后再傳輸到云端服務器來實現網絡同步。bitwarden使用的是微軟的Azure云服務器&#xff0c;利用PBKDF2 加密主密碼&#xff0c;而且…