[C++/Linux] UNIX域函数

目录

一.什么是UNIX域套接字?

二.如何使用UNIX域函数进行套接字编程?

三.利用socketpair函数进行文件描述符传递

 3.1 socketpair函数

3.2  实例

3.3 补充消息结构知识 


一.什么是UNIX域套接字?

Unix域套接字(Unix Domain Sockets),也称为本地套接字(Local Sockets),是一种特殊的套接字,它允许在同一台主机上的进程间进行通信。与网络套接字不同,Unix域套接字不经过网络协议栈,因此它们通常比网络套接字更快,并且不需要端口和IP地址。

Unix域套接字提供了流式(SOCK_STREAM)和数据报(SOCK_DGRAM)两种通信方式,类似于TCP和UDP。流式套接字提供了可靠的双向字节流通信,而数据报套接字则提供了不可靠的双向数据报通信。

二.如何使用UNIX域函数进行套接字编程?

        2.1)  UNIX域的地址结构是 sockaddr_un,它用于指定UNIX域套接字的地址。这个结构体通常定义在 <sys/un.h> 头文件中,其基本形式如下:   

struct sockaddr_un {sa_family_t sun_family;     /* Address family (AF_UNIX) */char        sun_path[108];   /* Path name */
};
  • sun_family:地址家族,对于UNIX域套接字,这个字段总是设置为 AF_UNIX
  • sun_path:一个字符数组,用于存放套接字的路径名。在UNIX域套接字中,这个路径名对应于文件系统中的一个节点,类似于网络套接字中的IP地址和端口号。

     2.2)UNIX域套接字的函数与普通的网络套接字函数类似,但是它们用于本地通信,而不是网络通信。下面是一些常用的UNIX域套接字函数:

  • socket():创建一个新的套接字,与普通套接字函数相同,但是地址家族参数指定为 AF_UNIX
  • bind():将套接字绑定到一个特定的地址上,对于UNIX域套接字,这个地址是一个文件系统路径。
  • listen():允许套接字接受来自其他套接字的连接请求。
  • accept():接受一个传入的连接请求,返回一个新的套接字文件描述符,用于与连接的客户端通信。
  • connect():发起一个连接到指定地址的套接字。
  • send()sendto()sendmsg():用于发送数据。
  • recv()recvfrom()recvmsg():用于接收数据。
  • close():关闭套接字连接。

    2.3)UNIX域套接字函数与普通套接字函数的主要区别在于地址结构和地址家族。UNIX域套接字使用 sockaddr_un 地址结构,而普通套接字使用 sockaddr_in 或 sockaddr_in6 地址结构。此外,UNIX域套接字的地址是一个文件系统路径,而不是IP地址和端口号。

UNIX域套接字的优点包括:

  • 本地通信速度更快,因为不需要经过网络协议栈。
  • 不受网络配置的影响,不需要IP地址和端口号。
  • 提供了流式(SOCK_STREAM)和数据报(SOCK_DGRAM)两种通信方式。

   UNIX域套接字的缺点包括:

  • 只能在同一台主机上的进程间通信。
  • 需要管理套接字文件,这可能会带来一些复杂性。

 2.4)一个简单实例:

服务端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>#define SOCKET_PATH "/tmp/uds_socket"
#define BUFFER_SIZE 1024int main() {int server_fd, client_fd;struct sockaddr_un server_addr, client_addr;socklen_t client_addr_len;char buffer[BUFFER_SIZE];// 创建 Unix 域套接字server_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (server_fd == -1) {perror("socket");exit(EXIT_FAILURE);}// 初始化服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sun_family = AF_UNIX;strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);// 绑定套接字到地址if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind");exit(EXIT_FAILURE);}// 监听套接字if (listen(server_fd, 1) == -1) {perror("listen");exit(EXIT_FAILURE);}// 接受客户端连接client_addr_len = sizeof(client_addr);client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);if (client_fd == -1) {perror("accept");exit(EXIT_FAILURE);}// 读取客户端发送的数据if (read(client_fd, buffer, BUFFER_SIZE) == -1) {perror("read");exit(EXIT_FAILURE);}printf("Received message: %s\n", buffer);// 关闭套接字close(client_fd);close(server_fd);// 删除 Unix 域套接字文件unlink(SOCKET_PATH);return 0;
}

 客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>#define SOCKET_PATH "/tmp/uds_socket"
