Linux中 socket編程中多進程/多線程TCP并發服務器模型

一、循環服務器(while)【不常用】

  1. 一次只能處理一個客戶端的請求,等這個客戶端退出后,才能處理下一個客戶端。
  2. 缺點:循環服務器所處理的客戶端不能有耗時操作。

模型

sfd = socket();
bind();
listen();
while(1)
{newfd = accept();while(1){recv();send();    }close(newfd);
}
close(sfd);

源碼

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <head.h>#define PORT 6666               //1024-49151
#define IP "192.168.122.80"    //ifconfig查看本機ipint main(int argc, const char *argv[])
{//創建流式套接字int sfd = socket(AF_INET,SOCK_STREAM,0);if(sfd < 0){ERR_MSG("socket");return -1;}printf("sfd= %d\n",sfd);                                                                                                    //填充地址信息結構體,真實的地址信息結構體根據地址族制定//AF_INET: man 7 ipstruct sockaddr_in sin;sin.sin_family         = AF_INET;       //必須填AF_INETsin.sin_port           = htons(PORT);   //端口號:1024~49151(網絡端口號的字節序)(端口號存儲在2個字節的無符號整數中)sin.sin_addr.s_addr    = inet_addr(IP); //本機IP inconfig查看(本機IP地址的字節序)//設置端口允許端口被快速復用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_MSG("setsockopt");return -1;}printf("允許端口快速重用成功\n");//綁定服務器的IP和端口號--->必須綁定( bind )if(bind(sfd , (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("bind");return -1;}printf("bind success\n");//將套接字設置為被動監聽狀態( listen)if( listen(sfd,128) < 0){ERR_MSG("listen");return -1;}printf("listen success\n");struct sockaddr_in cin;   //存儲客戶端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1;//從已完成連接的隊列中獲取一個客戶端信息,生成一個新的文件描述符//該文件描述符才是與客戶端的通信的文件描述符//int newfd = accept(sfd, NULL ,NULL); while(1){newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1;}printf("[%s : %d]newfd=%d 客戶端連接成功\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);char buf[128]="";ssize_t res = 0;while(1){//接收數據bzero(buf,sizeof(buf));res=recv(newfd,buf,sizeof(buf),0);if(res < 0){ERR_MSG("res");return -1;}else if(0 == res){printf("[%s : %d]newfd=%d 客戶端已下線\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);break;}printf("[%s : %d]newfd=%d : %s\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd, buf);//發送數據strcat(buf,"*_*");if(send(newfd,buf,sizeof(buf),0) < 0){ERR_MSG("send");return -1;}printf("send succuss\n");}}//關閉所有文件描述符close(newfd);close(sfd);return 0;
}

二、并發服務器【常用】

  1. 可以同時處理多個客戶端請求
  2. 父進程 / 主線程專門用于負責連接,創建子進程 / 分支線程用來與客戶端交互。

1) 多進程

模型

void zombie_callBack(int sig)
{while(waitpid(-1, NULL, WNOHANG) > 0);
}signal(17, zombie_callback);sfd = socket();
bind();
listen();
while(1)
{newfd = accept();if(0 == fork()){close(sfd) ;recv();send();close(newfd);exit(0);   }close(newfd);
}
close(sfd);

源碼

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <head.h>#define PORT 6666               //1024-49151
#define IP "192.168.122.80"    //ifconfig查看本機ipint deal_cli_msg(int newfd,struct sockaddr_in cin);void handlr(int sig)
{while(waitpid(-1,NULL,WNOHANG) > 0);
}int main(int argc, const char *argv[])
{if(signal(17,handlr) == SIG_ERR){ERR_MSG("signal");return -1;}printf("捕獲成功\n");//創建流式套接字int sfd = socket(AF_INET,SOCK_STREAM,0);if(sfd < 0){ERR_MSG("socket");return -1;}printf("sfd= %d\n",sfd);                                                                                                    //設置端口允許端口被快速復用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_MSG("setsockopt");return -1;}printf("允許端口快速重用成功\n");//填充地址信息結構體,真實的地址信息結構體根據地址族制定//AF_INET: man 7 ipstruct sockaddr_in sin;sin.sin_family         = AF_INET;       //必須填AF_INETsin.sin_port           = htons(PORT);   //端口號:1024~49151(網絡端口號的字節序)(端口號存儲在2個字節的無符號整數中)sin.sin_addr.s_addr    = inet_addr(IP); //本機IP inconfig查看(本機IP地址的字節序)//綁定服務器的IP和端口號--->必須綁定( bind )if(bind(sfd , (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("bind");return -1;}printf("bind success\n");//將套接字設置為被動監聽狀態( listen)if( listen(sfd,128) < 0){ERR_MSG("listen");return -1;}printf("listen success\n");struct sockaddr_in cin;   //存儲客戶端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1;pid_t cpid = 0;while(1){newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1;}printf("[%s : %d]newfd=%d 客戶端連接成功\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);cpid = fork();if(0 == cpid){close(sfd);deal_cli_msg(newfd,cin);exit(0);}close(newfd);}//關閉所有文件描述符close(sfd);return 0;
}int deal_cli_msg(int newfd,struct sockaddr_in cin)
{char buf[128]="";ssize_t res = 0;while(1){//接收數據bzero(buf,sizeof(buf));res=recv(newfd,buf,sizeof(buf),0);if(res < 0){ERR_MSG("recv");return -1;}else if(0 == res){printf("[%s : %d]newfd=%d 客戶端已下線\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);break;}printf("[%s : %d]newfd=%d : %s\n",\inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd, buf);//發送數據strcat(buf,"*_*");if(send(newfd,buf,sizeof(buf),0) < 0){ERR_MSG("send");return -1;}printf("send succuss\n");}close(newfd);
}

2) 多線程

