Linux網絡編程——tcp并發服務器(I/O復用之select)

https://blog.csdn.net/lianghe_work/article/details/46519633

多線程、多進程相比,I/O復用最大的優勢是系統開銷小,系統不需要建立新的進程或者線程,也不必維護這些線程和進程。


代碼示例:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <string.h>
  6. #include <sys/socket.h>
  7. #include <sys/types.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <sys/select.h>
  11. #define SERV_PORT 8080
  12. #define LIST 20 //服務器最大接受連接
  13. #define MAX_FD 10 //FD_SET支持描述符數量
  14. int main(int argc, char *argv[])
  15. {
  16. int sockfd;
  17. int err;
  18. int i;
  19. int connfd;
  20. int fd_all[MAX_FD]; //保存所有描述符,用于select調用后,判斷哪個可讀
  21. //下面兩個備份原因是select調用后,會發生變化,再次調用select前,需要重新賦值
  22. fd_set fd_read; //FD_SET數據備份
  23. fd_set fd_select; //用于select
  24. struct timeval timeout; //超時時間備份
  25. struct timeval timeout_select; //用于select
  26. struct sockaddr_in serv_addr; //服務器地址
  27. struct sockaddr_in cli_addr; //客戶端地址
  28. socklen_t serv_len;
  29. socklen_t cli_len;
  30. //超時時間設置
  31. timeout.tv_sec = 10;
  32. timeout.tv_usec = 0;
  33. //創建TCP套接字
  34. sockfd = socket(AF_INET, SOCK_STREAM, 0);
  35. if(sockfd < 0)
  36. {
  37. perror("fail to socket");
  38. exit(1);
  39. }
  40. // 配置本地地址
  41. memset(&serv_addr, 0, sizeof(serv_addr));
  42. serv_addr.sin_family = AF_INET; // ipv4
  43. serv_addr.sin_port = htons(SERV_PORT); // 端口, 8080
  44. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip
  45. serv_len = sizeof(serv_addr);
  46. // 綁定
  47. err = bind(sockfd, (struct sockaddr *)&serv_addr, serv_len);
  48. if(err < 0)
  49. {
  50. perror("fail to bind");
  51. exit(1);
  52. }
  53. // 監聽
  54. err = listen(sockfd, LIST);
  55. if(err < 0)
  56. {
  57. perror("fail to listen");
  58. exit(1);
  59. }
  60. //初始化fd_all數組
  61. memset(fd_all, -1, sizeof(fd_all));
  62. fd_all[0] = sockfd; //第一個為監聽套接字
  63. FD_ZERO(&fd_read); // 清空
  64. FD_SET(sockfd, &fd_read); //將監聽套接字加入fd_read
  65. int maxfd = fd_all[0]; //監聽的最大套接字
  66. while(1){
  67. // 每次都需要重新賦值,fd_select,timeout_select每次都會變
  68. fd_select = fd_read;
  69. timeout_select = timeout;
  70. // 檢測監聽套接字是否可讀,沒有可讀,此函數會阻塞
  71. // 只要有客戶連接,或斷開連接,select()都會往下執行
  72. err = select(maxfd+1, &fd_select, NULL, NULL, NULL);
  73. //err = select(maxfd+1, &fd_select, NULL, NULL, (struct timeval *)&timeout_select);
  74. if(err < 0)
  75. {
  76. perror("fail to select");
  77. exit(1);
  78. }
  79. if(err == 0){
  80. printf("timeout\n");
  81. }
  82. // 檢測監聽套接字是否可讀
  83. if( FD_ISSET(sockfd, &fd_select) ){//可讀,證明有新客戶端連接服務器
  84. cli_len = sizeof(cli_addr);
  85. // 取出已經完成的連接
  86. connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
  87. if(connfd < 0)
  88. {
  89. perror("fail to accept");
  90. exit(1);
  91. }
  92. // 打印客戶端的 ip 和端口
  93. char cli_ip[INET_ADDRSTRLEN] = {0};
  94. inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
  95. printf("----------------------------------------------\n");
  96. printf("client ip=%s,port=%d\n", cli_ip,ntohs(cli_addr.sin_port));
  97. // 將新連接套接字加入 fd_all 及 fd_read
  98. for(i=0; i < MAX_FD; i++){
  99. if(fd_all[i] != -1){
  100. continue;
  101. }else{
  102. fd_all[i] = connfd;
  103. printf("client fd_all[%d] join\n", i);
  104. break;
  105. }
  106. }
  107. FD_SET(connfd, &fd_read);
  108. if(maxfd < connfd)
  109. {
  110. maxfd = connfd; //更新maxfd
  111. }
  112. }
  113. //從1開始查看連接套接字是否可讀,因為上面已經處理過0(sockfd)
  114. for(i=1; i < maxfd; i++){
  115. if(FD_ISSET(fd_all[i], &fd_select)){
  116. printf("fd_all[%d] is ok\n", i);
  117. char buf[1024]={0}; //讀寫緩沖區
  118. int num = read(fd_all[i], buf, 1024);
  119. if(num > 0){
  120. //收到 客戶端數據并打印
  121. printf("receive buf from client fd_all[%d] is: %s\n", i, buf);
  122. //回復客戶端
  123. num = write(fd_all[i], buf, num);
  124. if(num < 0){
  125. perror("fail to write ");
  126. exit(1);
  127. }else{
  128. //printf("send reply\n");
  129. }
  130. }
  131. else if(0 == num){ // 客戶端斷開時
  132. //客戶端退出,關閉套接字,并從監聽集合清除
  133. printf("client:fd_all[%d] exit\n", i);
  134. FD_CLR(fd_all[i], &fd_read);
  135. close(fd_all[i]);
  136. fd_all[i] = -1;
  137. continue;
  138. }
  139. }else {
  140. //printf("no data\n");
  141. }
  142. }
  143. }
  144. return 0;
  145. }

