Unix/Linux编程:Unix domain socket

news2024/11/26 21:51:06

〇、前言

socket 是一种 IPC (Inter-Process Communication,进程间通信)方法,它允许位于同一主机(计算机)或使用网络连接起来的不同主机上的应用程序之间交换数据。通过使用Socket,开发人员可以创建网络应用程序,使其能够通过网络进行数据交换和通信。

Socket API通常用于基于TCP/IP协议栈的网络通信,但也可以用于其他网络协议。它提供了一组函数和数据结构,允许应用程序创建、连接、发送和接收数据,并管理网络连接。

Socket API的使用通常涉及以下几个步骤:

  • 创建Socket:使用socket()函数创建一个Socket对象,该函数指定了协议族(例如,IPv4、IPv6)和Socket类型(例如,流式Socket或数据报Socket)。
  • 绑定Socket:对于服务器应用程序,需要使用bind()函数将Socket绑定到一个特定的IP地址和端口号。
  • 监听连接请求(可选):对于服务器应用程序,可以使用listen()函数开始监听传入的连接请求。
  • 接受连接请求(可选):对于服务器应用程序,使用accept()函数接受传入的连接请求,并创建一个新的Socket对象来处理与客户端的通信。

对于客户端:

  • 建立连接(可选):对于客户端应用程序,使用connect()函数与服务器建立连接。
  • 发送和接收数据:使用send()函数将数据发送到远程主机,使用recv()函数从远程主机接收数据。
  • 关闭Socket:使用close()函数关闭Socket连接。

这只是Socket编程的基本概念和步骤,具体的使用方法和函数会根据编程语言和操作系统而有所不同。常见的编程语言,如C、C++、Java和Python,都提供了相应的Socket API库,使开发人员能够使用Socket进行网络编程。

通过Socket编程,应用程序可以实现客户端-服务器模型,建立网络通信、传输数据和实现各种网络应用,如Web服务器、聊天应用、文件传输等。

流程图如下:

在这里插入图片描述

本文将会写一个简单的、基本的服务器和客户端,并且成功让两者通信,平台为mac M1。

一、地址结构

对于各种 socket domain 都需要定义一个不同的结构类型来存储socket地址。然而由于诸如bind()之类的系统调用适用于所有 socket domain , 因此它们必须要能够接受任意类型的地址结构。为支持这种行为,socket API 定义了一个通用 的地址结构 struct sockaddr。这个类型的唯一用途是将各种 domain 特定的地址结构转换成单个类型以供 socket 系统调用中的各个参数使用。sockaddr 结构通常被定义成如下所示的结构:·

struct  sockaddr_un {
	unsigned char   sun_len;        /* sockaddr len including null */
	sa_family_t     sun_family;     /* [XSI] AF_UNIX */
	char            sun_path[104];  /* [XSI] path name (gag) */
};

因此,服务器中首先得创建一个 socket,并做好准备工作以及初始化:

#define BACKLOG 5
#define SV_SOCK_PATH "/tmp/us_xfr"
#define BUF_SIZE 100


	struct sockaddr_un addr;
    int sfd, cfd;
    ssize_t numRead;
    char buf[BUF_SIZE];

    // 创建一个 socket
    sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    // 对这个长度是有规定的
    if (strlen(SV_SOCK_PATH) > sizeof(addr.sun_path) - 1) {
        printf("Server socket path too long: %s", SV_SOCK_PATH);
        exit(EXIT_FAILURE);
    }

    // 绑定之前,先将这个文件删除了,因为如果它存在的话,说明已经被绑定到了其它 socket 上
    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT) {
        printf("remove-%s", SV_SOCK_PATH);
        exit(EXIT_FAILURE);
    }

    // 初始化 socket
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);

二、绑定地址

在Socket编程中,bind()函数用于将一个特定的网络地址或者本地地址绑定到一个Socket对象上。这个绑定操作指定了Socket要使用的本地网络地址和端口,使得其他进程可以通过这个地址和端口与该Socket进行通信。

bind() 函数的原型如下:

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

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

  • sockfd:Socket文件描述符,表示要进行绑定的Socket对象。
  • addr:指向struct sockaddr类型的指针,包含要绑定的本地地址信息。
  • addrlen:addr结构体的大小。

以下是一个绑定网络地址的例子:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>

int main() {
	// ipv4
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in addr;
    // 初始化
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;
    
    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
        printf("Socket绑定成功\n");
    } else {
        perror("Socket绑定失败\n");
    }
   
    return 0;
}

三、监听:listen()

在Socket编程中,listen()函数用于将一个已绑定的Socket对象标记为被动状态,以便它可以开始监听传入的连接请求。listen()函数告知操作系统,该Socket将用于接受传入的连接,从而创建一个服务器端的Socket。
listen() 函数定义如下:

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