#define BUFFER_SIZE 1024int main() {int client_fd;struct sockaddr_un server_addr;char buffer[BUFFER_SIZE] = "Hello, Server!";// 创建 Unix 域套接字client_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (client_fd == -1) {perror("socket");exit(EXIT_FAILURE);}// 初始化服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sun_family = AF_UNIX;strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);// 连接到服务器if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("connect");exit(EXIT_FAILURE);}// 发送数据到服务器if (write(client_fd, buffer, strlen(buffer) + 1) == -1) {perror("write");exit(EXIT_FAILURE);}// 关闭套接字close(client_fd);return 0;
}

这个过程非常类似网络通信,但这仅仅是一个主机内的不同进程间通信,大家需要弄清楚原理!

三.利用socketpair函数进行文件描述符传递

 3.1 socketpair函数

在Unix系统中,socketpair 函数用于创建一对无连接的套接字,这两个套接字可以用于本地进程间的通信。这个函数创建的套接字对类似于管道(pipe),但提供了全双工通信的能力,即两个进程都可以同时进行发送和接收操作。

 注意:socketpair 函数创建的套接字对专门用于本地进程间通信,它们不能用于网络通信。这些套接字是基于 Unix 域(AF_UNIX)的,这意味着它们只在同一台主机上的进程之间提供通信能力。Unix域套接字不涉及网络协议栈,因此它们不受网络接口、IP 地址、端口等网络概念的影响。

如果你需要在不同主机上的进程之间进行通信,你应该使用网络套接字,这通常是基于 AF_INET(IPv4)或 AF_INET6(IPv6)地址族的。网络套接字允许进程通过 TCP 或 UDP 协议在网络中进行通信,并且可以通过指定目标 IP 地址和端口号来建立连接。

int socketpair(int domain, int type, int protocol, int sv[2]);
  • domain:指定套接字域,通常为 AF_UNIX,表示本地通信。
  • type:指定套接字类型,可以是 SOCK_STREAM(流式套接字)或 SOCK_DGRAM(数据报套接字)。
  • protocol:通常设置为0,让系统自动选择协议。
  • sv:一个包含两个整数的数组,用于接收创建的套接字文件描述符。

 socketpair 函数成功时返回0,失败时返回-1并设置errno来表示错误。

3.2  实例

 要使用 socketpair 函数给两个不同进程传递 msghdr 消息,我们可以利用 socketpair 创建的套接字对来进行进程间通信。下面是一个示例代码,展示了如何在一个进程中创建 msghdr 消息,然后通过 socketpair 传递给另一个进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define BUF_SIZE 1024int main(int argc, char *argv[]) {int sock_pair[2];struct msghdr msg;struct iovec iov;char buf[BUF_SIZE];pid_t pid;// 创建 socketpairif (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair) < 0) {perror("socketpair");exit(EXIT_FAILURE);}pid = fork();if (pid == 0) { // 子进程close(sock_pair[0]); // 关闭不需要的套接字// 接收消息memset(&msg, 0, sizeof(msg));iov.iov_base = buf;iov.iov_len = BUF_SIZE;msg.msg_iov = &iov;msg.msg_iovlen = 1;if (recvmsg(sock_pair[1], &msg, 0) < 0) {perror("recvmsg");exit(EXIT_FAILURE);}printf("Child received: %s\n", buf);close(sock_pair[1]);} else if (pid > 0) { // 父进程close(sock_pair[1]); // 关闭不需要的套接字// 准备发送消息strcpy(buf, "Hello, child process!");iov.iov_base = buf;iov.iov_len = strlen(buf) + 1;msg.msg_iov = &iov;msg.msg_iovlen = 1;// 发送消息if (sendmsg(sock_pair[0], &msg, 0) < 0) {perror("sendmsg");exit(EXIT_FAILURE);}close(sock_pair[0]);wait(NULL); // 等待子进程结束} else {perror("fork");exit(EXIT_FAILURE);}return 0;
}