運行結果:




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

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

相關文章

操作系統【二】死鎖問題以及處理方法

死鎖的概念 死鎖&#xff1a;在并發環境下&#xff0c;個進程因為競爭資源而造成的一種互相等待對方手里的資源&#xff0c;導致各進程都阻塞&#xff0c;無法向前推進的現象。 區別&#xff1a; 饑餓&#xff1a;由于長期得不到想要的資源進程無法向前推進的現象。死循環&a…

Linux網絡編程——I/O復用之poll函數

https://blog.csdn.net/lianghe_work/article/details/46534029一、回顧前面的selectselect優點&#xff1a;目前幾乎在所有的平臺上支持&#xff0c;其良好跨平臺支持也是它的一個優點select缺點&#xff1a;1.每次調用 select()&#xff0c;都需要把 fd 集合從用戶態拷貝到內…

操作系統【一】進程同步和信號量

基本概念 進程異步性特征&#xff1a;各并發執行的進程以各自獨立的&#xff0c;不可預知的速度向前推進。 進程同步又稱作直接制約關系&#xff0c;他是指為完成某種任務而建立的兩個或者多個進程&#xff0c;這些進程因為需要在某些位置上協調他們的工作順序而產生的制約關…

計算機網絡【四】數據鏈路層基本概念+點到點通信(PPP協議)

數據鏈路層基本概念 路由器是網絡層設備 數據鏈路層&#xff1a;數據管道&#xff0c;傳輸的是數據包加上發送地址&#xff0c;接收地址&#xff0c;校驗的數據幀 數據鏈路層的信道類型&#xff1a; 點到點信道&#xff1a;使用一對一的點到點通信方式&#xff08;兩個設備…

Linux網絡編程——tcp并發服務器(poll實現)

https://blog.csdn.net/lianghe_work/article/details/46535859想詳細徹底地了解poll或看懂下面的代碼請參考《Linux網絡編程——I/O復用之poll函數》 代碼&#xff1a;#include <string.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#…

二分查找的最大比較次數

二分查找很簡單&#xff0c;可是對于一個區間長度為n的數組&#xff0c;最大的比較次數為多少呢&#xff1f; 對于標準的二分查找&#xff0c;我們每次從區間[l,r)中取一個值&#xff0c;和中間值mid(lr)>>1進行比較&#xff0c;然后將數組分為[l,mid) [mid1,r)&#xf…

Linux網絡編程——I/O復用函數之epoll

https://blog.csdn.net/lianghe_work/article/details/46544567一、epoll概述epoll 是在 2.6 內核中提出的&#xff0c;是之前的 select() 和 poll() 的增強版本。相對于 select() 和 poll() 來說&#xff0c;epoll 更加靈活&#xff0c;沒有描述符限制。epoll 使用一個文件描述…

操作系統【三】內存管理基礎+連續內存分配

內存的基礎知識 內存分為按字節編址&#xff08;8位&#xff09;和字編制&#xff08;不同計算機不一樣&#xff0c;64位計算機就是64位&#xff0c;即8個字節&#xff09; 相對地址邏輯地址 絕對地址物理地址 從邏輯地址到物理地址的轉換由裝入解決。 裝入的三種方式 絕對…