int listen(int sockfd, int backlog);

  • sockfd:Socket文件描述符,表示要监听的Socket对象。
  • backlog:等待连接队列的最大长度。

在调用listen() 函数时,需要提供一个已绑定的Socket对象,并指定等待连接队列的最大长度。等待连接队列是一个存储传入连接请求的缓冲区,如果该队列已满,新的连接请求将被拒绝。

四、请求链接:connect()

当服务器在 listen()的时候,这时候就可以由客户端发起链接请求。在Socket编程中,connect()函数用于建立客户端与服务器之间的连接。通过connect()函数,客户端可以向特定的服务器地址和端口发起连接请求,以便进行数据交换和通信。
以下是connect() 的原型:

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

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

  • sockfd:Socket文件描述符,表示要进行连接的Socket对象。
  • addr:指向struct sockaddr类型的指针,包含要连接的服务器地址信息。
  • addrlen:addr结构体的大小。

在调用connect() 函数时,需要传递一个struct sockaddr结构体指针作为参数,其中包含要连接的服务器地址信息。具体的地址信息结构体取决于使用的网络协议族,例如struct sockaddr_in用于IPv4地址。
以下是一个连接网络地址的示例:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>

int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == 0) {
        printf("成功与服务器建立连接\n");
    } else {
        perror("连接失败");
    }
    
    return 0;
}

需要注意的是,connect() 函数是一个阻塞调用,它会阻塞当前进程,直到连接建立成功或失败。如果连接成功建立,客户端就可以通过Socket与服务器进行数据交换。

五、接受链接请求:accept()

在Socket编程中,accept()函数用于服务器端接受客户端的连接请求,并创建一个新的Socket对象来与客户端进行通信。accept()函数在服务器端被调用,用于接受传入的连接请求,并返回一个新的Socket文件描述符,以便与客户端进行数据交换。
这里需要格外理解的是,如果在 accept()之前,没有客户端请求 connect()(未决的请求),那么服务端就会阻塞,直到有请求链接 connect()accept()会创建一个新的 socket,并将这个新的 socket 替换为原来的 socket,这样原来的 socket 就可以一直处在监听状态!

accept()函数的原型如下:

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd:Socket文件描述符,表示要接受连接请求的Socket对象;
  • addr:指向struct sockaddr类型的指针,用于存储客户端的地址信息;
  • addrlen:指向socklen_t类型的指针,用于存储addr结构体的大小。

六、关闭:close()

在Socket编程中,close()函数用于关闭一个打开的Socket连接或文件描述符。在网络编程中,close()函数用于关闭与对方主机的连接或者释放已创建的Socket对象。

close()函数的原型如下:

#include <unistd.h>
int close(int sockfd);
  • sockfd:Socket文件描述符或者文件描述符,表示要关闭的连接或文件。

在调用close()函数时,需要提供要关闭的Socket文件描述符或文件的文件描述符。close()函数将关闭指定的连接或文件,并释放相关的资源。

七、一个服务端的例子

这里会展示一个服务端的例子,这个例子给出了基本的创建方式和功能:

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


#define BACKLOG 5
#define SV_SOCK_PATH "/tmp/us_xfr"
#define BUF_SIZE 100
int main(int argc, char *argv[]) {
    struct sockaddr_un addr;

    int sfd, cfd;
    ssize_t numRead;
    char buf[BUF_SIZE];

    // 创建一个 socket
    sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    // 对这个长度是有规定的
    if (strlen(SV_SOCK_PATH) > sizeof(addr.sun_path) - 1) {
        printf("Server socket path too long: %s", SV_SOCK_PATH);
        exit(EXIT_FAILURE);
    }

    // 删除所有与路径名一致的既有文件,这样才能将 socket 绑定到这个路径名上
    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT) {
        printf("remove-%s", SV_SOCK_PATH);
        exit(EXIT_FAILURE);
    }

    // 初始化 socket
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);

    //将 socket 绑定到该地址上
    if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
        printf("bind");
        exit(EXIT_FAILURE);
    }

    // 将这个 socket 标记为监听 socket。
    if (listen(sfd, BACKLOG) == -1) {
        printf("listen");
        exit(EXIT_FAILURE);
    }

    // 执行一个无限循环来处理进入的客户端请求。每次循环迭代执行下列任务
    for (;;) { 
        // 接受一个连接,为该连接获取一个新 socket cfd
        cfd = accept(sfd, NULL, NULL);
        if (cfd == -1) {
            printf("accept");
            exit(EXIT_FAILURE);
        }

        //从已连接的 socket 中读取所有数据并将这些数据写入到标准输出中
        while ((numRead = read(cfd, buf, BUF_SIZE)) > 0)

            if (write(STDOUT_FILENO, buf, numRead) != numRead) {
                printf("partial/failed write");
                exit(EXIT_FAILURE);
            }

        if (numRead == -1) {
            printf("read");
            exit(EXIT_FAILURE);
        }

        // 关闭已连接的 socket cfd
        if (close(cfd) == -1) {
            printf("close");
            exit(EXIT_FAILURE);
        }
    }
}

