本地套接字通信

概念:

         本地套接字是IPC, 即本地进程间通信的一种实现方式。除了本地套接字以外,还有前面学过的其他技术,如管道,共享内存,信号,信号量,信号灯集,消息队列等也是进程间通信的常用方法。

  • 创建套接字时使用本地协议PF_UNIX(PF_LOCAL)。这两种表达方式本质相同AF_UNIX是比较传统的命名方式,AF_LOCAL是后来为了更通用的命名而引入的。其想法是将这种本地通信(区别于网络通信)的套接字地址族称为 “本地(LOCAL)” 通信方式,而不是和特定的 UNIX 操作系统绑定。
  • AF_UNIX(地址族):AF 代表 “Address Family”,即地址族。AF_UNIX 用于指定 UNIX 域套接字的地址格式。它主要关注的是地址的格式和表示方式,用于在同一主机上的进程间通信。例如,当你使用 UNIX 域套接字进行通信时,你需要使用 AF_UNIX 来告诉系统你要使用的地址类型是基于 UNIX 域套接字的地址,这种地址通常和文件系统路径相关。
  • PF_UNIX(协议族):PF 代表 “Protocol Family”,即协议族。PF_UNIX 用于指定 UNIX 域套接字所使用的协议族。它更侧重于协议的分类,表明在这个通信过程中所遵循的协议集合是属于 UNIX 域套接字相关的协议。比如,当创建一个套接字时,使用 PF_UNIX 表示这个套接字将遵循 UNIX 域套接字协议来进行通信。
  • 分为流式套接字和用户数据报套接字
  • 和其他进程间通信方式相比使用方便、效率更高
  • 常用于前后台进程通信

利用本地套接字可完成可靠字节流和数据报两种协议

可通过ls  -l命令查看Linux系统内的本地套接字状况。

套接字是一种特殊类型的套接字,与TCP/UDP套接字不同。TCP/UDP即使在本地地址通信,也要走系统网络协议栈,而本地套接字,严格意义上来说提供了一种单主机跨进程间调用的手段,减少 协议栈实现的复杂度,效率比TCP/UDP套接字高很多。

本地地址就是本地套接字专属的。

API接口

#include <sys/socket.h>
#include <sys/un.h>
unix_socket = socket(AF_UNIX, type, 0);
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 本地路径 */
};
struct sockaddr_un myaddr;
bzero(&myaddr, sizeof(myaddr));
myaddr.sun_family = PF_UNIX;
strcpy(myaddr.sun_path, "mysocket"); //可以指定路径

本地字节流套接字

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
 
#define SOCKET_PATH "/tmp/unix_socket"
#define BUFFER_SIZE 1024
 
int main() {
    int server_fd, new_socket;
    struct sockaddr_un address;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    int opt = 1;
    int addrlen_size;
 
    // 创建套接字
    if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
 
    // 设置套接字选项,允许重用地址和端口
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        close(server_fd);
        exit(EXIT_FAILURE);
    }
 
    // 清除地址结构
    memset(&address, 0, sizeof(struct sockaddr_un));
 
    // 设置地址和端口
    address.sun_family = AF_UNIX;
    strncpy(address.sun_path, SOCKET_PATH, sizeof(address.sun_path) - 1);
 
    // 绑定套接字到地址
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(struct sockaddr_un)) < 0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }
 
    // 监听连接
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        close(server_fd);
        unlink(SOCKET_PATH); // 删除套接字文件
        exit(EXIT_FAILURE);
    }
 
    // 接受客户端连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen_size)) < 0) {
        perror("accept");
        close(server_fd);
        unlink(SOCKET_PATH); // 删除套接字文件
        exit(EXIT_FAILURE);
    }
 
    // 读取客户端发送的数据
    int valread = read(new_socket, buffer, BUFFER_SIZE);
    printf("Received: %s\n", buffer);
 
    // 发送响应给客户端
    char *hello = "Hello from server";
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");
 
    // 关闭套接字
    close(new_socket);
    close(server_fd);
    unlink(SOCKET_PATH); // 删除套接字文件
 
    return 0;
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
 
#define SOCKET_PATH "/tmp/unix_socket"
#define BUFFER_SIZE 1024
 
int main() {
    int sock = 0;
    struct sockaddr_un serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    char *hello = "Hello from client";
 
    // 创建套接字
    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        perror("Socket creation error");
        return -1;
    }
 
    serv_addr.sun_family = AF_UNIX;
    strcpy(serv_addr.sun_path, SOCKET_PATH);
 
    // 连接服务端
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_un)) < 0) {
        perror("Connection Failed");
        return -1;
    }
 
    // 发送数据到服务端
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");
 
    // 读取服务端的响应
    int valread = read(sock, buffer, BUFFER_SIZE);
    printf("Received: %s\n", buffer);
 
    // 关闭套接字
    close(sock);
 
    return 0;
}

注意:关于本地文件路径,需要明确一点,它必须是“绝对路径”,这样的话,编写好的程序可以在任何目录里被启动和管理。如果是“相对路径”,为了保持同样的目的,这个程序的启动路径就必须固定,这样一来,对程序的管理反而是一个很大的负担。
另外还要明确一点,这个本地文件,必须是一个“文件”,不能是一个“目录”。如果文件不存在,后面 bind 操作时会自动创建这个文件

本地数据报套接字

这部分内容省略,详细看:

IPC——数据报套接字通信 - Jessica程序猿 - 博客园

Linux进程间通讯方式:

[整理] Linux 进程间通信的方式、应用场景及优缺点 - 哆啦梦乐园 - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值