在这个例子中,父进程通过 socketpair 创建了一对套接字,然后通过 fork 创建了一个子进程。父子进程各自关闭了自己不需要的套接字文件描述符,然后使用剩下的套接字进行通信。父进程准备了一个 msghdr 消息并通过 sendmsg 发送给子进程,子进程通过 recvmsg 接收并打印接收到的消息。

这个例子展示了如何使用 socketpair 和 msghdr 在两个进程间传递消息。这种通信方式适用于本地进程间的高效通信,不需要经过网络协议栈,因此在性能上比网络通信更优。

3.3 补充消息结构知识 

在Unix网络编程中,msghdr 结构体是用来在进程和内核之间传递消息的控制信息。这个结构体在 <sys/socket.h> 头文件中定义,用于 sendmsg 和 recvmsg 系统调用,这两个调用可以处理带外数据、多个数据缓冲区以及控制信息。

msghdr 结构体的定义可能因不同的Unix系统和版本而有所不同,但通常包含以下字段:

  • msg_name:指向接收者或发送者的地址的指针。
  • msg_namelen:地址的长度。
  • msg_iov:一个 iovec 结构数组,用于指定数据缓冲区。
  • msg_iovlenmsg_iov 数组中的元素个数。
  • msg_control:指向辅助数据的指针,通常用于传递Ancillary Data(如文件描述符、凭证等)。
  • msg_controllen:辅助数据的长度。
  • msg_flags:指定接收或发送时的消息标志,例如 MSG_OOB 表示带外数据。

这里是一个简化的 msghdr 结构体的示例:

struct msghdr {void         *msg_name;       /* Optional address */socklen_t     msg_namelen;     /* Size of address */struct iovec *msg_iov;         /* Scatter/gather array */int           msg_iovlen;      /* # elements in msg_iov */void         *msg_control;     /* Ancillary data, see below */socklen_t     msg_controllen;  /* Ancillary data buffer len */int           msg_flags;       /* Flags on received message */
};

 而cmsghdr 是在 Unix 网络编程中用于处理辅助数据(ancillary data)的结构体。辅助数据是一种特殊的通信数据,它不直接包含在正常的数据流中,而是与正常数据一起通过 sendmsg 和 recvmsg 系统调用发送和接收。辅助数据可以用于传递控制信息,如文件描述符、证书、流量控制信息等。

cmsghdr 结构体通常定义在 <sys/socket.h> 头文件中,它的字段包括:

  • cmsg_len:表示整个 cmsghdr 结构体加上随后的数据的长度。
  • cmsg_level:指定协议级别,通常是 SOL_SOCKET,表示通用套接字选项,也可以是其他协议级别。
  • cmsg_type:指定辅助数据的类型,例如 SCM_RIGHTS 用于传递文件描述符。
struct cmsghdr {socklen_t cmsg_len;    /* Data byte count, including the cmsghdr */int       cmsg_level;  /* Originating protocol */int       cmsg_type;   /* Protocol-specific type */
/* followed byunsigned char cmsg_data[]; */
};

在发送和接收辅助数据时msg_control 字段指向一个缓冲区,这个缓冲区包含了一个或多个 cmsghdr 结构体,每个结构体后面跟着相应的数据msg_controllen 字段指定了整个控制消息缓冲区的大小。