这里需要注意的是:服务端在处理字符的时候,用了系统调用 read()write(),其中 read()cfd 中读取了某些字符存储到 buf 中;而 write()则是将 buff 中的数据写入到STDOUT_FILENO中,STDOUT_FILENO为标准输出,通常用文件描述符 1 表示,标准错误通常用文件描述符 2 表示,标准输入通常用文件描述符 0 表示。

八、一个客户端的例子

// us_xfr.h
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <zconf.h>

#define BACKLOG 5
#define SV_SOCK_PATH "/tmp/us_xfr"
#define BUF_SIZE 100
int main(int argc, char *argv[]) {
    struct sockaddr_un addr;
    int sfd;
    ssize_t numRead;
    char buf[BUF_SIZE];

    sfd = socket(AF_UNIX, SOCK_STREAM, 0); /* Create client socket */
    if (sfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    // 初始化
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);

    if (connect(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) ==
        -1) {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    /* Copy stdin to socket */
    while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE)) > 0)
        if (write(sfd, buf, numRead) != numRead) {
            printf("partial/failed write");
            exit(EXIT_FAILURE);
        }

    if (numRead == -1) {
        printf("read");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS); /* Closes our socket; server sees EOF */
}

九、启动客户端和服务端,并进行通信

编译,运行服务端:

(base) ***@shenjian Test % gcc 1.c -o server
(base) ***@shenjian Test % gcc 2.c -o client
./server


在另一个窗口运行客户端:

./client
hello

结果在服务端成功地接收到了客户端发来的消息hello:

(base) ***@shenjian Test % ./server         
hello

这就是一个简单的UDS 例子了,全文完,感谢阅读。

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

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

相关文章

在VSCode中使用LaTex,语法检测插件grammarly

整个文章分为以下几个内容&#xff0c;打 * 的是必须要安装的 LaTex 安装*VSCode 安装*在 VSCode 中配置 LaTexGrammarly语法检测插件 LaTex 安装* latex的下载安装可参考&#xff1a;LaTex&#xff08;2021&#xff09;安装教程 VSCode 安装* VSCode下载&#xff1a;VSCo…

带你用WePY框架提升开发效率

在小程序开发中&#xff0c;提高开发效率、优化代码质量和增强用户体验是每位开发者都追求的目标。而wepy作为一种基于Vue.js的小程序开发框架&#xff0c;提供了更好的开发体验和更高效的开发方式。本文将介绍wepy的基本功能和特性&#xff0c;分享一些实际的代码案例&#xf…

图像的匹配

2023.6.7 图像的匹配 模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。为此&#xff0c;OpenCV 带有一个函数 cv.matchTemplate()。它只是在输入图像上滑动模板图像&#xff08;如在 2D 卷积中&#xff09;&#xff0c;并比较模板图像下的模板和输入图像的补丁。在…

STM32——CAN通信

1、CAN通信概述 STM32F103有两个CAN&#xff0c;都分别有自己的发送接收邮箱。 发送邮箱共有3个来发送报文&#xff0c;发送调度器根据优先级决定哪个邮箱的报文先被发送。 共有2个接收FIFO&#xff0c;每个FIFO都可以存放3个完整的报文。它们完全由硬件来管理。 CAN通信通过…

Mujoco 控制器接口(四)

目录 .1 简介 1.1 控制器实现 .2 sim.step() .3 实例 References .1 简介 control看到下面的图中就是mjtNum类型 mjtNum实际上就是浮点数 ctrl是底层的输入 外界扰动就是通过上图的qfrc和xfrc来添加的 1.1 控制器实现 实际上加上控制器就是xml里添加actuator joint就是…

算法提高-图论-单源最短路的建图方式

单源最短路的建图方式 单源最短路的建图方式AcWing 1129. 热浪AcWing 1128. 信使AcWing 1127. 香甜的黄油AcWing 1126. 最小花费AcWing 920. 最优乘车AcWing 903. 昂贵的聘礼 单源最短路的建图方式 AcWing 1129. 热浪 #include <iostream> #include <cstring>usi…

安装OpenWrt到电脑,安装包选哪个?

https://downloads.openwrt.org/releases/22.03.5/targets/x86/ releases发行&#xff0c;targets目标&#xff1b; —————————————— 64&#xff1a;具有64位功能的计算机&#xff1b; generic&#xff1a;一般的&#xff0c;通用的&#xff0c;仅32位计算机&…