MSG_PEEK標志

https://blog.csdn.net/aspnet_lyc/article/details/28937229 MSG_PEEK標志可以用來讀取套接字接收隊列中可讀的數據&#xff0c;一些情況會用到它&#xff0c;比如為了避免不阻塞而先檢查套接字接收隊列中可讀的數據長度&#xff0c;再采取相應操作。當然&#xff0c;不阻塞也…

快速排序詳解+各種實現方式

快速排序的思想大體來說比較簡單&#xff0c;就是從數組中挑選一個數字當做樞紐&#xff0c;然后將比樞紐大的和比樞紐小的分別放在樞紐的兩邊&#xff0c;再遞歸地對兩邊進行操作&#xff0c;從而進行分治解決問題。平均情況下快速排序是復雜度為O(nlogn)O(nlogn)O(nlogn)&…

C++的單例模式與線程安全單例模式(懶漢/餓漢)

https://www.cnblogs.com/qiaoconglovelife/p/5851163.html1 教科書里的單例模式我們都很清楚一個簡單的單例模式該怎樣去實現&#xff1a;構造函數聲明為private或protect防止被外部函數實例化&#xff0c;內部保存一個private static的類指針保存唯一的實例&#xff0c;實例的…

計算矩陣的逆和行列式的值(高斯消元+LU分解)

計算矩陣的逆 選主元的高斯消元法 樸素的高斯消元法是將矩陣A和單位矩陣放在一起&#xff0c;通過行操作&#xff08;或者列操作&#xff09;將A變為單位矩陣&#xff0c;這個時候單位矩陣就是矩陣A的逆矩陣。從上到下將A變為上三角矩陣的復雜度為O(n3n^3n3)&#xff0c;再從下…

Linux網絡編程——tcp并發服務器(epoll實現)

https://blog.csdn.net/lianghe_work/article/details/46551871通過epoll實現tcp并發回執服務器&#xff08;客戶端給服務器發啥&#xff0c;服務器就給客戶端回啥&#xff09; 代碼如下&#xff1a;#include <string.h>#include <stdio.h>#include <stdlib.h&g…

證明AVL樹的上界和下界

對于n個節點的AVL樹&#xff0c;其高度最低的時候肯定為葉子節點只在最后一層和倒數第二層的時候。即對于2k?1<n≦2k1?12^k-1< n\leqq 2^{k1}-12k?1<n≦2k1?1的時候下界都為kkk。因此下界為h┌log2(n1)┐?1h\ulcorner log_2(n1)\urcorner-1h┌log2?(n1)┐?1 對…

淺談dup和dup2的用法

https://blog.csdn.net/u012058778/article/details/78705536一、dup和dup2函數 這兩個函數都可以來復制一個現有的文件描述符&#xff0c;他們的聲明如下&#xff1a;#include <unistd.h>int dup(int fd);int dup2(int fd, int fd 2); 123 關于dup函數&#xff0c;當我…

C++ cin 實現循環讀入

習慣了使用while(~scanf("%d",x)){}來實現循環讀入&#xff0c;但是有時候使用泛型編程的時候就必須使用C中的cin&#xff0c;但是當我想要實現循環讀入的時候卻發現有些困難。 我們可以看一下下面這個簡單的例子&#xff1a; #include <iostream>using name…

BFPTR算法詳解+實現+復雜度證明

BFPTR算法是由Blum、Floyed、Pratt、Tarjan、Rivest這五位牛人一起提出來的&#xff0c;其特點在于可以以最壞復雜度為O(n)O(n)O(n)地求解top?ktop-ktop?k問題。所謂top?ktop-ktop?k問題就是從一個序列中求解其第k大的問題。 top?ktop-ktop?k問題有許多解決方法&#xff…

C++子類對象隱藏了父類的同名成員函數(隱藏篇)

https://blog.csdn.net/alpha_love/article/details/75222175#include <iostream>#include <stdlib.h>#include <string>using namespace std;/*** 定義人類: Person* 數據成員: m_strName* 成員函數: attack()*/class Person{public:Person(){cout<<&…

隨機化快速排序+快速選擇 復雜度證明+運行測試

對于快速排序和快速選擇我之前的文章已經有詳細的說明&#xff0c;需要了解的同學可以移步 傳送門&#xff1a;快速排序&#xff5c;快速選擇(BFPTR) 所謂隨機化其實就是選擇樞紐的時候使用隨機數選擇而已&#xff0c;實現起來很簡單。但是我們使用隨機數如何保證復雜度呢&am…