Ahao's Technical Blog Ahao's Technical Blog
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

阿豪讲Framework

不积跬步无以至千里
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础篇

  • 玩转AOSP篇

  • 学穿Binder篇

  • 基础组件篇

    • 001.Android 平台智能指针使用与分析
    • 002.弱引用 wp 的作用
    • 003.Linux eventfd 原理与实践
    • 004.Linux IO 多路复用 epoll 机制
    • 005.Linux timerfd 的基本使用
    • 006.Android Native Looper 机制
    • 007.Android Java Looper 机制
    • 008.Handler 同步屏障机制
    • 009.IdleHanlder 原理与使用
    • 010.Android 属性系统入门
    • 011.属性文件生成过程分析
    • 012.如何添加系统属性
    • 013.属性与 Selinux
    • 014.属性系统源码分析一
    • 015.属性系统源码分析二
    • 016.属性系统源码分析三
    • 017.Unix Domain Socket 使用解析之 UDP 篇
    • 018.Unix Domain Socket 使用解析之 TCP 篇
      • TCP 相关API
        • 构建 socket
        • bind
        • listen
        • accept
        • 读操作 recv
        • 写操作 send
        • 关闭 socket
        • 客户端连接服务端
      • 示例代码
      • 参考资料
    • 019.Android 中的 Unix Domain Socket 使用解析
    • 020.socketpair 使用解析
    • 021.Android 平台日志系统整体框架
    • 022.logd 守护进程初始化过程
    • 023.客户端写日志过程分析
    • 024.logd 写日志过程分析一
    • 025.logd 写日志过程分析二
    • 026.logd 读日志过程分析
    • 027.Android 平台日志丢失问题分析
  • 系统启动过程分析

  • Hal开发入门与实践

  • 显示系统

  • Framework
  • 基础组件篇
阿豪
2023-11-10
目录

018.Unix Domain Socket 使用解析之 TCP 篇

Android Framework 的 Native 层大量使用了 Unix domain socket。Unix domain socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信。socket 原本是为网络通讯设计的,但后来在 socket 的框架上发展出一种 IPC 机制,就是 UNIX domain socket。虽然网络 socket 也可用于同一台主机的进程间通讯(通过 loopback 地址 127.0.0.1),但是 UNIX domain socket 用于 IPC 更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC 机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。

# TCP 相关API

TCP 程序整体框架:

Server 端流程:

  • 构建 socket
  • bind
  • listen
  • accept
  • send/recv
  • close

Client 端流程:

  • Socket
  • bind
  • connect
  • send/recv
  • close

# 构建 socket

#include <sys/types.h>
#include <sys/socket.h>

int socket (int domain, int type, int protocol)
1
2
3
4
  • domain 指定Socket类型,AF_INET,AF_INET6,AF_UNIX,分别对应 ipv4、ipv6 和 Unix Domain Socket
  • type 可以选择 SOCK_DGRAM 或 SOCK_STREAM。SOCK_STREAM 意味着会提供按顺序的、可靠、双向、面向连接的比特流。SOCK_DGRAM 意味着会提供定长的、不可靠、无连接的通信。
  • protocol 参数指定为 0 即可

对于 Unix Domain Socket,一般这样初始化:

int fd = socket(AF_UNIX,SOCK_DGRAM,0);
1

# bind

收发数据之前,需要对 socket 进行绑定操作,所谓绑定操作,就是让 socket fd 和一个地址相关联:

int bind (int fd, CONST_SOCKADDR_ARG addr, socklen_t len)
1
  • fd 是构建 socket 过程中返回的文件描述符
  • addr 指定了地址,它是 sockaddr_un 类型,在 sys/un.h 头文件中定义
  • len 用于指定 addr 的长度

addr 数据结构定义如下:

struct sockaddr_un{
	sa_family_t sun_family;   // always AF_UNIX
	char sun_path[128];  
};
1
2
3
4

用法如下:

char* server_file = "server.sock";

struct sockaddr_un addr;
memset(&addr,0,sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,server_file);