模型

sfd = socket();
bind();
listen();
while(1)
{newfd = accept();pthread_create();     --> callBack();pthread_detach(tid);
}
close(sfd);void* callBack(void* arg)
{參數另存recv();send();close(newfd);pthread_exit(NULL);
}

源碼

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>#define ERR_MSG(msg) do{\fprintf(stderr, "line:%d ", __LINE__);\perror(msg);\
}while(0)#define PORT 6666               //1024~49151
#define IP  "127.0.0.1"     //IP地址,本機IP ifconfigstruct cli_msg
{int newfd;struct sockaddr_in cin;
};void* deal_cli_msg(void* arg);int main(int argc, const char *argv[])
{//創建流式套接字int sfd = socket(AF_INET, SOCK_STREAM, 0); if(sfd < 0){   ERR_MSG("socket");return -1; }   printf("socket create success  sfd = %d\n", sfd);//設置允許端口快速被重用int resue = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) < 0){   ERR_MSG("setsockopt");return -1; }   //填充服務器的地址信息結構體//真實的地址信息結構體根據地址族執行,AF_INET: man 7 ipstruct sockaddr_in sin;sin.sin_family      = AF_INET;      //必須填AF_INET;sin.sin_port        = htons(PORT);  //端口號的網絡字節序,1024~49151sin.sin_addr.s_addr = inet_addr(IP);    //IP地址的網絡字節序,ifconfig查看//綁定---必須綁定if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0){   ERR_MSG("bind");return -1; }   printf("bind success __%d__\n", __LINE__);//將套接字設置為被動監聽狀態if(listen(sfd, 128) < 0){   ERR_MSG("listen");return -1; }   printf("listen success __%d__\n", __LINE__);//功能:阻塞函數,阻塞等待客戶端連接成功。//當客戶端連接成功后,會從已完成連接的隊列頭中獲取一個客戶端信息,//并生成一個新的文件描述符;新的文件描述符才是與客戶端通信的文件描述符struct sockaddr_in cin;     //存儲連接成功的客戶端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1; pthread_t tid;struct cli_msg info;while(1){   //主線程負責連接//accept函數阻塞之前,會先預選一個沒有被使用過的文件描述符//當解除阻塞后,會判斷預選的文件描述符是否被使用//如果被使用了,則重新遍歷一個沒有被用過的//如果沒有被使用,則直接返回預先的文件描述符;newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1; }printf("[%s : %d] newfd=%d 客戶端連接成功\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);info.newfd = newfd;info.cin = cin;//能運行到當前位置,則代表有客戶端連接成功//則需要創建一個分支線程用來,與客戶端交互if(pthread_create(&tid, NULL, deal_cli_msg, &info) != 0){fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);return -1; }pthread_detach(tid);        //分離線程}   //關閉所有套接字文件描述符close(sfd);return 0;
}//線程執行體
void* deal_cli_msg(void* arg)   //void* arg = (void*)&info
{//必須要另存,因為同一個進程下的線程共享其附屬進程的所有資源//如果使用全局,則會導致每次連接客戶端后, newfd和cin會被覆蓋//如果使用指針間接訪問外部成員變量,也會導致,成員變量被覆蓋。int newfd = ((struct cli_msg*)arg)->newfd;struct sockaddr_in cin = ((struct cli_msg*)arg)->cin;char buf[128] = ""; ssize_t res = -1; while(1){   bzero(buf, sizeof(buf));//接收res = recv(newfd, buf, sizeof(buf), 0); if(res < 0){ERR_MSG("recv");break;}else if(0 == res){fprintf(stderr, "[%s : %d] newfd=%d 客戶端下線\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);break;}printf("[%s : %d] newfd=%d : %s\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf);//發送 -- 將數據拼接一個 *_* 發送回去strcat(buf, "*_*");if(send(newfd, buf, sizeof(buf), 0) < 0){ERR_MSG("send");break;}printf("send success\n");}   close(newfd);pthread_exit(NULL);
}

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

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

