成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

TCP/IP網絡編程之進程與進程間通信

網絡 通信技術
進程間通信意味著兩個不同進程間可以交換數據,為了完成這一點,操作系統中應提供兩個進程可以同時訪問的內存空間。但我們知道,進程具有完全獨立的內存結構,就連通過fork函數創建的子進程也不會和父進程共享內存,因此,進程間通信只能通過其他特殊方法完成。

 [[281737]]

進程間通信基本概念

進程間通信意味著兩個不同進程間可以交換數據,為了完成這一點,操作系統中應提供兩個進程可以同時訪問的內存空間。但我們知道,進程具有完全獨立的內存結構,就連通過fork函數創建的子進程也不會和父進程共享內存,因此,進程間通信只能通過其他特殊方法完成。

基于管道實現進程間通信

圖1-1表示基于管道(PIPE)的進程間通信結構模型

 

TCP/IP網絡編程之進程與間通信

 

圖1-1 基于管道的進程間通信模型

從圖1-1可以看到,為了完成進程間通信,需要創建管道。管道并非屬于進程資源,而是和套接字一樣,屬于操作系統資源(也就不是fork函數的復制對象)。下面介紹創建管道函數

  1. #include 
  2. int pipe (int filedes[2]);//成功時返回0,失敗時返回-1 
  • filedes[0]:通過管道接收數據時使用的文件描述符,即管道出口
  • filedes[1]:通過管道傳輸數據時使用的文件描述符,即管道入口

以長度為2的int數組地址值作為參數調用上述函數時,數組中存有兩個文件描述符,它們將被用作管道的出口和入口。父進程調用該函數時將創建管道,同時獲取對應于出入口的文件描述符,此時父進程可以讀寫同一管道。但父進程的目的是與子進程進行數據交換,因此需要將入口和出口中的一個文件描述符傳遞給子進程,如何完成傳遞呢?答案還是調用fork函數。

  1. pipe1.c 
  2. #include <stdio.h> 
  3. #include <unistd.h> 
  4. #define BUF_SIZE 30 
  5. int main(int argc, char *argv[]) 
  6. int fds[2]; 
  7. char str[] = "Who are you?"
  8. char buf[BUF_SIZE]; 
  9. pid_t pid; 
  10. pipe(fds); 
  11. pid = fork(); 
  12. if (pid == 0) 
  13. write(fds[1], str, sizeof(str)); 
  14. else 
  15. read(fds[0], buf, BUF_SIZE); 
  16. puts(buf); 
  17. return 0; 
  • 第12行:調用pipe函數創建管道,fds數組中保存用于I/O的文件描述符
  • 第13行:接著調用fork函數,子進程將同時擁有通過12行函數調用獲取的兩個文件描述符。注意!復制的并非管道,而是用于管道I/O的文件描述符。至此,父子進程同時擁有I/O文件描述符
  • 第16、20行:子進程通過第16行代碼向管道傳遞字符串,父進程通過第20行代碼從管道接收字符串

編譯pipe1.c并運行

  1. # gcc pipe1.c -o pipe1 
  2. # ./pipe1 
  3. Who are you? 

上述示例中的通信方法及路徑如圖1-2所示,重點在于,父子進程都可以訪問管道的I/O路徑,但子進程僅用輸入路徑,父進程僅用輸出路徑

 

TCP/IP網絡編程之進程與間通信

 

圖1-2 示例pipe1.c的通信路徑

以上就是管道的基本原理及通信方法,應用管道時還有一部分內容需要注意,通過雙向通信示例進一步說明

通過管道進行進程間雙向通信

下面創建兩個進程通過一個管道進行雙向數據交換的示例,其通信方式如圖1-3所示

 

TCP/IP網絡編程之進程與間通信

 

圖1-3 雙向通信模型1

從圖1-3可以看出,通過一個管道可以進行雙向通信,但采用這種模型需格外小心,先給出示例,稍后再討論。

