?
hello !大家好呀! 歡迎大家來到我的Linux高性能服務器編程系列之高性能服務器框架介紹,在這篇文章中,你將會學習到高效的創(chuàng)建自己的高性能服務器,并且我會給出源碼進行剖析,以及手繪UML圖來幫助大家來理解,希望能讓大家更能了解網絡編程技術?。。?/strong>
希望這篇文章能對你有所幫助
,大家要是覺得我寫的不錯的話,那就點點免費的小愛心吧!
(注:這章對于高性能服務器的架構非常重要喲!??!)
? ? ? ? ?
目錄
?一.服務器模型
? ? 1.1 C/S模型
C/S模型的組成
C/S模型的通信過程
1.2 P2P模型
Linux 中的服務器 P2P 模型
二. 兩種高效的服務器事件處理
2.1 Reactor模式
Linux 中的 Reactor 模式
2.2. Proactor模式
Linux 中的 Proactor 模式
?一.服務器模型
? ? 1.1 C/S模型
C/S模型,即客戶端/服務器模型(Client/Server Model),是一種網絡計算模型,它將任務和工作負載分配到客戶端和服務器兩個不同的計算環(huán)境中。在這種模型中,客戶端負責發(fā)送請求,而服務器負責處理請求并返回響應。
如圖:?
C/S模型的組成
-
客戶端(Client):
- 客戶端通常是用戶直接交互的應用程序,例如網頁瀏覽器、電子郵件客戶端或移動應用。
- 它向服務器發(fā)送請求,并接收服務器返回的數據。
- 客戶端可以執(zhí)行一些計算任務,但主要依賴于服務器來處理復雜或數據密集型的任務。
-
服務器(Server):
- 服務器是一個提供數據存儲和服務的系統(tǒng),它響應客戶端的請求。
- 服務器通常擁有強大的計算能力和存儲空間,能夠處理多個客戶端的請求。
- 服務器可以運行數據庫管理系統(tǒng),如 MySQL 或 PostgreSQL,以及各種服務器軟件,如 HTTP 服務器 Apache 或 Nginx。
C/S模型的通信過程
- 請求:客戶端建立一個到服務器的連接,并發(fā)送一個請求。
- 處理:服務器接收到請求后,對其進行處理。
- 響應:服務器將處理結果作為響應發(fā)送回客戶端。
- 關閉連接:客戶端接收響應后,通常關閉與服務器的連接
我們可以使用多線程來進行實現,一個連接的業(yè)務處理分配一個線程:
?
核心代碼如下:
線程處理函數:
// 定義線程函數
void *handle_client(void *socket_desc) {
int sock = *(int*)socket_desc;
char *message;
int len;
// 接收客戶端數據
while((len = read(sock, message, 1024)) > 0) {
printf("收到數據:%s\n", message);
// 發(fā)送響應
write(sock, "Hello, Client!", 14);
memset(message, 0, 1024);
}
// 關閉套接字
close(sock);
return 0;
}
?主函數:
?
int main() {
int sock, newsock, clilen;
struct sockaddr_in serv_addr, cli_addr;
int *new_sock;
pthread_t thread_id;
// 創(chuàng)建套接字
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
printf("Could not create socket");
}
// 填充服務器地址結構
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);
// 綁定套接字到地址
if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("bind failed");
return -1;
}
// 監(jiān)聽套接字
listen(sock, 3);
printf("Listening...\n");
clilen = sizeof(cli_addr);
// 接受客戶端連接
while((newsock = accept(sock, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen)) > 0) {
// 創(chuàng)建新線程
new_sock = malloc(1);
*new_sock = newsock;
if (pthread_create(&thread_id, NULL, handle_client, (void*)new_sock) < 0) {
perror("could not create thread");
return -1;
}
pthread_detach(thread_id);
}
// 關閉套接字
if (newsock < 0) {
perror("accept failed");
return -1;
}
return 0;
}
?
?
1.2 P2P模型
在 Linux 環(huán)境中,P2P(點對點)模型是一種直接連接兩個或多個計算機的網絡通信方式,其中沒有中心服務器參與數據傳輸。在 P2P 模型中,每個節(jié)點既是客戶端也是服務器,可以相互發(fā)送和接收數據。
Linux 中的服務器 P2P 模型
-
節(jié)點間通信:
- P2P 網絡中的每個節(jié)點都直接與其他節(jié)點通信,沒有中央服務器來處理連接和數據傳輸。
- 節(jié)點之間通過套接字(sockets)進行通信,可以建立全雙工連接。
-
網絡拓撲:
- P2P 網絡可以是星型、網狀或混合型,取決于節(jié)點的連接方式和網絡結構。
- 節(jié)點可以通過各種機制發(fā)現其他節(jié)點,如 DHT(分布式哈希表)算法。
如圖:
代碼和C/S相似,大家可以去網上自行尋找資料,這里就不再重復了哦!
二. 兩種高效的服務器事件處理
2.1 Reactor模式
Reactor 模式是一種事件驅動的網絡編程模式,用于處理高并發(fā)網絡服務。在 Reactor 模式中,一個或多個線程負責監(jiān)聽網絡事件,當事件發(fā)生時,例如新的連接請求、數據到達等,Reactor 模式會觸發(fā)相應的處理函數來處理這些事件。
Linux 中的 Reactor 模式
-
事件循環(huán):
- Reactor 模式通常包含一個事件循環(huán),該循環(huán)不斷地輪詢所有事件,等待事件發(fā)生并處理它們。
-
事件處理器:
- 事件處理器是處理特定事件的函數,它們通常與事件類型相關聯。
-
事件分派器:
- 事件分派器負責將事件分發(fā)給相應的事件處理器。
-
事件源:
- 事件源是產生事件的實體,例如網絡套接字、文件描述符等。
流程圖如下:
??
使用的是同步I/O模型。
1)?主線程往epol?l?內核事件表中注冊socket上的讀就緒事件。
2)?主線程調用epol?l?_?wait等待?socket?上有數據可讀?!?/p>
3)當socket?上有數據可讀時,?epoll?_?wait通知主線程。主線程則將socket?可讀事件放入請求隊列?! ?/p>
4)睡眠在請求隊列上的某個工作線程被喚醒,它從?socket?讀取數據,并處理客戶請求,然后往epol?l內核事件表中注冊該socket?上的寫就緒事件。
5)?主線程調用epoll?_?wait等待?socket?可寫。
6)當socket?可寫時,?epoll?wait通知主線程。主線程將socket?可寫事件放入請求隊列?!?/p>
7)睡眠在請求隊列上的某個工作線程被喚醒,它往socket上寫入服務器處理客戶請求的結果。
?
?
2.2. Proactor模式
Proactor 模式是一種事件驅動的網絡編程模式,與 Reactor 模式類似,但它使用異步 I/O 操作來處理網絡事件。在 Proactor 模式中,一個或多個線程負責監(jiān)聽網絡事件,當事件發(fā)生時,例如新的連接請求、數據到達等,Proactor 模式會觸發(fā)相應的處理函數來處理這些事件。
Linux 中的 Proactor 模式
-
事件循環(huán):
- Proactor 模式通常包含一個事件循環(huán),該循環(huán)不斷地輪詢所有事件,等待事件發(fā)生并處理它們。
-
事件處理器:
- 事件處理器是處理特定事件的函數,它們通常與事件類型相關聯。
-
事件分派器:
- 事件分派器負責將事件分發(fā)給相應的事件處理器。
-
事件源:
- 事件源是產生事件的實體,例如網絡套接字、文件描述符等。
-
異步 I/O 操作:
- Proactor 模式使用異步 I/O 操作來處理網絡事件,這樣可以減少線程間的上下文切換,提高系統(tǒng)的性能。
?具體流程圖如下:
1)主線程調用aio?_?read?函數向內核注冊socket?上的讀完成事件,?并告訴內核用戶讀緩沖區(qū)的位置,以及讀操作完成時如何通知應用程序(這里以信號為例,詳情請參考sigevent的man手冊)。
2)主線程繼續(xù)處理其他邏輯?!?/p>
3)當socket上的數據被讀入用戶緩沖區(qū)后,內核將向應用程序發(fā)送一個信號,以通知應用程序數據已經可用?! ?/p>
4)應用程序預先定義好的信號處理函數選擇一個工作線程來處理客戶請求。工作線程處理完客戶請求之后,調用aio?_?write函數向內核注冊?socket上的寫完成事件,?并告訴內核用戶寫緩沖區(qū)的位置,以及寫操作完成時如何通知應用程序(仍然以信號為例)。
5)主線程繼續(xù)處理其他邏輯?! ?/p>
6)當用戶緩沖區(qū)的數據被寫入socket之后,內核將向應用程序發(fā)送一個信號,以通知應用程序數據已經發(fā)送完畢。
7)應用程序預先定義好的信號處理函數選擇一個工作線程來做善后處理,比如決定是否關閉?socket。
?這里給出一個代碼例子:
事件處理函數:
// 事件處理函數
void *handle_connection(void *socket_desc) {
int sock = *(int*)socket_desc;
char *message;
int len;
// 接收客戶端數據
while((len = read(sock, message, 1024)) > 0) {
printf("收到數據:%s\n", message);
// 發(fā)送響應
write(sock, "Hello, Client!", 14);
memset(message, 0, 1024);
}
// 關閉套接字
close(sock);
return 0;
}
循環(huán)邏輯:
while(1) {
activity = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if ((activity < 0) && (errno != EINTR)) {
printf("epoll_wait error");
return -1;
}
for (i = 0; i < activity; i++) {
if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
// 處理錯誤情況
close(events[i].data.fd);
continue;
}
if (events[i].data.fd == sock) {
// 有新的客戶端連接
newsock = accept(sock, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen);
printf("新的客戶端連接:%s\n", inet_ntoa(cli_addr.sin_addr));
client_sockets[i] = newsock;
event.events = EPOLLIN;
event.data.fd = newsock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, newsock, &event) == -1) {
perror("epoll_ctl failed");
return -1;
}
} else {
// 處理客戶端數據
new_sock = malloc(1);
*new_sock = events[i].data.fd;
if (pthread_create(&thread_id, NULL, handle_connection, (void*)new_sock) < 0) {
perror("could not create thread");
return -1;
}
pthread_detach(thread_id);
}
}
}
// 關閉 epoll 實例
close(epollfd);
return 0;
}
在這個例子中,服務器端使用?epoll
?函數來實現 Proactor 模式,創(chuàng)建一個簡單的服務器。服務器使用?pthread
?庫來創(chuàng)建多線程來處理多個客戶端的連接,每個連接都由一個單獨的線程處理。服務器收到客戶端的請求后,發(fā)送一個響應,并關閉與客戶端的連接。(不是完整代碼哦?。?mark hidden color="red">文章來源:http://www.zghlxwxcb.cn/news/detail-857806.html
???好啦!到這里這篇文章就結束啦,關于實例代碼中我寫了很多注釋,如果大家還有不懂得,可以評論區(qū)或者私信我都可以哦
?。?感謝大家的閱讀,我還會持續(xù)創(chuàng)造網絡編程相關內容的,記得點點小愛心和關注喲!
?????文章來源地址http://www.zghlxwxcb.cn/news/detail-857806.html
到了這里,關于【Linux高性能服務器編程】——高性能服務器框架的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!