1. 程式人生 > >I/O多路複用伺服器程式設計

I/O多路複用伺服器程式設計

一、實驗目的

理解I/O多路複用技術的原理。

學會編寫基本的單執行緒併發伺服器程式和客戶程式。

二、實驗平臺

ubuntu-8.04作業系統

三、實驗內容

採用I/O多路複用技術實現單執行緒併發伺服器,完成使用一個執行緒處理併發客戶請求的功能。

四、實驗原理

除了可以採用多程序和多執行緒方法實現併發伺服器之外,還可以採用I/O多路複用技術。通過該技術,系統核心緩衝I/O資料,當某個I/O準備好後,系統通知應用程式該I/O可讀或可寫,這樣應用程式可以馬上完成相應的I/O操作,而不需要等待系統完成相應I/O操作,從而應用程式不必因等待I/O操作而阻塞。

與多程序和多執行緒技術相比,I/O多路複用技術的最大優勢是系統開銷小,系統不必建立程序

/執行緒,也不必維護這些程序/執行緒,從而大大減小了系統的開銷。

對於I/O複用典型的應用如下:

1)當客戶處理多個描述字時(一般是互動式輸入和網路套介面),必須使用I/O複用。

2)當一個客戶同時處理多個套介面時,而這種情況是可能的,但很少出現。

3)如果一個TCP伺服器既要處理監聽套介面,又要處理已連線套介面,一般也要用到I/O複用。

4)如果一個伺服器即要處理TCP,又要處理UDP,一般要使用I/O複用。

5)如果一個伺服器要處理多個服務或多個協議,一般要使用I/O複用。

I/O複用呼叫select()poll()函式,並在該函式上阻塞,等待資料報套介面可讀;當select()返回可讀條件時,呼叫

recvfrom()將資料報拷貝到應用程式緩衝區中,如圖8.1所示。

8.1 I/O多路複用工作過程

select()函式:

select()函式允許程序指示核心等待多個事件中的任意一個發生,並僅在一個或多個事件發生或經過指定的時間時才喚醒程序。這個函式的形式如下:

-------------------------------------------------------------------
#include<sys/select.h>

#include<sys/time.h>

intselect(intmaxfdp1,fd_set*readset,fd_set*writeset,

fd_set*execepset,conststructtimeval*timeout);

返回:返回值表示所有描述字集中已準備好的描述字個數。如定時到,則返回0;若出錯,則返回-1

-------------------------------------------------------------------

在上面的引數中可以看到一個timeval結構,這個結構可以提供秒數和毫秒數成員,形式如下:

structtimeval

{

long tv_sec; /second*/

long tv_usec; /*microsecond*/

}

這個timeval結構有以下3種可能:

1)永遠等待下去:僅在有一個描述字準備好I/O時才返回,因此可以將引數timeout設定為空指標。

2)等待固定時間:在有一個描述字準備好I/O時返回,但不超過由timeout引數所指timeval結構中指定的秒數和微秒數。

3)根本不用等待:檢查描述字後立即返回,這稱為輪詢(polling)。

在前兩種情況的等待中,如果程序捕獲了一個訊號並從訊號處理程式返回,那麼等待一般被中斷。

引數readsetwritesetexeceptset指定讓核心測試讀、寫、異常條件的描述字。如果我們對它們不感興趣,可將其設為空指標。

select()函式使用描述字集為引數readsetwritesetexceptset)指定多個描述字,描述字集是一個整數陣列,每個數中的每一個對應於一個描述字,例如32位整數,則陣列的第一個元素對應於031描述字,第二個元素對應於3263描述字等。

引數readsetwritesetexceptset為值結果引數,呼叫select時,指定我們所關心的描述字,返回時結果指示那些描述字已準備好。

引數maxfdp1指定被測試的描述字的個數,它是被測試的最大描述字加1。如要測試124描述字,則必須測試012345個描述字。

採用select()函式實現I/O多路複用的基本步驟如下:

1)清空描述符集合;

2)建立需要監視的描述符與描述符集合的聯絡;

3)呼叫select()函式;

4)檢查所有需要監視的描述符,利用FD_ISSET巨集判斷是否已經準備好;

