Unix/Linux编程:UDS 流(Stream)

news2024/11/17 9:27:16

〇、前言

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/634485.html

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

相关文章

【C++】——栈和队列(stack、queue)及优先队列(priority_queue)的介绍和模拟实现

文章目录 1. 前言2. 容器适配器2.1 容器适配器的介绍2.2 STL标准库中stack和queue的底层结构2.3 deque的简单介绍2.4 deque的缺陷2.5 为什么选择deque作为stack和queue的底层默认容器 3. stack3.1 stack的介绍3.2 stack的使用3.3 stack模拟实现 4. queue4.1 queue的介绍4.2 que…

数据分布——长尾分布的处理

前言 长尾分布在分类任务中会提到这个名,这是因为长尾分布这个现象问题会导致在训练过程中会出现出错率高的问题&#xff0c;影响了实验结果。 这里要说的是&#xff0c;长尾分布是一种现象&#xff0c;有的地方说是一种理论或定律&#xff0c;我感觉这样说不太确切&#xff0…

取石子游戏——算法与编程

取石子游戏 目录 问题描述输入输出格式输入格式&#xff1a;输出格式&#xff1a; 输入输出样例输入样例#1&#xff1a;输出样例#1&#xff1a;提示信息 算法尼姆博奕 代码 问题描述 A l i c e Alice Alice和 B o b Bob Bob在玩取石子游戏&#xff0c;摆在他们面前的有 n n n堆…

GIS入门进阶之012

一、引言 空间数据可视化是有效传输与表达地理信息&#xff0c;挖掘空间数据之间的内在联系&#xff0c;揭示地理现象内在规律的重要手段。它通过运用地图学、计算机图形学和图像处理技术&#xff0c;将地学信息的输入、处理、查询、分析与预测的结果采用符号、图形、图像并结合…

OpenGL 材质实现

1.简介 在现实世界里&#xff0c;每个物体会对光产生不同的反应。比如&#xff0c;钢制物体看起来通常会比陶土花瓶更闪闪发光&#xff0c;一个木头箱子也不会与一个钢制箱子反射同样程度的光。有些物体反射光的时候不会有太多的散射&#xff0c;因而产生较小的高光点&#xf…

35岁被淘汰?软件测试工程师职业生涯规划,从技术到管理...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 入门阶段&#xf…

Parallel Desktop中按照的centos在切换root用户时,密码正确,但一直切换不成功,显示su: Authentication failure

目录 一、出现问题二、分析问题三、解决问题四、参考资料 一、出现问题 我的密码明明是输入正确的&#xff0c;但又一直给我报下面的错误 二、分析问题 我怀疑是我密码记错了&#xff0c;所以我点击Log Out&#xff0c;重新去输入了一下密码&#xff0c;发现是正确的我确认…

[学习笔记] [机器学习] 9. 朴素贝叶斯(概率基础、联合概率、条件概率、贝叶斯公式、情感分析)

视频链接数据集下载地址&#xff1a;无需下载 学习目标&#xff1a; 4. 说明条件概率与联合概率 5. 说明贝叶斯公式、以及特征独立的关系 6. 记忆贝叶斯公式 7. 知道拉普拉斯平滑系数 8. 应用贝叶斯公式实现概率的计算 9. 会使用朴素贝叶斯对商品评论进行情感分析 1. 朴素贝叶…

对象进阶-继承、原型-原型链

工厂方法创建对象 我们之前已经学习了如何创建一个对象&#xff0c;那我们要是想要创建多个对象又该怎么办&#xff1f;聪明的同学可能会说&#xff0c;直接在写几个对象不就好了吗&#xff1f;比如下边的代码&#xff1a; var person1 {name: "孙悟空",age: 18,s…

APP自动化测试,Appium+PO模式+Pytest框架实战—项目案例

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 PO模式&#xff1…

如何解决GEE导出影像的Nodata值在ArcGIS中无法正常显示?