使用 cmsghdr 需要注意正确计算 cmsg_len,以及在发送和接收时正确处理对齐问题,因为 cmsg_data 部分可能需要特定于平台的对齐。在 Linux 上,可以使用 CMSG_FIRSTHDRCMSG_NXTHDRCMSG_DATA 等宏来帮助处理这些细节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/813664.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

程序“猿”高阶函数

高阶函数是函数式编程的一个核心概念&#xff0c;它提供了强大的抽象能力&#xff0c;使得代码更加简洁和模块化。正如你所提到的例子&#xff0c;高阶函数可以接受其他函数作为参数&#xff0c;或者返回一个函数。这种特性让它们在处理列表操作、事件处理、异步编程等场景中非…

【力扣一刷】代码随想录day38(动态规划part1:509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼)

目录 【动态规划理论基础】 【509. 斐波那契数】简单题 方法一 用额外的数组存储每个状态 方法二 用2个遍历存储前两个状态&#xff08;减小空间复杂度&#xff09; 【70. 爬楼梯】简单题 【746. 使用最小花费爬楼】简单题 【动态规划理论基础】 1、定义&#xff1a;英…

代码随想录算法训练营第四十二天|leetcode121、122题

一、leetcode第121题 本题要求买卖股票一次获取最大利润&#xff0c;设置dp数组&#xff0c;其中dp[i][0]的含义是第i天持有股票的最大利润&#xff0c;dp[i][1]的含义是第i天不持有股票的最大利润&#xff0c;可得递推公式为dp[i][0]max(dp[i-1][0],-prices[i])&#xff0c;d…

A15 STM32_HAL库函数 之 FLASH扩展驱动 所有函数的介绍及使用

A15 STM32_HAL库函数 之 FLASH扩展驱动 所有函数的介绍及使用 1 FLASH扩展驱动 预览1.1 HAL_FLASHEx_Erase1.2 HAL_FLASHEx_Erase_IT1.3 HAL_FLASHEx_OBErase1.4 HAL_FLASHEx_OBProgram1.5 HAL_FLASHEx_OBGetConfig1.6 HAL_FLASHEx_OBGetUserData 该文档修改记录&#xff1a;总…

【从浅学到熟知Linux】环境变量详谈(含使用程序获取环境变量的3种方法、如何查看环境变量)

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程及数据库等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 环境变量基本概念查看环境变量的方法环境变量相关命令环境变量组织方式及获取环境变量的3种方法验…

Cesium.js--》探秘Cesium背后的3D模型魔力—加载纽约模型

今天简单实现一个Cesium.js的小Demo&#xff0c;加强自己对Cesium知识的掌握与学习&#xff0c;先简单对这个开源库进行一个简单的介绍吧&#xff01; Cesium 是一个开源的地理空间可视化引擎&#xff0c;用于创建基于 Web 的三维地球应用程序。它允许开发人员在网页上呈现高度…

Java基础第十一课——类与对象(2)

由于类与对象这一部分的知识点很多&#xff0c;而且操作方法也有很多&#xff0c;所以这次将继续深入讨论一下关于类与对象中方法传参、方法重载、构造方法以及this关键字使用方面的知识。 一、方法传参 1.return关键字 return关键字作用 作用场景&#xff1a;方法内 作用…

天猫精灵要会员,不能听歌,还能用来干什么呢?榨干它的剩余价值

目录 起因&#xff1a;以听歌为主要功能的设备&#xff0c;却不能听歌了 1.蓝牙音箱 2.控制智能家电 3.万能遥控器&#xff0c;需要一个外接设备 4.倒计时/提醒&#xff0c;闹钟提醒&#xff0c;整点提醒&#xff08;这功能有人不喜欢&#xff0c;闲吵&#xff0c;还不能关…

LeetCode题练习与总结:最小路径和--64

一、题目描述 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],[1,5,1],[4,2,1]] 输出…

UI设计规范