相關文章

分別在linux和windows上設置socket為阻塞模式

在 Linux 和 Windows 系統中&#xff0c;都可以將 socket 設置為非阻塞模式。 Linux平臺 在 Linux 系統中&#xff0c;可以使用 fcntl 函數來設置 socket 為非阻塞模式。例如&#xff1a; int flags fcntl(socket_fd, F_GETFL, 0); fcntl(socket_fd, F_SETFL, flags | O_NO…

【問心篇】渴望、熱情和選擇

加班太嚴重完全沒有時間學習&#xff0c;怎么辦&#xff1f; 我真的不算聰明的人&#xff0c;但是&#xff0c;我對學習真的是有渴望的。說得好聽一點&#xff0c;我希望自己在不停地成長&#xff0c;不辜負生活在這個信息化大變革的時代。說得不好的一點&#xff0c;就是我從…

斷點續傳的未來發展趨勢與前景展望

斷點續傳是一種在網絡傳輸中斷后&#xff0c;能夠從中斷的位置繼續傳輸的技術。它可以有效地避免因為網絡不穩定、服務器故障、用戶操作等原因導致的傳輸失敗&#xff0c;節省了用戶的時間和流量&#xff0c;提高了傳輸的效率和可靠性。斷點續傳在很多場景中都有廣泛的應用&…

AI 繪畫Stable Diffusion 研究(八)sd采樣方法詳解

大家好&#xff0c;我是風雨無阻。 本文適合人群&#xff1a; 希望了解stable Diffusion WebUI中提供的Sampler究竟有什么不同&#xff0c;想知道如何選用合適采樣器以進一步提高出圖質量的朋友。 想要進一步了解AI繪圖基本原理的朋友。 對stable diffusion AI繪圖感興趣的朋…

【洛谷 P5736】【深基7.例2】質數篩 題解(埃氏篩法)

【深基7.例2】質數篩 題目描述 輸入 n n n 個不大于 1 0 5 10^5 105 的正整數。要求全部儲存在數組中&#xff0c;去除掉不是質數的數字&#xff0c;依次輸出剩余的質數。 輸入格式 第一行輸入一個正整數 n n n&#xff0c;表示整數個數。 第二行輸入 n n n 個正整數 …

jquery如何修改選中狀態

jquery修改選中狀態的方法&#xff1a;1、使用addClass和removeClass方法&#xff0c;可以向選中的元素添加一個多個類名&#xff0c;從而改變其樣式或狀態&#xff1b;2、使用toggleClass方法&#xff0c;可以在選中元素上添加或移除一個類名&#xff0c;如果該類名已經存在&a…

208. 實現 Trie (前綴樹)

Trie&#xff08;發音類似 "try"&#xff09;或者說 前綴樹 是一種樹形數據結構&#xff0c;用于高效地存儲和檢索字符串數據集中的鍵。這一數據結構有相當多的應用情景&#xff0c;例如自動補完和拼寫檢查。 請你實現 Trie 類&#xff1a; Trie() 初始化前綴樹對象…

手撕LFU緩存

手撕LRU緩存_右大臣的博客-CSDN博客 是LRU的升級&#xff0c;多了一個訪問次數的維度 實現 LFUCache 類&#xff1a; LFUCache(int capacity) - 用數據結構的容量 capacity 初始化對象int get(int key) - 如果鍵 key 存在于緩存中&#xff0c;則獲取鍵的值&#xff0c;否則返…

C# Lamda到底是什么?!

lamda作為匿名函數&#xff0c;現在已經能夠出現子啊C#程序的任何可能位置&#xff0c;它可能作為參數為委托或其他函數復制&#xff0c;或者單獨作為表達式&#xff0c;或者承擔一些類似C中內聯函數的一些作用承擔一些簡單計算。熟練的使用Lamda表達式能夠讓減少代碼的冗余&am…

Django圖書商城系統實戰開發-總結經驗之后端開發