pipe2.c

  1. #include <stdio.h> 
  2. #include <unistd.h> 
  3. #define BUF_SIZE 30 
  4. int main(int argc, char *argv[]) 
  5. int fds[2]; 
  6. char str1[] = "Who are you?"
  7. char str2[] = "Thank you for your message"
  8. char buf[BUF_SIZE]; 
  9. pid_t pid; 
  10. pipe(fds); 
  11. pid = fork(); 
  12. if (pid == 0) 
  13. write(fds[1], str1, sizeof(str1)); 
  14. sleep(2); 
  15. read(fds[0], buf, BUF_SIZE); 
  16. printf("Child proc output: %s \n", buf); 
  17. else 
  18. read(fds[0], buf, BUF_SIZE); 
  19. printf("Parent proc output: %s \n", buf); 
  20. write(fds[1], str2, sizeof(str2)); 
  21. sleep(3); 
  22. return 0; 
  • 第17~20行:子進程運行區域,通過第17行行傳輸數據,通過第19行接收數據。第18行的sleep函數至關重要,這一點稍后再討論
  • 第24~26行:父進程的運行區域,通過第24行接收數據,這是為了接收第17行子進程傳輸的數據。另外通過第26行傳輸數據,這些數據將被第19行的子進程接收
  • 第27行:父進程先終止時會彈出命令提示符,這時子進程仍然在工作,故不會產生問題。這條語句主要是為了防止子進程終止前彈出命令提示符(故可刪除)

編譯pipe2.c并運行

  1. # gcc pipe2.c -o pipe2 
  2. # ./pipe2 
  3. Parent proc output: Who are you? 
  4. Child proc output: Thank you for your message 

運行結果和我們設想一致,不過如果嘗試將18行的代碼注釋后再運行,雖然這行代碼只將運行時間延遲了兩秒,但一旦注釋便會引發錯誤,是什么原因呢?

向管道傳遞數據時,先讀的進程會把數據取走。簡言之,數據進入管道后成為無主數據,也就是通過read函數先讀取數據的進程將得到數據,即使該進程將數據傳到了管道。因此,注釋第18行將產生問題,在第19行,子進程將讀回自己在第17行向管道發送的數據。結果父進程調用read函數后將無限期等待數據進入管道。

從上述示例可以看到,只用一個管道進行雙向通信并非易事,為了簡化在進行雙向通信時,既然一個管道很難完成的任務,不如就讓兩個管道來一起完成?因此創建兩個管道,各自負責不同的數據流動即可。其過程如圖1-4所示

 

TCP/IP網絡編程之進程與間通信

 

圖1-4 雙向通信模型2

由圖1-4可知,使用兩個管道可以解決單單通過一個管道來進行雙向通信的麻煩,下面采用上述模型來改進pipe2.c。

pipe3.c

  1. #include <stdio.h> 
  2. #include <unistd.h> 
  3. #define BUF_SIZE 30 
  4. int main(int argc, char *argv[]) 
  5. int fds1[2], fds2[2]; 
  6. char str1[] = "Who are you?"
  7. char str2[] = "Thank you for your message"
  8. char buf[BUF_SIZE]; 
  9. pid_t pid; 
  10. pipe(fds1), pipe(fds2); 
  11. pid = fork(); 
  12. if (pid == 0) 
  13. write(fds1[1], str1, sizeof(str1)); 
  14. read(fds2[0], buf, BUF_SIZE); 
  15. printf("Child proc output: %s \n", buf); 
  16. else 
  17. read(fds1[0], buf, BUF_SIZE); 
  18. printf("Parent proc output: %s \n", buf); 
  19. write(fds2[1], str2, sizeof(str2)); 
  20. sleep(3); 
  21. return 0; 
  22. }   
  • 第13行:創建兩個管道
  • 第17、33行:子進程可以通過數組fds1指向的管道向父進程傳輸數據
  • 第18、25行:父進程可以通過數組fds2指向的管道向子進程傳輸數據
  • 第26行:沒有太大的意義,只是為了延遲父進程終止的插入的代碼

編譯pipe3.c并運行

  1. # gcc pipe3.c -o pipe3 
  2. # ./pipe3 
  3. Parent proc output: Who are you? 
  4. Child proc output: Thank you for your message 

運用進程間通信

上一節學習了基于管道的進程間通信方法,接下來將其運用到網絡代碼中。如前所述,進程間通信與創建服務端并沒有直接關聯,但有助于理解操作系統。

保存消息的回聲服務端