5)對已經準備好的描述符進行I/O操作。

五、實驗步驟

1、登陸進入ubuntu作業系統,新建一個檔案,命名為io.c

2、在io.c中編寫相應程式碼並儲存,作為伺服器端程式。客戶端程式程式碼同上次的mproc_client.c一致,部落格地址:http://blog.csdn.net/yueguanghaidao/article/details/7060350

3、開啟一個終端,執行命令進入io.cmproc_client.c所在目錄。

4、執行命令g++oioio.c生成可執行檔案io

5、執行命令./io,執行伺服器端。

6、開啟第2終端,執行命令進入io.cmproc_client.c所在目錄。

7、執行命令./mproc_client127.0.0.1,模擬客戶1

8、開啟第3終端,執行命令進入io.cmproc_client.c所在目錄。

9、執行命令./mproc_client127.0.0.1,模擬客戶2

10、程式執行結果如下:

伺服器端:


客戶1


客戶2


11、在客戶端按下Ctrl+D,關閉客戶連線。

12、認真分析原始碼,體會單執行緒併發伺服器程式和客戶程式的編寫。

六、參考程式(io.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include<sys/types.h>
  6. #include<sys/socket.h>
  7. #include<netinet/in.h>
  8. #include<arpa/inet.h>
  9. #include<sys/time.h>
  10. #define PORT 1234
  11. #define BACKLOG 5
  12. #define MAXDATASIZE  1000
  13. typedefstruct {  
  14. int fd;  
  15. char  *name;  
  16. struct  sockaddr_in   addr;  
  17. char *data;  
  18. }CLIENT;  
  19. void  process_cli(CLIENT  *client, char* recvbuf, int len);  
  20. void savedata(char*recvbuf, int len, char* data);  
  21. main()  
  22. {  
  23. int i, maxi,maxfd,sockfd;  
  24. int nready;  
  25. ssize_t  n;  
  26. fd_set  rset, allset;  
  27. int listenfd,connectfd;  
  28. struct sockaddr_in  server;  
  29. CLIENT  client[FD_SETSIZE];  
  30. char recvbuf[MAXDATASIZE];  
  31. socklen_t  sin_size;  
  32. if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
  33. perror("Creatingsocket failed.");  
  34. exit(1);  
  35. }  
  36. int opt =SO_REUSEADDR;  
  37. setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
  38. bzero(&server,sizeof(server));  
  39. server.sin_family=AF_INET;  
  40. server.sin_port=htons(PORT);  
  41. server.sin_addr.s_addr= htonl (INADDR_ANY);  
  42. if (bind(listenfd,(struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {  
  43. perror("Bind()error.");  
  44. exit(1);  
  45. }  
  46. if(listen(listenfd,BACKLOG)== -1){  
  47. perror("listen()error\n");  
  48. exit(1);  
  49. }  
  50. sin_size=sizeof(struct   sockaddr_in);  
  51. maxfd = listenfd;  
  52. maxi = -1;  
  53. for (i = 0; i <FD_SETSIZE; i++) {  
  54. client[i].fd =-1;  
  55. }  
  56. FD_ZERO(&allset);  
  57. FD_SET(listenfd,&allset);  
  58. while(1)  
  59. {  
  60. struct sockaddr_in   addr;  
  61. rset = allset;  
  62. nready =select(maxfd+1, &rset, NULL, NULL, NULL);  
  63. if(FD_ISSET(listenfd, &rset)) {  
  64. if ((connectfd =accept(listenfd,(struct sockaddr *)&addr,&sin_size))==-1) {  
  65. perror("accept() error\n");  
  66. continue;  
  67. }  
  68. for (i = 0; i <FD_SETSIZE; i++)  
  69. if(client[i].fd < 0) {  
  70. client[i].fd = connectfd;  
  71. client[i].name = newchar[MAXDATASIZE];  
  72. client[i].addr = addr;  
  73. client[i].data = newchar[MAXDATASIZE];  
  74. client[i].name[0] = '\0';  
  75. client[i].data[0] = '\0';  
  76. printf("You got a connection from %s. ",inet_ntoa(client[i].addr.sin_addr) );  
  77. break;  
  78. }  
  79. if (i ==FD_SETSIZE) printf("too many clients\n");  
  80. FD_SET(connectfd, &allset);  
  81. if (connectfd> maxfd) maxfd = connectfd;  
  82. if (i >maxi) maxi = i;  
  83. if (--nready<= 0) continue;  
  84. }  
  85. for (i = 0; i <=maxi; i++) {  
  86. if ( (sockfd= client[i].fd) < 0) continue;  
  87. if(FD_ISSET(sockfd, &rset)) {  
  88. if ( (n =recv(sockfd, recvbuf, MAXDATASIZE,0)) == 0) {  
  89. close(sockfd);  
  90. printf("Client( %s ) closed connection. User's data:%s\n",client[i].name,client[i].data);  
  91. FD_CLR(sockfd, &allset);  
  92. client[i].fd = -1;  
  93. delete  client[i].name;  
  94. delete  client[i].data;  
  95. }  
  96. else
  97. process_cli(&client[i], recvbuf, n);  
  98. if(--nready <= 0) break;  
  99. }  
  100. }  
  101. }  
  102. close(listenfd);  
  103. }  
  104. void process_cli(CLIENT *client, char* recvbuf, int len)  
  105. {  
  106. char  sendbuf[MAXDATASIZE];  
  107. recvbuf[len-1] ='\0';  
  108. if(strlen(client->name) == 0) {  
  109. memcpy(client->name,recvbuf, len);  
  110. printf("Client'sname is %s.\n",client->name);  
  111. return;  
  112. }  
  113. printf("Receivedclient( %s ) message: %s\n",client->name, recvbuf);  
  114. savedata(recvbuf,len,client->data);  
  115. for (int i1 = 0; i1< len - 1; i1++) {  
  116. sendbuf[i1] =recvbuf[len - i1 -2];  
  117. }  
  118. sendbuf[len - 1] ='\0';  
  119. send(client->fd,sendbuf,strlen(sendbuf),0);  
  120. }  
  121. void savedata(char  *recvbuf, int len, char   *data)  
  122. {  
  123. int start =strlen(data);  
  124. for (int i = 0; i <len; i++) {  
  125. data[start + i]= recvbuf[i];  
  126. }  
  127. }  

相關推薦

I/O伺服器程式設計

一、實驗目的 理解I/O多路複用技術的原理。 學會編寫基本的單執行緒併發伺服器程式和客戶程式。 二、實驗平臺 ubuntu-8.04作業系統 三、實驗內容 採用I/O多路複用技術實現單執行緒併發伺服器,完成使用一個執行緒處理併發客戶請求的功能。 四、實驗原理 除了可以採用多

網路程式設計(4)select函式實現I/O伺服器

    我按理解整了個基於select模式的單程序多路複用併發伺服器,並寫了個簡單的測試程式測了下,雖然離實用還差得遠,但用來練習select夠用了。 至於如何實現的細節,程式碼註釋算比較清楚,就不多弄了。 一。伺服器部份 單程序併發伺服器程式碼: /*********

嵌入式Linux網路程式設計I/O,epoll()示例,epoll()客戶端,epoll()伺服器,單鏈表

文章目錄 1,I/O多路複用 epoll()示例 1.1,epoll()---net.h 1.2,epoll()---client.c 1.3,epoll()---sever.c 1.4,epoll()---linklist.h

嵌入式Linux網路程式設計I/O,poll()示例,poll()客戶端,poll()伺服器,單鏈表

文章目錄 1,IO複用poll()示例 1.1,poll()---net.h 1.2,poll()---client.c 1.3,poll()---sever.c 1.4,poll()---linklist.h 1.5,p

嵌入式Linux網路程式設計I/O,select()示例,select()客戶端,select()伺服器,單鏈表

文章目錄 1,IO複用select()示例 1.1 select()---net.h 1.2 select()---client.c 1.3 select()---sever.c 1.4 select()---linklist.h

Linux網路程式設計---I/O之select

1.I/O多路複用(IO multiplexing) 我們之前講了I/O多路複用和其他I/O的區別,在這裡,我們再具體討論下I/O多路複用是怎麼工作? I/O 多路複用技術就是為了解決程序或執行緒阻塞到某個 I/O 系統呼叫而出現的技術,使程序不阻塞於某個特定的 I/O 系統呼叫。

嵌入式Linux網路程式設計I/O,阻塞I/O模式,非阻塞I/O模式fcntl()/ioctl(),I/O select()/pselect()/poll(),訊號驅動I/O

文章目錄 1,I/O模型 2,阻塞I/O 模式 2.1,讀阻塞(以read函式為例) 2.2,寫阻塞 3,非阻塞模式I/O 3.1,非阻塞模式的實現(fcntl()函式、ioctl() 函式)

UNIX網路程式設計-I/O

目錄 Unix下可用的5種I/O模型 阻塞式I/O模型 非阻塞式I/O模型 I/O複用模型 訊號驅動式I/O模型 非同步I/O模型 各種I/O模型的比較 參考   Unix下可用的5種I/O模型 阻塞式I/O 非阻塞式I/O

Socket網路程式設計_之I/O

1. IO多路複用: 每一次網路通訊都是一個Socket的I/O流,對於伺服器而言,有兩種方法 1.傳統的多程序併發模型(每進來一個新的I/O流會分配一個新的程序管理。) 2.方法二就是I/O的多路複用

Linux網路程式設計---I/O之epoll

/* TCP伺服器 用法:./server port */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string

java併發程式設計之IO基礎入門之I/O技術

在I/O程式設計過程中,當需要同時處理多個客戶端接入請求時,可以利用多執行緒或者I/O多路複用技術進行處理。I/O多路複用技術通過把多個I/O的阻塞複用到同一個select的阻塞上,從而使得系統在單執行緒的情況下可以同時處理多個客戶端請求。與傳統的多執行緒/多程序模型比,

Java網路程式設計與NIO詳解2:JAVA NIO 一步步構建I/O的請求模型

微信公眾號【黃小斜】作者是螞蟻金服 JAVA 工程師,專注於 JAVA 後端技術棧:SpringBoot、SSM全家桶、MySQL、分散式、中介軟體、微服務,同時也懂點投資理財,堅持學習和寫作,相信終身學習的力量!關注公眾號後回覆”架構師“即可領取 Java基礎、進階、專案和架構師等免費學習資料,更有資料

I/O

I/O型別:     接下來我們將介紹幾種常見的I/O模型及其區別         阻塞I/O:blocking I/O(如果沒有資訊,則阻塞)       

【Linux】I/O

五種IO模型     阻塞IO(等待魚上鉤)         在核心將資料準備好之前,系統呼叫會一直等待,所有的套接字,預設是阻塞模式。         等待,拷貝資料到buf中,(等待的時間長)     非阻塞IO(定期檢視是否有魚上鉤)         如果核心還未將資料

I/O技術(multiplexing)

作者:知乎使用者 連結:https://www.zhihu.com/question/28594409/answer/52835876 來源:知乎 著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。 下面舉一個例子,模擬一個tcp伺服器處理30個客戶soc

I/O之select、poll、epoll

很早之前有寫過篇IO多路複用的文章:https://www.cnblogs.com/klcf0220/archive/2013/05/14/3077003.html 參考連結:https://segmentfault.com/a/1190000003063859 select,poll,epoll都是IO多路

IO基礎入門之I/O技術

在I/O程式設計過程中,當需要同時處理多個客戶端接入請求時,可以利用多執行緒或者I/O多路複用技術進行處理。I/O多路複用技術通過把多個I/O的阻塞複用到同一個select的阻塞上,從而使得系統在單執行緒的情況下可以同時處理多個客戶端請求。與傳統的多執行緒/多程序模型比,I

淺談網路I/O模型 select & poll & epoll

我們首先需要知道select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,

I/O之 epoll 系統呼叫

I/O多路複用除了之前我們提到的select和poll外,epoll 也可以檢查多個檔案描述符的就緒狀態,以達到I/O多路複用的目的。 epoll 系統呼叫是 Linux 系統專有的,在 Linux 核心 2.6 版本新增,epoll 的主要優點有: 當檢

Linux 下I/O總結

 select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件