学习Vue这一个就够

1、淘宝镜像 1: 解释一下 npm 为什么要配置淘宝镜像原因&#xff1a;因为node.js 默认使用的是国外的网站 。国内访问有一个跨国内局域网的操作。所以就会有时候很慢。这就跟为什么网站的静态资源有些会使用CDN 加速一样的淘宝镜像是什么&#xff1f;就是npm 很多的插件淘宝已经…

docker cgroub

docker 的资源管理 1、cpu的资源控制 一&#xff1a;设置cpu的资源上限 cd /sys/fs/cgroup/cpu/docker/ cpu 的占用量达到100% cpu 设置一半50% 2. 设置cpu资源占用比&#xff08;设置多个容器才有用&#xff09; docker run -itd --name c3 --cpu-shares 512 centos:7 do…

springboot+mybatis实现删除(二)

一&#xff0c;XML映射文件和动态SQL XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;&#xff0c;右键创建目录/分隔&#xff0c;例&#xff1a;com/baidu/crm XML映射文件的namespace属性为…

工程训练 -江苏海洋大学-mooc-最终答案

这不点赞评论一下嘛&#xff1f;&#xff1f;&#xff1f;呜呜呜 判断题&#xff08;共217道&#xff09; 1.舂实模样周围及砂箱边或狭窄部分的型砂&#xff0c;通常采用砂舂的平头端舂砂。 2.造型时&#xff0c;分型面上通常使用的是面砂&#xff0c;覆盖模样的则使用背砂。 3…

【微服务】springboot整合swagger多种模式使用详解

目录 一、前言 1.1 编写API文档 1.2 使用一些在线调试工具 1.3 postman 1.4 swagger 二、swagger简介 2.1 背景 2.2 swagger优缺点 2.2.1 swagger优点 2.2.2 swagger缺点 2.2.3 swagger使用场景 三、swagger常用的几种整合模式 3.1 swagger2 3.2 knife4j 3.2.1 k…

《设计模式》之命令模式

文章目录 1、定义2、动机3、类结构4、优缺点5、总结6、代码实现(C) 1、定义 将一个请求封装为一个对象&#xff0c;从而使你可用不同的请求对客户端进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 2、动机 在软件构建过程中&#xff0c…

虚拟内存和物理内存:概念、原理和应用

目录 概述1. 概念2. 原理3. 设计寻位原理4. 应用场景结论 概述 当你使用计算机时&#xff0c;内存是一个非常重要的资源。它用于存储正在运行的程序和数据&#xff0c;确保系统的正常运行。在计算机系统中&#xff0c;存在着虚拟内存和物理内存的概念&#xff0c;它们共同协作…

Docker Gitlab Container Registry配置

文章目录 前言一、Registry是什么二、步骤配置gitlab.rb文件修改docker-compose.yaml文档验证推送镜像 总结 前言 找了很多资料包括官网1都没有发现比较清楚的配置registry的方法&#xff0c;自己摸索了半天发现其实通过简单设置就能够配置好Container Registry。 之所以在题…

【Docker】3.Docker Registry

文章目录 Docker RegistryDocker Registry CommandImage Command NginxNginx System installNginx Config Container CommandCreate My DegistryBusyBox腾讯云镜像仓库搭建 Docker Registry 镜像仓库负责存储、管理、分发镜像&#xff0c;并且提供了登录认证的能力&#xff0c…

LeetCode_二叉树_DFS_中等_129.求根节点到叶节点数字之和

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a;例如&#xff0c;从根节点到叶节点的路径 1 -> 2 -> …

【计算机网络】IP 地址处理函数

目录 1.struct sockaddr_in的结构 2.一般我们写的结构 3.常见的“点分十进制” 到 ” uint32_t 的转化接口 3.1. inet_aton 和 inet_ntoa &#xff08;ipv4&#xff09; 3.2. inet_pton 和 inet_ntop (ipv4 和 ipv6&#xff09; 3.3. inet_addr 和 inet_network 3…

人工智能-深度学习-科研神器推荐

根据知乎问题 有没有什么可以节省大量时间的 Deep Learning 效率神器&#xff1f; 的回答&#xff0c;筛选整理出一些深度学习科研神器。包括参数优化、数据可视化、模型部署蒸馏剪枝等。收录到 人工智能-深度学习-科研神器推荐https://​www.webhub123.com/#/home/detail?p4O…

vue监听缓存数据(localStorage)

方法&#xff1a;可以重写localStorage的setItem方法&#xff0c;当调用setItem方法设置新值的时候&#xff0c;会new Event(‘setItemEvent’) 用window.dispatchEvent()这个方法来派发一个事件&#xff0c;让window去监听 以下demo实现的是 一个页面获取诗句 然后将获取的数据…