一套商城系统的诞生&#xff0c;除了代码的编写&#xff0c;UI设计也至关重要。UI设计关系到商城系统的最终呈现效果&#xff0c;关乎整体商城的风格展现&#xff0c;如果UI设计做不好&#xff0c;带来的负面影响也是不容小觑的。 1、在很多商城系统开发中&#xff0c;有时会有…

【Sql Server】锁表如何解锁,模拟会话事务方式锁定一个表然后进行解锁

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言创建表模拟…

运放噪声评估的来龙去脉

运放噪声评估的来龙去脉 友情提示&#xff0c;运放电路的噪声分析还是比较复杂的&#xff0c;不论是基础理论还是对应的推导过程&#xff0c;都不是特别容易。考虑到兄弟们的基础参差不齐&#xff0c;所以我还是尽量说清楚点&#xff0c;这样导致看起来就有点罗里吧嗦&#xff…

10 Php学习:循环

在 PHP 中&#xff0c;提供了下列循环语句&#xff1a; while - 只要指定的条件成立&#xff0c;则循环执行代码块do…while - 首先执行一次代码块&#xff0c;然后在指定的条件成立时重复这个循环for - 循环执行代码块指定的次数foreach - 根据数组中每个元素来循环代码块 当…

【Java EE】获取Cookie和Session

文章目录 &#x1f38d;Cookie简介&#x1f340;理解Session&#x1f333;Cookie 和 Session 的区别&#x1f332;获取Cookie&#x1f338;传统获取Cookie&#x1f338;简洁获取Cookie &#x1f334;获取Session&#x1f338;Session存储&#x1f338;Session读取&#x1f33b;…

Fence同步

在《Android图形显示系统》没有介绍到帧同步的相关概念&#xff0c;这里简单介绍补充一下。 在图形显示系统中&#xff0c;图形缓存GraphicBuffer可以被不同的硬件来访问&#xff0c;如CPU、GPU、HWC都可以对缓存进行读写&#xff0c;如果同时对图形缓存进行操作&#xff0c;有…

mysql8.0高可用集群架构实战

MySQL :: MySQL Shell 8.0 :: 7 MySQL InnoDB Cluster 基本概述 InnoDB Cluster是MySQL官方实现高可用读写分离的架构方案,其中包含以下组件 MySQL Group Replication,简称MGR,是MySQL的主从同步高可用方案,包括数据同步及角色选举Mysql Shell 是InnoDB Cluster的管理工具,用…

java项目之校园兼职系统(ssm框架+mysql数据库+文档)

项目简介 校园兼职系统的主要使用者分为&#xff1a;管理员&#xff1a;首页、个人中心、专业管理、商家管理、热门兼职管理、学生管理、兼职接单管理、学生咨询管理、兼职任务管理、完成评价管理、管理员管理、系统管理等模块信息的查看及相应操作&#xff1b;学生&#xff1…

OpenCV C++学习笔记

1.图像的读取与显示 1.1 加载并显示一张图片 #include<opencv2/opencv.hpp> #include<iostream>using namespace cv; using namespace std; int main(int argc,char** argv){Mat srcimread("sonar.jpg");//读取图像if(src.empty()){printf("Could…

超越传统Lambda函数:深入解析Out-of-line Lambdas的奇妙之处

超越传统函数&#xff1a;深入解析线外 Lambda函数 的奇妙之处 一、背景二、lambda 的捕获三、可能出现的警告四、lambda的广义捕获五、为每种情况进行重载六、总结 一、背景 Out-of-line Lambdas翻译过来就是“线外Lambda函数”或“离线Lambda函数”。Lambda 是使代码更具表现…

[阅读笔记] 电除尘器类细分市场2023年报

0.原始链接&#xff1a; 2023年除尘行业评述及2024年发展展望-北极星大气网 中国环保产业协会 供稿 1.重要信息摘录 市场占有率最大的是电除尘和袋式除尘行业装备产品名录: 国家鼓励发展的重大环保技术装备目录&#xff08;2023年版&#xff09;权威评审机构&#xff1a;…