加入收藏 | 设为首页 | 会员中心 | 我要投稿 南京站长网 (https://www.025zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 网站设计 > 教程 > 正文

TCP/IP网络编程之进程与间通信

发布时间:2019-11-09 02:25:41 所属栏目:教程 来源:Hu先生Linux后台开发
导读:副标题#e# 进程间通信基本概念 进程间通信意味着两个不同进程间可以交换数据,为了完成这一点,操作系统中应提供两个进程可以同时访问的内存空间。但我们知道,进程具有完全独立的内存结构,就连通过fork函数创建的子进程也不会和父进程共享内存,因此,进

编译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. { 
  6. int fds1[2], fds2[2]; 
  7. char str1[] = "Who are you?"; 
  8. char str2[] = "Thank you for your message"; 
  9. char buf[BUF_SIZE]; 
  10. pid_t pid; 
  11. pipe(fds1), pipe(fds2); 
  12. pid = fork(); 
  13. if (pid == 0) 
  14. { 
  15. write(fds1[1], str1, sizeof(str1)); 
  16. read(fds2[0], buf, BUF_SIZE); 
  17. printf("Child proc output: %s n", buf); 
  18. } 
  19. else 
  20. { 
  21. read(fds1[0], buf, BUF_SIZE); 
  22. printf("Parent proc output: %s n", buf); 
  23. write(fds2[1], str2, sizeof(str2)); 
  24. sleep(3); 
  25. } 
  26. return 0; 
  27. } 
  • 第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. { 
  14. int serv_sock, clnt_sock; 
  15. struct sockaddr_in serv_adr, clnt_adr; 
  16. int fds[2]; 
  17. pid_t pid; 
  18. struct sigaction act; 
  19. socklen_t adr_sz; 
  20. int str_len, state; 
  21. char buf[BUF_SIZE]; 
  22. if (argc != 2) 
  23. { 
  24. printf("Usage : %s <port>n", argv[0]); 
  25. exit(1); 
  26. } 
  27. act.sa_handler = read_childproc; 
  28. sigemptyset(&act.sa_mask); 
  29. act.sa_flags = 0; 
  30. state = sigaction(SIGCHLD, &act, 0); 
  31. serv_sock = socket(PF_INET, SOCK_STREAM, 0); 
  32. memset(&serv_adr, 0, sizeof(serv_adr)); 
  33. serv_adr.sin_family = AF_INET; 
  34. serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); 
  35. serv_adr.sin_port = htons(atoi(argv[1])); 
  36. if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1) 
  37. error_handling("bind() error"); 
  38. if (listen(serv_sock, 5) == -1) 
  39. error_handling("listen() error"); 
  40. pipe(fds); 
  41. pid = fork(); 
  42. if (pid == 0) 
  43. { 
  44. FILE *fp = fopen("echomsg.txt", "wt"); 
  45. char msgbuf[BUF_SIZE]; 
  46. int i, len; 
  47. for (i = 0; i < 10; i++) 
  48. { 
  49. len = read(fds[0], msgbuf, BUF_SIZE); 
  50. fwrite((void *)msgbuf, 1, len, fp); 
  51. } 
  52. fclose(fp); 
  53. return 0; 
  54. } 
  55. while (1) 
  56. { 
  57. adr_sz = sizeof(clnt_adr); 
  58. clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz); 
  59. if (clnt_sock == -1) 
  60. continue; 
  61. else 
  62. puts("new client connected..."); 
  63. pid = fork(); 
  64. if (pid == 0) 
  65. { 
  66. close(serv_sock); 
  67. while ((str_len = read(clnt_sock, buf, BUF_SIZE)) != 0) 
  68. { 
  69. write(clnt_sock, buf, str_len); 
  70. write(fds[1], buf, str_len); 
  71. } 
  72. close(clnt_sock); 
  73. puts("client disconnected..."); 
  74. return 0; 
  75. } 
  76. else 
  77. close(clnt_sock); 
  78. } 
  79. close(serv_sock); 
  80. return 0; 
  81. } 
  82. void read_childproc(int sig) 
  83. { 
  84. pid_t pid; 
  85. int status; 
  86. pid = waitpid(-1, &status, WNOHANG); 
  87. printf("removed proc id: %d n", pid); 
  88. } 
  89. void error_handling(char *message) 
  90. { 
  91. fputs(message, stderr); 
  92. fputc('n', stderr); 
  93. exit(1); 
  94. }  
  • 第47、48行:第47行创建管道,第48行创建负责保存文件的进程
  • 第49~62行:第49行创建的子进程运行区域,该区域从管道出口fds[0]读取数据并保存到文件中。另外,上述服务端并不终止运行,而是不断向客户端提供服务。因此,数据在文件中累计到一定程序即关闭文件,该过程通过第55行的循环完成
  • 第80行:第73行通过fork函数创建的所有子进程将复制第47行创建的管道的文件描述符,因此,可以通过管道入口fds[1]传递字符串信息

(编辑:南京站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读