目录 01 ArcGIS对于GEE掩膜影像的Nodata值的说明 02 处理方法 2.1 方法1-GEE修改掩膜值 Arguments: Returns: Image 2.2 方法2-ArcGIS重新赋值Nodata&#xff08;推荐&#xff09; 01 ArcGIS对于GEE掩膜影像的Nodata值的说明 当在GEE中进行掩膜后&#xff0c;将影像在Ar…

打造极简风格动效 —— 5 分钟轻松实现惊艳、震撼人心的视觉效果

前期回顾 是不是在为 API 烦恼 &#xff1f;好用免费的api接口大全呼之欲出_免流接口api_彩色之外的博客-CSDN博客APi、常用框架、UI、文档—— 整理合并https://blog.csdn.net/m0_57904695/article/details/130459417?spm1001.2014.3001.5501 &#x1f44d; 本文专栏&…

20道嵌入式经典面试题(附答案)

1.嵌入式系统中经常要用到无限循环&#xff0c;如何用C编写死循环 答&#xff1a;while(1){} 或者 for(;;) 2.程序的局部变量存在于哪里&#xff0c;全局变量存在于哪里&#xff0c;动态申请数据存在于哪里。 答&#xff1a;程序的局部变量存在于栈区&#xff1b;全局变量存在…

【Linux】浅谈文件原理与操作

目录 问题引入 浅谈文件原理 文件操作 文件的打开与关闭 open close write与read 再谈C库文件操作 问题引入 &#x1f338;以前我们学过C语言的文件操作&#xff0c;而不同语言的文件操作都是不一样的&#xff0c;我们该如何理解这一现象&#xff0c;能不能用一种统一…

有关 string 类的练习(下)

目录 一、反转字符串 II 二、反转字符串中的单词 III 三、找出字符串中第一个只出现一次的字符 四、字符串相乘 五、把字符串转换成整数 一、反转字符串 II 给定一个字符串 s 和一个整数 k&#xff0c;从字符串开头算起&#xff0c;每计数至 2k 个字符&#xff0c;就反转…

Spring,注解开发

Spring是一个轻量级的控制反转&#xff08;IOC&#xff09;和面向切面编程&#xff08;AOP&#xff09;的框架 1、组成 spring七大模块详解 2、IOC理论推导 传统的开发 (pojo层、DAO层和业务层&#xff09; &#xff08;1&#xff09;UserDao &#xff08;2) UserDaoImpl (3)…

天狼星-大熊座 Ursa Major SIRIUS

大熊座 Ursa Major SIRIUS 键盘说明 客制化键盘&#xff1a; 大熊座 Ursa Major SIRIUS 配列&#xff1a; 75 键帽&#xff1a;KCA-HelloWorld-Black 双模&#xff1a; 蓝牙-分裂方案 驱动配置&#xff1a;Link Lab 驱动设置软件 键盘操作 键盘说明参考&#xff1a;键位配…

利用WinDbg查看堆栈中方法入参的值4(C#)

由于作者水平有限&#xff0c;如有写得不对的地方&#xff0c;请指正。 使用WinDbg的过程中&#xff0c;坑特别的多&#xff0c;对版本要求比较严格&#xff0c;如&#xff1a; 1 32位应用程序导出的Dump文件要用32位的WinDbg打开&#xff0c;想要没有那么多的问题&#xf…

海底光缆位置探测技术的应用概述

1. 概述 海底光缆运行在地质环境复杂的海洋环境中&#xff0c;地震、海床塌陷、滑坡、洋流变化、海洋生物及船只抛锚都有可能造成光缆断裂、破损&#xff0c;影响光缆的安全运行。海底光缆一旦遭受损坏&#xff0c;其造成的经济损失无法估量。因此在海洋开发工程实施前&#xf…

Web转化为APP——YonBIP(APICloud迁移版)

目录 平台注册 平台使用 设计封面&#xff08;端设置&#xff09; APP证书 代码上传 移动打包 运营管理和移动插件 众所周知&#xff0c;APP开发是一件非常麻烦的事&#xff0c;很多擅长Web开发的人未必擅长APP开发。那么作为一个Web开发者&#xff0c;可不可以有很方便…