Android Framework实战开发视频--跨进程通信之Unix Socket通信

Posted Android高级知识分享官

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Framework实战开发视频--跨进程通信之Unix Socket通信相关的知识,希望对你有一定的参考价值。

android Framework实战开发视频–跨进程通信之Unix Socket

1 专门用于跨进程通信的Unix Socket

做过java相关app开发肯定学过socket通信等一些小程序来实现局域网通信,学会了通过ip地址来进行一种网络通信,但这种通信一般用于跨设备访问场景。而我们如果在同一个设备上,只是进程与进程之间的一种通信,那就完全没有必要使用这种网络通信方式,因为明显感觉不合适,进程间本身处于一个设备上,为啥还要通过ip网络,网卡走一圈再回来。所以针对这个问题就出现了本节课要讲的Unix Domain Socket通信。

UNIX Domain SOCKET 是在Socket架构上发展起来的用于同一台主机的进程间通讯(IPC)。它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序列号应答等。只是将应用层数据从一个进程拷贝到另一个进程。UNIX Domain SOCKET有SOKCET_DGRAM和SOCKET_STREAM两种模式,类似于UDP和TCP,但是面向消息的UNIX socket也是可靠的,消息既不会丢失也不会顺序错乱。

2 与正常网络socket的使用差异

因为默认已经对我们的网络socket有一定了解,没有了解的可以看我上一个blog,这里我们学习Unix Socket时候就只需要重点掌握一些它们的差异既可以。
最大差异看一下socket方法:

int socket(int protofamily, int type, int protocol);

//protofamily:即协议域,又称为协议族(family)。这里我们Unix Socket通信情况下选择AF_UNIX,协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定socket类型。常用的socket类型有,SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)。

int bind(int sockfd, const struct sockaddr addr, socklen_t addrlen);

这里的bind方法里面sockaddr 在上一节讲解网络通信时候时候用的是sockaddr_in结构体,传入最主要是端口和ip,这里unix socket对应结构体则是sockaddr_un:

struct sockaddr_un
{
  uint8_t sun_len;
  sa_family_t sun_family; /* AF_LOCAL */
  char sun_path[104]; /* null-terminated pathname */
};

sun_path主要传入一个绝对路径就可以,因为不需要ip进行网络通信。
其他部分基本和网络socket一样
通信流程图:

3 实战Unix Socket通信

客户端代码:

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#define MAXLINE 80

char *client_path = "client-socket";
char *server_path = "server-socket";

int main() {
        struct  sockaddr_un cliun, serun;
        int len;
        char buf[100];
        int sockfd, n;

        if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
                perror("client socket error");
                exit(1);
        }

    memset(&serun, 0, sizeof(serun));
    serun.sun_family = AF_UNIX;
    strncpy(serun.sun_path,server_path ,
                   sizeof(serun.sun_path) - 1);
    if (connect(sockfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0){
        perror("connect error");
        exit(1);
    }
    printf("please input send char:");
    while(fgets(buf, MAXLINE, stdin) != NULL) {
         write(sockfd, buf, strlen(buf));
         n = read(sockfd, buf, MAXLINE);
         if ( n < 0 ) {
            printf("the other side has been closed.\\n");
         }else {
            printf("received from server: %s \\n",buf);
         }
         printf("please input send char:");
    }
    close(sockfd);
    return 0;
}


服务端代码:

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

#define MAXLINE 80

char *socket_path = "server-socket";

int main()
{
    struct sockaddr_un serun, cliun;
    socklen_t cliun_len;
    int listenfd, connfd, size;
    char buf[MAXLINE];
    int i, n;

    if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        perror("socket error");
        exit(1);
    }

    memset(&serun, 0, sizeof(serun));
    serun.sun_family = AF_UNIX;
    strncpy(serun.sun_path,socket_path ,
                   sizeof(serun.sun_path) - 1);
    unlink(socket_path);//这个相当于把之前的地址要移除,不然上一个server没有结束,移除会报错already in use
    if (bind(listenfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0) {
        perror("bind error");
        exit(1);
    }
    printf("UNIX domain socket bound\\n");

    if (listen(listenfd, 20) < 0) {
        perror("listen error");
        exit(1);
    }
    printf("Accepting connections ...\\n");

    while(1) {
        cliun_len = sizeof(cliun);
        if ((connfd = accept(listenfd, (struct sockaddr *)&cliun, &cliun_len)) < 0){
            perror("accept error");
            continue;
        }
        printf("new client connect to server,client sockaddr === %s \\n",((struct sockaddr *)&cliun)->sa_data);
        while(1) {
            memset(buf,0,sizeof(buf));
            n = read(connfd, buf, sizeof(buf));
            if (n < 0) {
                perror("read error");
                break;
            } else if(n == 0) {
                printf("EOF\\n");
                break;
            }

            printf("received: %s\\n", buf);

            for(i = 0; i < n; i++) {
                buf[i] = toupper(buf[i]);
            }
            write(connfd, buf, n);
        }
        close(connfd);
    }
    close(listenfd);
    return 0;
}

结果截图:
服务先启动,然后客户端后启动,然后输入任何一个字符“hello”会立即受到服务端回复的大小字母“HELLO”

以上是关于Android Framework实战开发视频--跨进程通信之Unix Socket通信的主要内容,如果未能解决你的问题,请参考以下文章

Android Framework实战开发视频--跨进程通信之Socket通信

Android Framework实战开发视频--跨进程通信之Socket通信

Android Framework实战开发视频--跨进程通信之Unix Socket通信

Android Framework实战开发视频--跨进程通信之Unix Socket通信

Android Framework实战视频系列

千里马Android Framework实战开发视频-binder驱动的简单核心方法及原理介绍