Django圖書商城系統實戰開發-總結經驗之后端開發 簡介 在這篇博客中&#xff0c;我將總結經驗分享后端開發Django圖書商城系統的過程。在開發過程中&#xff0c;我遇到了各種挑戰和問題&#xff0c;并且通過實踐獲得了寶貴的經驗和教訓。通過本文&#xff0c;我希望能幫助讀者…

vue3+vite配置vantUI主題

?在項目中統一配置UI主題色&#xff0c;各個組件配色統一修改 vantUI按需安裝 參考vantUI文檔 創建vantVar.less文件夾進行樣式編寫 vantVar.less :root:root{//導航--van-nav-bar-height: 44px;//按鈕--van-button-primary-color: #ffffff;--van-button-primary-backgr…

linux——mysql的高可用MHA

目錄 一、概述 一、概念 二、組成 三、特點 四、工作原理 二、案例 三、構建MHA 一、基礎環境 二、ssh免密登錄 三、主從復制 master slave1 四、MHA安裝 一、環境 二、安裝node 三、安裝manager 一、概述 一、概念 MHA&#xff08;MasterHigh Availability&a…

力扣 198. 打家劫舍

題目來源&#xff1a;https://leetcode.cn/problems/house-robber/description/ C題解&#xff1a;因為是間接偷竊&#xff0c;所以偷nums[i]家前&#xff0c;一定偷過第i-2或者i-3家&#xff0c;因為i-1不能偷。 例如12345共5家&#xff0c;先偷第1家&#xff0c;那么2不能偷…

(三)Unity開發Vision Pro——入門

3.入門 1.入門 本節涵蓋了幾個重要主題&#xff0c;可幫助您加快visionOS 平臺開發速度。在這里&#xff0c;您將找到構建第一個 Unity PolySpatial XR 應用程序的分步指南的鏈接&#xff0c;以及 PolySpatial XR 開發時的一些開發最佳實踐。 2.開發與迭代 有關先決條件、開…

顯卡nvidia-smi后 提示Faild 解決過程,包含卸載重裝NVIDIA驅動步驟

顯卡異常: 顯卡nvidia-smi后 提示Faild 解決過程&#xff0c;卸載重裝nvidia驅動步驟 文章目錄 顯卡異常: 顯卡nvidia-smi后 提示Faild 解決過程&#xff0c;卸載重裝nvidia驅動步驟 [toc]1 緣由2 解決過程3 過程所需命令4 解決4.1 把該顯卡重新拔插一下卸載NVIDIA驅動的方法&a…

單元測試優化:為什么要對程序進行測試?測試有什么好處?

單元測試&#xff08;Unit Testing&#xff09;又稱為模塊測試, 是針對程序模塊&#xff08;軟件設計的最小單位&#xff09;來進行正確性檢驗的測試工作。 程序單元是應用的最小可測試部件。簡單來說&#xff0c;就是測試數據的穩定性是否達到程序的預期。 我們日常開發時可能…

19、SQL注入之SQLMAP繞過WAF

目錄 邏輯層1、邏輯問題2、性能問題 白名單方式一&#xff1a;IP白名單方式二&#xff1a;靜態資源方式三&#xff1a;url白名單方式四: 爬蟲白名單 sqlmap在測試漏洞的時候&#xff0c;選擇了no&#xff0c;它就不會去測試其它的了&#xff0c;我們一般選擇yes&#xff0c;為了…

Deep Learning With Pytorch - 最基本的感知機、貫序模型/分類、擬合

文章目錄 如何利用pytorch創建一個簡單的網絡模型&#xff1f;Step1. 感知機&#xff0c;多層感知機&#xff08;MLP&#xff09;的基本結構Step2. 超平面 ω T ? x b 0 \omega^{T}xb0 ωT?xb0 or ω T ? x b \omega^{T}xb ωT?xb感知機函數 Step3. 利用感知機進行決策…

SpringBoot整合Minio

SpringBoot整合Minio 在企業開發中&#xff0c;我們經常會使用到文件存儲的業務&#xff0c;Minio就是一個不錯的文件存儲工具&#xff0c;下面我們來看看如何在SpringBoot中整合Minio POM pom文件指定SpringBoot項目所依賴的軟件工具包 <?xml version"1.0" …

Ubuntu上安裝RabbitMQ

在Ubuntu上安裝RabbitMQ并設置管理員用戶為"admin"&#xff0c;密碼為"123456"&#xff0c;并開啟開機自啟 更新系統軟件包列表。在終端中執行以下命令&#xff1a; sudo apt update安裝RabbitMQ服務器軟件包。運行以下命令&#xff1a; sudo apt insta…