if (access(addr.sun_path,0) != -1)
{
    remove(addr.sun_path);
}

 if (bind(socketfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
    perror("bind");
    return -1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

这里有一个细节:如果绑定的文件已经存在,要删掉它,不然bind不会成功的

# listen

int listen (int fd, int n)
1

listen 通常又服务器调用,用于与连接前的准备。

  • fd 就是 socket 的描述符
  • n 就是最大的连接数量

# accept

int accept (int fd, SOCKADDR_ARG addr,
		   socklen_t *addr_len)
1
2

listen 是为连接做准备,而 accept 则会等待新的连接,如果有新的连接,那就会返回一个新的连接的文件描述符。连接成功后,会将客户端的地址填充到 addr 的结构体上,字节大小填充到 addr_len。

# 读操作 recv

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
1
2
3
4

读写的字节由 len 决定,读取成功后,将内容存放在 buf 当中。如果读取成功则返回接收到的字节数量。如果发生异常,则返回 -1,通常用返回值判断连接是否正常。

# 写操作 send

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
1
2
3
4

要发送的信息存放在 buf 当中,本次要发送的字节数量由 len 决定。

# 关闭 socket

int close(int fd);
1

# 客户端连接服务端

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
1
2
3
4
5

connect 是客户端专用,用于连接 server。connect 异常时返回-1,否则返回 0。地址在 addr 中指明。

用法如下:

struct sockaddr_un serveraddr;
socklen_t addrlen = sizeof(serveraddr);
memset(&serveraddr,0,addrlen);
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path,server_file);

int newcon = -1;
newcon = connect(socketfd,(sockaddr*)&serveraddr,addrlen);
1
2
3
4
5
6
7
8

# 示例代码

服务端代码:

// tcpserver.cpp
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <memory.h>
#include <unistd.h>
#include <stdio.h>

using namespace std;

char* server_file = "/tmp/localserver.sock";

int main(int argc,char** argv)
{

    int socketfd = socket(AF_UNIX,SOCK_STREAM,0);
    if ( socketfd < 0)
    {
        perror("socket");
        return -1;
    }


    struct sockaddr_un addr;
    memset(&addr,0,sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path,server_file);

    if (access(addr.sun_path,0) != -1)
    {
        remove(addr.sun_path);
    }

    if (bind(socketfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
    {
        perror("bind");
        return -1;
    }

    if (listen(socketfd,12) < 0)
    {
        perror("listen");
        return -1;
    }

    struct sockaddr_un clientaddr;
    socklen_t addrlen = sizeof(clientaddr);

    char buf[128];

    int newcon = -1;

    newcon = accept(socketfd,(sockaddr*)&clientaddr,&addrlen);

    if (newcon > 0)
    {

        int curindex = 0;
        int edgeindex = 0;
        while (1)
        {
            if (recv(newcon,buf,sizeof(buf),0) == -1)
            {
                perror("server recv:");
                break;
            }

            cout << "server recv a message:" << buf << endl;

            memset(buf,0,sizeof(buf));


            if (send(newcon,"ok",2,0) < 0)
            {
                perror("server send:");
                break;
            }
        }

    }

    close(newcon);
    close(socketfd);

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

客户端代码:

// tcpclient.cpp 
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <memory.h>
#include <unistd.h>
#include <stdio.h>

using namespace std;


char* server_file = "/tmp/localserver.sock";
char* client_file = "/tmp/localclient.sock";

int main(int argc,char** argv)
{

    int socketfd = socket(AF_UNIX,SOCK_STREAM,0);
    if ( socketfd < 0)
    {
        perror("client socket");
        return -1;
    }


    struct sockaddr_un addr;
    memset(&addr,0,sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path,client_file);

    if (access(addr.sun_path,0) != -1)
    {
        remove(addr.sun_path);
    }

    if (bind(socketfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
    {
        perror("client bind");
        return -1;
    }


    struct sockaddr_un serveraddr;
    socklen_t addrlen = sizeof(serveraddr);
    memset(&serveraddr,0,addrlen);
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path,server_file);

    char buf[128];

    int newcon = -1;


    newcon = connect(socketfd,(sockaddr*)&serveraddr,addrlen);

    if (newcon < 0)
    {
        perror("client connect");
    }


    int curindex = 0;
    int edgeindex = 0;
    int index = 0;
    
    while (index < 3)
    {
        
        string temp = "client for test:";
        temp.append(to_string(index));

        if (send(socketfd,temp.c_str(),temp.capacity(),0) < 0)
        {
            perror("client send:");
            break;
        }

        if (recv(socketfd,buf,sizeof(buf),0) == -1)
        {
            perror("client recv:");
            break;
        }

        cout << "client recv:" << buf << endl;

        memset(buf,0,sizeof(buf));

        index++;

        sleep(1);

    }


    close(socketfd);

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

# 参考资料

  • Android LocalSocket 详细解析 (opens new window)
  • Unix domain socket 简介 (opens new window)
  • Linux 多进程通信开发(八): unix domain socket 之 TCP 通信 (opens new window)
  • Linux 多进程通信开发(七): unix domain socket 之 UDP 通信 (opens new window)
  • Android Java层和Native层通信实战大荟萃之LocalSocket实现通信 (opens new window)
017.Unix Domain Socket 使用解析之 UDP 篇
019.Android 中的 Unix Domain Socket 使用解析

← 017.Unix Domain Socket 使用解析之 UDP 篇 019.Android 中的 Unix Domain Socket 使用解析→

最近更新
01
如何调试 SurfaceFlinger
10-05
02
SurfaceFlinger 概述
10-05
03
HWC 接口分析
10-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 AHao Framework | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式