擴展TCP/IP網絡編程之多進程服務端(二)這一章的echo_mpserv.c,添加將回聲客戶端傳輸的字符串按序保存到文件中。我們可以將這個任務交給另外的進程,換言之,另行創建進程,從向客戶端服務的進程字符串信息。當然,該過程需要創建用于接收數據的管道。

下面給出示例,該示例可以與任意回聲客戶端配合運行,我們將用之前介紹過的echo_mpserv.c。

echo_storeserv.c

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <string.h> 
  4. #include <unistd.h> 
  5. #include <signal.h> 
  6. #include <sys/wait.h> 
  7. #include <arpa/inet.h> 
  8. #include <sys/socket.h> 
  9. #define BUF_SIZE 100 
  10. void error_handling(char *message); 
  11. void read_childproc(int sig); 
  12. int main(int argc, char *argv[]) 
  13. int serv_sock, clnt_sock; 
  14. struct sockaddr_in serv_adr, clnt_adr; 
  15. int fds[2]; 
  16. pid_t pid; 
  17. struct sigaction act; 
  18. socklen_t adr_sz; 
  19. int str_len, state; 
  20. char buf[BUF_SIZE]; 
  21. if (argc != 2) 
  22. printf("Usage : %s <port>\n", argv[0]); 
  23. exit(1); 
  24. act.sa_handler = read_childproc; 
  25. sigemptyset(&act.sa_mask); 
  26. act.sa_flags = 0; 
  27. state = sigaction(SIGCHLD, &act, 0); 
  28. serv_sock = socket(PF_INET, SOCK_STREAM, 0); 
  29. memset(&serv_adr, 0, sizeof(serv_adr)); 
  30. serv_adr.sin_family = AF_INET; 
  31. serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); 
  32. serv_adr.sin_port = htons(atoi(argv[1])); 
  33. if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1) 
  34. error_handling("bind() error"); 
  35. if (listen(serv_sock, 5) == -1) 
  36. error_handling("listen() error"); 
  37. pipe(fds); 
  38. pid = fork(); 
  39. if (pid == 0) 
  40. FILE *fp = fopen("echomsg.txt""wt"); 
  41. char msgbuf[BUF_SIZE]; 
  42. int i, len; 
  43. for (i = 0; i < 10; i++) 
  44. len = read(fds[0], msgbuf, BUF_SIZE); 
  45. fwrite((void *)msgbuf, 1, len, fp); 
  46. fclose(fp); 
  47. return 0; 
  48. while (1) 
  49. adr_sz = sizeof(clnt_adr); 
  50. clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz); 
  51. if (clnt_sock == -1) 
  52. continue
  53. else 
  54. puts("new client connected..."); 
  55. pid = fork(); 
  56. if (pid == 0) 
  57. close(serv_sock); 
  58. while ((str_len = read(clnt_sock, buf, BUF_SIZE)) != 0) 
  59. write(clnt_sock, buf, str_len); 
  60. write(fds[1], buf, str_len); 
  61. close(clnt_sock); 
  62. puts("client disconnected..."); 
  63. return 0; 
  64. else 
  65. close(clnt_sock); 
  66. close(serv_sock); 
  67. return 0; 
  68. void read_childproc(int sig) 
  69. pid_t pid; 
  70. int status; 
  71. pid = waitpid(-1, &status, WNOHANG); 
  72. printf("removed proc id: %d \n", pid); 
  73. void error_handling(char *message) 
  74. fputs(message, stderr); 
  75. fputc('\n', stderr); 
  76. exit(1); 
  77. }    
  • 第47、48行:第47行創建管道,第48行創建負責保存文件的進程
  • 第49~62行:第49行創建的子進程運行區域,該區域從管道出口fds[0]讀取數據并保存到文件中。另外,上述服務端并不終止運行,而是不斷向客戶端提供服務。因此,數據在文件中累計到一定程序即關閉文件,該過程通過第55行的循環完成
  • 第80行:第73行通過fork函數創建的所有子進程將復制第47行創建的管道的文件描述符,因此,可以通過管道入口fds[1]傳遞字符串信息

