美文网首页
进程间通信(IPC InterProcess Communica

进程间通信(IPC InterProcess Communica

作者: 锋芒不露大宝剑 | 来源:发表于2019-04-29 11:49 被阅读0次
    一: 管道 - 便捷
      1> 本质: 内核的缓冲区(伪文件)
            伪文件 - 不占用磁盘空间
      2> 特点:  
          1) 两部分:
                读端, 写端, 对应两个文件描述符
                数据写端流入, 读端流出
          2) 操作管道的进程被销毁之后, 管道自动被释放
          3) 管道默认是阻塞的
                读写都阻塞
      3> 原理:
          1) 内部实现方式: 队列
                环形队列
                特点: 先进先出
          2) 缓冲区大小:
                默认4k, 会适当做调整, 不会无限放大
      4> 管道的局限性
          1) 队列:
                数据只能读一次, 不能重复读取
          2) 半双工方式:
                单工:  遥控器(单向)
                半双工:  对讲机(可以双向传输, 但是只能以阻断方式, 同一时间只允许一方传输)
                      数据传输的方向是单向的
                双工:  电话(可以双方同时传输)
          3) 匿名管道:
                适用于有血缘关系的进程
      5> 创建匿名管道
          1) int pipe(int pipefd[2]);
                int pipefd[2] - 传出参数(out): 环形队列读端与写端的文件描述符
                pipefd[0] - 读端
                pipefd[1] - 写端
                返回值:
                  success: 0
                  faild: -1, perror();
                如果文件描述符表此时没有打开的文件, pipefd[0]=3  pipefd[1]=4;
      6>设置非阻塞管道
          1) 默认读写两端都阻塞
          2) 设置读端为非阻塞pipe(fd[]);
              1. 使用fcntl() - 变参函数
                  复制文件描述符 - dup
                  修改文件的属性 - open的时候对应的flag属性
              2. 设置方法
                   获取原来的flags
                   int flags = fcntl(fd[0], F_GETFL);
                   设置新的flags
                   flags |= O_NONBLOCK;
                   fcntl(fd[0], F_SETFL, falgs);
    
    二: 信号 - 系统开销小, 由内核产生并发送
      1> 特点:
          1)  简单
          2)  携带的信息量少
          3)  使用在某个特定的场景中
          4)  发送进程将信号发送给内核, 再由内核发送给目标进程
          5)  信号的优先级比较高, 进程收到信号之后, 暂停正在处理的事情, 优先处理信号, 处理完成之后再继续开始暂停的工作
          6)  可能会造成变量数据的污染, 信号的处理逻辑影响正常执行代码块的变量值
      2> 信号的状态:
          1)  产生
                  键盘触发: ctrl+c
                  命令:    kill命令
                  系统函数: kill函数
                  软条件:   定时器
                  硬条件:   段错误, 除0错误 等非法的操作
              会变为未决状态
          2)  未决状态 - 没有被处理的状态
                  转变为递达状态的行为:
                    1. 被忽略
                    2. 被捕捉
                    3. 执行了默认的动作
                  会变为递达状态 
          3)  递达状态 - 信号被处理了
      3> 处理方式:
    
      4> 信号的四要素:
    
      5> 通过man文档查看信号:
          1) man 7 signal
          2) The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
                SIGKILL(强制杀死) 和 SIGSTOP(强制暂停) 不能被捕捉, 阻塞 或者忽略
      6> 概念: 阻塞信号集, 未决信号集:
          1) 在pcb中(内核), 不能直接操作
          2) 阻塞信号集: 
                里面存放要屏蔽的信号
          3) 未决信号集: 如果信号被阻塞了, 该信号集会对阻塞的信号
                没有被处理的信号的集合
      7> 信号相关函数:
          1) kill -- 发送信号给指定进程
                函数原型: int kill(pid_t pid, int sig);
                      pid > 0: 发送信号给指定进程
                      pid = 0: 发送信号给 与调用kill函数进程属于同一进程组的所有进程
                      pid < -1: 取 |pid| 发给对应进程组
                      pid = -1: 发送给进程有权限发送的系统中所有进程
          2) raise -- 自己给自己发信号
                函数原型: int raise(pid_t pid, int sig);
                         返回值: 
          3) abort -- 给自己发送异常终止信号
                函数原型: void abort(void);
                         对应信号: SIGABRT(终止进程并产生core文件)
                         没有参数没有返回值, 永远不会调用失败
          4) 闹钟(定时器)
                1. alarm -- 设置定时器(每个进程只有一个定时器)
                          使用的时自然定时法
                              不受进程状态影响
                          函数原型: unsigned int alarm(unsigned int seconds);
                              参数: 秒
                              当时间到达之后, 函数发出一个信号量: SIGALRM
                              当定时器触发时  再次调用alarm(0), 传入参数0, 此时会将定时器取消
                              当定时器触发时  再次调用alarm(x), 传入参数x(x > 0), 此时会将定时器时间重置为x, 返回值是之前设置定时器将要触发的剩余时间
                          返回值: 距离上一次设置的触发时间, 还剩余多少秒
                2. setitimer -- 定时器, 并实现周期性定时    // <sys/time.h>
                          函数原型:
                              int setitimer(
                                  int which,   // 定时法则: 
                                               ITIMER_REAL(自然定时法, 到时间后发出SIGALRM信号), 
                                               ITIMER_VIRTUAL(只计算用户区代码运行的时间(区别于内核区), 到时间后发出SIGVTALRM信号), 
                                               ITIMER_PROF(计算用户+内核运行的时间, 不计算运行损耗. 到时间后发出SIGPROF信号)
                                  const struct itimerval* new_value, 
                                  struct itimerval* old_value // 一般NULL, 传出参数, 会写出上一次定时器的信息
                              );
                              struct itimerval {
                                  struct timeval it_interval,  // 定时器循环周期
                                  struct timeval it_value,     // 第一次触发定时器的时间
                              };
                              struct timeval {
                                  time_t tv_sec,               // seconds, 秒
                                  suseconds_t tv_usec,         // microseconds, 微秒
                              };
                          基础配置:
                              int main(int argc, char* argv[]) {
        
                                  // 设置定时器
                                  struct itimerval new_value;
                                  // 第一次触发的时间 tv_sec(秒) tv_usec(微秒) 同时设置 是相加的操作  周期 = 秒 + 微秒
                                      // 其中一个不设置就赋值为0, 如若不进行赋值, 其值为随机值
                                  new_value.it_value.tv_sec = 2;    // 秒
                                  new_value.it_value.tv_usec = 0;   // 微秒
                                  // 周期性定时
                                      // 其中一个不设置就赋值为0, 如若不进行赋值, 其值为随机值
                                  new_value.it_interval.tv_sec = 1;  // 每n秒发一次信号
                                  new_value.it_interval.tv_usec = 0; // 微秒
        
                                  // 倒计时两秒后周期性发送信号, 需要监听该信号, 否则进程会被杀死
                                  int ret = setitimer(ITIMER_REAL, &new_value, NULL);
                                  while(1) {
                                      printf("hello, itimer!\n");
                                      sleep(1);
                                  }
                                      return 0;
                              }
      8> 信号集 -- 信号集操作相关函数
          1) 概念
                1. 未决信号集;
                    没有被当前进程处理的信号
                1. 阻塞信号集;
                    将某个信号放到阻塞信号集, 这个信号就不会被进程处理
                    阻塞解决之后, 信号被处理(从未决到了递达状态)
          2) 自定义信号集
                 int sigemptyset(sigset_t* set);  // 将集合置空
                 int sigfillset(sigset_t* set);   // 将所有信号加入set集合
                 int sigaddset(sigset_t* set, int signo);
                      将signo信号加入到set集合
                 int sigdelset(sigset_t* set, int signo);
                      从set集合中移除signo信号
                 int sigismember(const sigset_t* set, int signo);
                      判断信号是否存在
          3) sigprocmask函数
                 屏蔽and接触信号屏蔽, 将自定义信号集设置给阻塞信号集
                 函数原型:
                      int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
                          how: SIG_BLOCK(阻塞, 将set中被添加(等于1)的信号设置到阻塞信号集中)
                               SIG_UNBLOCK(解除阻塞, 将set中被添加(等于1)的信号从内核阻塞信号集中解除阻塞)
                               SIG_SETMASK(将set覆盖到到内核的阻塞信号集上)
          4) sigpending -- 读取当前进程的未决信号集
                 函数原型: int sigpending(sigset_t* set);
                 参数: set -- 内核将未决信号集写入set
          4) demo
                // 手动屏蔽某些信号
                sigset_t pendset;
                sigemptyset(&pendset);  // 集合置空
                // 添加阻塞的信号
                sigaddset(&pendset, SIGINT);    // ctrl + c
                sigaddset(&pendset, SIGQUIT);   // ctrl + \ 3号信号
                sigaddset(&pendset, SIGKILL);
                // 设置给内核的阻塞信号集
                sigprocmask(SIG_BLOCK, &pendset, NULL);
                int i = 1;
                while(1) {
                    sigpending(&pendset);
                    // 每隔一秒读取一次内存的未决信号集(1-31号信号)
                    for (i = 1; i < 32; i++) {
                        // 一次判断每个信号
                        if(sigismember(&pendset, i)) {
                            printf("1 ");
                        } else {
                            printf("0 ");
                        }
                    }
                    printf("\n");
                    sleep(1);
                }
      8> 信号捕捉
          1) signal函数 -- 没有遵循poxy标准, 可能会因为操作系统的区别有所差异
                 typedef void(*sighandler_t)(int);
                 sighandler_t signal(int signum <要捕捉的信号>, sighandler_t handler <回调函数地址|函数名>);
             demo:
                 extern void touch_INT_SIG(int n);
                 main...
                   // 注册捕捉函数
                   signal(SIGINT, touch_INT_SIG);
                   while(1){}              
                 return 0;
                 void touch_INT_SIG(int n) {
                      printf("捕获到 %d 号信号\n", n);
                 }              
          2) sigaction函数 -- 不会有差异
                 函数原型
                    int sigaction(
                          int signum,   // 捕捉的信号
                          const struct sigaction* act,
                          struct sigaction* oldact
                    );
                    struct sigaction {
                          void     (*sa_handler)(int),  // 与signal(int, sighandler_t) 一样, 用于回调
                          void     (*sa_sigaction)(int, siginfo_t*, void*),  // 一般不用  
                          sigset_t   sa_mask,
                              // 作用: 再信号处理函数执行过程中, 临时的屏蔽掉某些信号, 放到sa_mask;(临时的, 函数执行结束后自动解除屏蔽)
                              // 如果没有使用的需求, 就进行清空操作: sigemptyset(sigset_t* set);                                                   
                          int      sa_flags,
                              // 回调函数sa_handler或sa_sigaction 
                                  // 如果使用了sa_handler, sa_flags一定赋值为0, 信号将阻塞的系统调用打断后自动重启使用SA_RESTART
                                  http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3772700&page=1                           
                          void     (*sa_restorer)(void);  // 被废弃
                    };                      
             demo:
                    extern void touch_sig_fun(int sig_id);
                    int main(int argc, char* argv[]) {
                        struct sigaction act;
                        act.sa_handler = touch_sig_fun;
                        act.sa_flags = 0;
                        sigemptyset(&act.sa_mask);
                        // 添加临时屏蔽
                        sigaddset(&act.sa_mask, SIGQUIT);   // 再回调函数睡眠过程中, 暂不响应SIGQUIT信号, 等待执行回调结束之后再处理SIGQUIT信号
                        sigaction(SIGINT, &act, NULL);
                        while(1) {}
                        return 0;
                    }
                    void touch_sig_fun(int sig_id) {
                        printf("touch %d signal\n", sig_id);
                        sleep(3);
                        printf("wake up\n");
                    }
    
    三: 共享映射区 - 不区分进程之间有无血缘关系
      1> FIFO (first in first out), 先进先出(队列)
          1) 特点
              有名管道
              在磁盘上有这样一个文件 ls -l -> p(p类型的文件)
              伪文件, 在磁盘上的大小永远为0
              数据存储在内核中对应的一个缓冲区
              半双工的通信方式
          2) 使用场景
              没有血缘关系的进程间通信
          3) 创建方式
              1. 命令: mkfifo 管道名
              2. 函数: mkfifo(char* pathname, mode_t mode); man文档第三章
          4) 进程间通信(伪代码)  
              a. fifo文件 --- afifo
              1. 两个不相干的进程 A(a.c), B(b.c);
              2. a.c --> read
                    int fd = open(afifo, O_RDONLY);
                    read(fd , buf, sizeof(buf));
                    close(fd);
              b. b.c --- write
              1. int fd1 = open(afifo, O_WRONLY);
              write(fd1, "hello, world", 11);
              close(fd1);
            5) 示例(写文件)
              int main(int argc, char* argv[]) {
                  int ret = access("./1fifo", F_OK);
                  if (ret == -1) {
                      ret = mkfifo("./1fifo", 0664);
                      if (ret == -1) {
                          perror("create fifo error: ");
                          exit(1);
                      }
                  }
                  int fd = open("./1fifo", O_RDONLY);
                  if(fd == -1) {
                      perror("open fifo error: ");
                      exit(1);
                  }
                  char buff[1024] = {0};
                  while(1) {
                      memset(buff, 0, 1024);
                      read(fd, buff, 1024);
                      if(strncmp(buff, "bye", 3) == 0) {
                          break;
                      }
                      printf("buff: %s\n", buff);
                  }
                  close(fd);
                  printf("\n");
                      return 0;
              }
      2> mmap - 创建内存映射 - <sys/mman.h>
          1) 作用: 将磁盘文件的数据映射到内存中, 用户通过修改内存就能修改磁盘文件
          2) 函数原型: 
              void* mmap {
                  void* adrr,     // 映射区首地址, 传入NULL 由系统指定, 返回值就是系统指定的内存地址
                  size_t length,  // 映射区的大小 -- 4k的整数倍, 不能为0, 一般对剑多么大 length就多大
                  int prot,       // 映射区权限 
                      -- 对映射区的操作权限: PROT_READ(读, 必须要有)
                      -- PROT_WRITE(写, 可有可无)
                  int flags,      // 标志位参数 
                      -- MAP_SHARED(有PROT_WRITE权限, 修改了内存数据会同步到磁盘)
                      -- MAP_PRIVATE(有PROT_WRITE权限, 修改了内存数据不会同步到磁盘)
                  int fd,         // 文件描述符 -- 内存映射区所对应文件的描述符fd
                      -- 如何得到的: open();
                  off_t offset    // 映射文件的偏移量
                      -- 映射时候 文件指针的偏移量, 必须是4k的整数倍, 一般情况用0
              }
          3) 返回值: success -- 系统指定的映射内存首地址, faild -- MAP_FAILED ((void*) -1) -> perror();
          4) munmap - 释放内存映射区
              1. 函数原型: int munmap(void* addr, size_t length); -- 调用失败返回 -1, perror()
                  addr -- mmap的返回值;
                  length -- mmap的第二个参数
              2. 思考问题:
                  1$: 对mmap的返回值(ptr)做++操作(ptr++), munmap是否能够成功
                        不能释放成功, 可以重新定义一个指针指向ptr;
                  2$: 如果open时O_RDONLY, mmap时prot参数指定PROT_READ|PROT_WRITE会怎样
                        报错: Permission denied
                  3$: 如果文件偏移量为1000会怎样
                        无效的参数, 必须是4096的整数倍
                  4$: 如果不检测mmap的返回值会怎样
                        不会怎么样, 导致出了错误不知道, 无法回收
                  5$: mmap什么情况下会调用失败
                        第二个参数为0
                        第三个参数没有PROT_READ权限, 或大于fd的打开权限
                        最后一个参数offset不是4096的整数倍(必须是4k的整数倍)
                  6$: 可以open的时候O_CREATE一个新文件来创建映射区吗
                        不能, 创建出来的文件大小为0, 或者对创建出来的文件进行拓展(lseek(fd, 0, SEEL_END)再写操作 | truncate(path, length))
                  7$: mmap后关闭文件描述符, 对mmap映射有没有影响
                        没有影响
                  8$: 对ptr越界操作会怎么样
                        段错误(操作已经被占用的内存)
                        随机数(操作未被占用的内存)
            5) demo
                int main() {
                    // 打开文件
                    int fd = open("file", O_RDWR);
                    if(fd == -1) {
                        perror("open file error: ");
                        exit(1);
                    }
                    int len = lseek(fd, 0, SEEK_END);
                    // 创建内存映射区
                    void* ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
                    if(ptr == MAP_FAILED) {
                        perror("mmap error: ");
                        exit(2);
                    }
                    // 打印映射区中内容
                    printf("%s", (char*)ptr);
                    
                    // 释放映射区
                    munmap(ptr, len);
                    close(fd);
                    printf("\n");
                    return 0;
                }
            6)父子进程永远共享的东西
                1. 文件描述符
                2. 内存映射区(父进程创建的内存映射区, 子进程也能找到)
                3. 父子进程共享映射区
                int main() {
                    // 打开文件
                    int fd = open("file", O_RDWR);
                    if(fd == -1) {
                        perror("open file error: ");
                        exit(1);
                    }
                    int len = lseek(fd, 0, SEEK_END);
                    // 创建内存映射区
                    void* ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
                    if(ptr == MAP_FAILED) {
                        perror("mmap error: ");
                        exit(2);
                    }
                    pid_t pid = fork();
                    if(pid == -1) {
                          perror("fork error: ");
                          exit(1);
                    }
                    // 父子进程间的通信, 都通过ptr进行通信
                    if(pid > 0) {
                          // 写操作
                          strcpy((char*)ptr, “Hello World”);
                          // 回收子进程
                          wait(NULL);
                    } else if (pid == 0) {
                          // 读数据
                          printf("%s\n", (char*)ptr);
                    }
                    
                    // 释放映射区
                    munmap(ptr, len);
                    close(fd);
                    printf("\n");
                    return 0;
                }
            7) 匿名映射区(父子进程, 共享文件描述符&内存映射区)
                1. 代码
                int main() {
                    // 创建匿名内存映射区
                    int size = 4096;
                    void* ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0); 
                                  // fd: -1 不用文件
                                  // MAP: MAP_SHARED | MAP_ANON(或者MAP_ANONYMOUS)
                    if(ptr == MAP_FAILED) {
                        perror("mmap error: ");
                        exit(2);
                    }
                    pid_t pid = fork();
                    if(pid == -1) {
                          perror("fork error: ");
                          exit(1);
                    }
                    // 父子进程间的通信, 都通过ptr进行通信
                    if(pid > 0) {
                          // 写操作
                          strcpy((char*)ptr, “Hello World”);
                          // 回收子进程
                          wait(NULL);
                    } else if (pid == 0) {
                          // 读数据
                          printf("%s\n", (char*)ptr);
                    }
                    
                    // 释放映射区
                    munmap(ptr, len);
                    close(fd);
                    printf("\n");
                    return 0;
                }
            7) 没有血缘关系的进程间通信(不能使用匿名映射, 只能借助磁盘文件创建映射 hello)
                不阻塞
                1. 伪代码
                    a.c
                      int fd = open("hello", O_RDWR);
                      void* ptr = mmap(NULL, ,,,, fd, 0);
                      对映射区进行读写操作
                          对ptr进行操作
                    b.c
                      int fd = open("hello", O_RDWR);
                      void* ptr = mmap(NULL, ,,,, fd, 0);
                      对映射区进行读写操作
                          对ptr进行操作
                1. demo代码
                    read.c
                      int fd = open("tmp", O_RDWR|O_CREAT, 0664);
                      ftruncate(fd, 4096);  /// 使用ftruncate传入文件描述符给文件扩容
                      int len = lseek(fd, 0, SEEK_END);
                      void* ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
                      if(ptr == MAP_FAILED) {
                          perror("mmap: ");
                          exit(1);
                      }
                      while(1) {
                          sleep(1);
                          printf("%s\n", (char*)ptr + 1024);  // 不从首地址读
                      }
                      int ret = munmap(ptr, len);
                      if(ret == -1) {
                          perror("munmap: ");
                          exit(1);
                      }
    
                    write.c
                      int fd = open("tmp", O_RDWR|O_CREAT, 0664);
                      ftruncate(fd, 4096);  /// 使用ftruncate传入文件描述符给文件扩容
                      int len = lseek(fd, 0, SEEK_END);
                      void* ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
                      if(ptr == MAP_FAILED) {
                          perror("mmap: ");
                          exit(1);
                      }
                      while(1) {
                          char* p = (char*)ptr;
                          p+=1024;
                          strcpy(p, "hello read process\n");
                          sleep(2);
                      }
                      int ret = munmap(ptr, len);
                      if(ret == -1) {
                          perror("munmap: ");
                          exit(1);
                      }
    
    四: 本地套接字domain - 稳定
    
    server.c
    /*************************************************************************
        > File Name: server.c
        > Author: lijin
        > Mail: 
        > Created Time: Tue 28 May 2019 04:08:18 AM PDT
     ************************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <time.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/wait.h>
    #include <sys/time.h>
    #include <pthread.h>
    #include <semaphore.h>
    #include <sys/socket.h>
    #include <ctype.h>
    #include <arpa/inet.h>
    #include <sys/un.h>
    #include <stddef.h>
    
    #include "wrap.h"
    
    #define SERV_ADDR "serv.socket"
    
    int main(int argc, char* argv[]) {
        int lfd, cfd, len, size, i;
        struct sockaddr_un serv_addr, cli_addr;
        char buff[4096];
        
        lfd = Socket(AF_UNIX, SOCK_STREAM, 0);
        
        bzero(&serv_addr, sizeof(serv_addr));
        serv_addr.sun_family = AF_UNIX;
        strcpy(serv_addr.sun_path, SERV_ADDR);
        
        len = offsetof(struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path);  /** serv_addr total len*/
        unlink(SERV_ADDR);                              /** 确保bind之前serv_socket文件不存在, bind会创建该文件 */
        Bind(lfd, (struct sockaddr*)&serv_addr, len);   /** 参3不能是sizeof(serv_addr) */
        Listen(lfd, 128);
        printf("Accept...\n");
        while (1) {
            len = sizeof(cli_addr);
            cfd = Accept(lfd, (struct sockaddr*)&cli_addr, (socklen_t*)&len);
            len -= offsetof(struct sockaddr_un, sun_path);      /** 得到文件名的长度 */
            cli_addr.sun_path[len] = '\0';                      /** 确保打印时, 没有乱码出现 */
            
            printf("client bind filename %s\n", cli_addr.sun_path);
            while((size = read(cfd, buff, sizeof(buff))) > 0) {
                for (i = 0; i < size; i++) {
                    buff[i] = toupper(buff[i]);
                }
                write(cfd, buff, size);
            }
            close(cfd);
        }
    
        return 0;
    }
    
    
    client.c
    /*************************************************************************
        > File Name: client.c
        > Author: lijin
        > Mail: 
        > Created Time: Tue 28 May 2019 04:08:33 AM PDT
     ************************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <time.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/wait.h>
    #include <sys/time.h>
    #include <pthread.h>
    #include <semaphore.h>
    #include <sys/socket.h>
    #include <ctype.h>
    #include <arpa/inet.h>
    #include <sys/un.h>
    #include <stddef.h>
    
    #include "wrap.h"
    
    #define SERV_ADDR "serv.socket"
    #define CLIE_ADDR "clie.socket"
    
    int main(int argc, char* argv[]) {
        int cfd, len;
        struct sockaddr_un serv_addr, cli_addr;
        char buff[4096];
        
        cfd = Socket(AF_UNIX, SOCK_STREAM, 0);
        bzero(&cli_addr, sizeof(cli_addr));
        cli_addr.sun_family = AF_UNIX;
        strcpy(cli_addr.sun_path, CLIE_ADDR);
        
        len = offsetof(struct sockaddr_un, sun_path) + strlen(cli_addr.sun_path);   /** 计算客户端地址结构有效长度 */
        
        unlink(CLIE_ADDR);                      
        Bind(cfd, (struct sockaddr*)&cli_addr, len);                /** 客户端也需要bind, 不能依赖自动绑定 */
        
        bzero(&serv_addr, sizeof(serv_addr));                       /** 构造server 地址 */
        serv_addr.sun_family = AF_UNIX;
        strcpy(serv_addr.sun_path, SERV_ADDR);
        
        len = offsetof(struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path);  /** 计算服务器端地址结构有效长度 */
        Connect(cfd, (struct sockaddr*)&serv_addr, len);
        while(fgets(buff, sizeof(buff), stdin) != NULL) {
            write(cfd, buff, strlen(buff));
            len = read(cfd, buff, sizeof(buff));
            write(STDOUT_FILENO, buff, len);
        }
        close(cfd);
        return 0;
    }
    
    

    相关文章

      网友评论

          本文标题:进程间通信(IPC InterProcess Communica

          本文链接:https://www.haomeiwen.com/subject/bctqnqtx.html