編譯echo_storeserv.c并運行

  1. # gcc echo_storeserv.c -o echo_storeserv 
  2. # ./echo_storeserv 8500 
  3. new client connected... 
  4. new client connected... 
  5. client disconnected... 
  6. removed proc id: 8647 
  7. removed proc id: 8633 
  8. client disconnected... 
  9. removed proc id: 8644 

運行結果echo_mpclient ONE:

  1. # ./echo_mpclient 127.0.0.1 8500 
  2. Hello world! 
  3. Message from server: Hello world! 
  4. Hello Amy! 
  5. Message from server: Hello Amy! 
  6. Hello Tom! 
  7. Message from server: Hello Tom! 
  8. Hello Jack! 
  9. Message from server: Hello Jack! 
  10. Hello Rose! 
  11. Message from server: Hello Rose! 
  12. q   

運行結果echo_mpclient TWO:

  1. # ./echo_mpclient 127.0.0.1 8500 
  2. Hello Java! 
  3. Message from server: Hello Java! 
  4. Hello Python! 
  5. Message from server: Hello Python! 
  6. Hello Golang! 
  7. Message from server: Hello Golang! 
  8. Hello Spring! 
  9. Message from server: Hello Spring! 
  10. Hello Flask! 
  11. Message from server: Hello Flask! 

打印echomsg.txt文件

  1. # cat echomsg.txt 
  2. Hello world! 
  3. Hello Amy! 
  4. Hello Java! 
  5. Hello Python! 
  6. Hello Tom! 
  7. Hello Jack! 
  8. Hello Rose! 
  9. Hello Golang! 
  10. Hello Spring! 
  11. Hello Flask! 

如上運行結果所示,啟動多個客戶端向服務端傳輸數據時,文件中累計一定數量的字符串后(共調用十次fwrite函數),可以打開echomsg.txt存入字符串。

 

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2017-08-06 00:05:18

進程通信開發

2015-04-24 09:48:59

TCPsocketsocket編程

2021-01-22 10:58:16

網絡安全進程間碼如

2010-01-05 10:00:48

Linux進程間通信

2011-06-22 17:09:50

QT 進程 通信

2020-11-04 07:17:42

Nodejs通信進程

2020-11-18 09:06:04

Python

2018-01-12 14:35:00

Linux進程共享內存

2013-03-28 13:14:45

AIDL進程間通信Android使用AI

2017-06-19 13:36:12

Linux進程消息隊列

2021-02-14 21:05:05

通信消息系統

2011-08-08 10:02:55

iPhone開發 進程 通信

2024-01-05 08:41:31

進程間通信IPC異步通信

2020-09-22 07:35:06

Linux線程進程

2018-05-30 13:58:02

Linux進程通信

2011-06-24 14:01:34

Qt QCOP 協議

2019-05-08 11:10:05

Linux進程語言

2017-01-10 13:39:57

Python線程池進程池

2020-04-29 15:10:16

Linux命令進程

2019-09-18 20:07:06

AndroidTCP協議
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区视频在线 | 午夜欧美一区二区三区在线播放 | 五月激情六月婷婷 | av国产精品毛片一区二区小说 | 免费观看av| 欧美国产精品久久久 | 欧美13videosex性极品 | 国产精品久久亚洲7777 | 日本特黄a级高清免费大片 特黄色一级毛片 | 日韩一区二区三区精品 | 久久久国产一区二区三区 | 91成人在线 | 久久久久久久久毛片 | 91在线电影 | 日日摸夜夜爽人人添av | 欧美日韩高清一区 | 久操伊人 | 色站综合 | 在线观看成人精品 | 国产高清精品一区 | 亚洲激情在线观看 | 国产精品自产av一区二区三区 | 亚洲国产一区视频 | 国产成人综合在线 | 欧洲视频一区二区 | 最新中文字幕久久 | 精品一区二区三区免费视频 | 男人的天堂在线视频 | 国产成人啪免费观看软件 | 性色av一区二区三区 | 看羞羞视频 | h在线看 | 成人日韩av | 中文字幕第十一页 | 激情免费视频 | 精品一级| 中日韩av | 国产精品久久久久久久久久 | 欧美一区二区三区,视频 | 国内精品久久久久久影视8 最新黄色在线观看 | 黄视频网址 |