Linux 文件操作与 Socket 编程核心知识详解

news/2025/2/27 5:32:12

Linux 文件操作与 Socket 编程核心知识详解

一、Linux 文件与 Socket 的统一性

1.1 核心设计理念

在 Linux 系统中,秉持"一切皆文件"的设计理念:

  • 所有 I/O 设备(常规文件、网络 socket、外设等)均被抽象为文件
  • 统一通过文件描述符(File Descriptor)进行管理
  • 使用相同的系统调用接口(open/read/write/close)

1.2 统一操作接口

操作类型文件操作示例Socket 操作示例
打开资源open("file.txt", ...)socket(AF_INET, ...)
写入数据write(fd, buf, len)write(sockfd, buf, len)
读取数据read(fd, buf, len)read(sockfd, buf, len)
关闭资源close(fd)close(sockfd)

1.3 文件描述符机制

// 典型文件操作流程
int fd = open("data.txt", O_RDWR);  // 返回文件描述符
write(fd, buffer, sizeof(buffer));
close(fd);

// Socket 创建示例
int sockfd = socket(AF_INET, SOCK_STREAM, 0);  // 返回 socket 描述符

二、Linux 底层文件操作

2.1 关键系统调用函数

函数参数说明返回值典型应用场景
open()(路径, 标志位, 权限)
例:`O_CREAT
O_RDWR, 0644`文件描述符(成功)/-1(失败)
write()(fd, 数据指针, 长度)
例:write(fd, "Hello", 5)
写入字节数/-1文件写入/网络发送
read()(fd, 缓冲区, 长度)
例:read(fd, buf, 1024)
读取字节数/-1文件读取/网络接收
close()(文件描述符)
例:close(fd)
0/-1释放资源

2.2 文件描述符分配规则

分配原则:选择当前最小可用描述符

默认描述符

#define STDIN_FILENO  0  // 标准输入(键盘)
#define STDOUT_FILENO 1  // 标准输出(屏幕)
#define STDERR_FILENO 2  // 标准错误(屏幕)

实验验证

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
    close(0);  // 关闭标准输入
    close(1);  // 关闭标准输出
    close(2);  // 关闭标准错误
    
    int fd = open("test.txt", O_CREAT | O_RDWR, 0666);
    printf("File descriptor: %d\n", fd);  // 输出到文件(因为stdout已关闭)
    
    close(fd);
    return 0;
}

运行结果:文件描述符将分配为 0

三、进程文件描述符管理

3.1 系统查看方法

# 查看进程描述符
ls -l /proc/<PID>/fd

# 示例输出
lrwx------ 1 user user 64 Aug 10 10:00 0 -> /dev/pts/1
lrwx------ 1 user user 64 Aug 10 10:00 3 -> /tmp/test.data

3.2 标准 I/O 流关系

文件描述符C++ 对象C 函数指针设备关联
0cinstdin键盘输入
1coutstdout屏幕输出
2cerrstderr错误输出

四、网络编程与文件操作的结合

4.1 TCP 通信示例

服务端代码

#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8888
#define BUFFER_SIZE 1024

int main() {
    // 创建 socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 绑定地址
    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(PORT),
        .sin_addr.s_addr = INADDR_ANY
    };
    bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    
    // 监听连接
    listen(sockfd, 5);
    
    // 接受连接
    int client_fd = accept(sockfd, NULL, NULL);
    
    // 数据交换
    char buffer[BUFFER_SIZE];
    ssize_t bytes = read(client_fd, buffer, sizeof(buffer));
    write(client_fd, "ACK", 3);
    
    close(client_fd);
    close(sockfd);
    return 0;
}

客户端代码

#include <sys/socket.h>
#include <arpa/inet.h>

int main() {
    // 创建 socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 连接服务器
    struct sockaddr_in serv_addr = {
        .sin_family = AF_INET,
        .sin_port = htons(8888),
        .sin_addr.s_addr = inet_addr("127.0.0.1")
    };
    connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    
    // 发送数据
    write(sockfd, "Hello", 5);
    
    // 接收响应
    char buffer[1024];
    read(sockfd, buffer, sizeof(buffer));
    
    close(sockfd);
    return 0;
}

五、C++ 流与底层操作的关系

5.1 流对象底层实现

#include <fstream>
#include <unistd.h>

int main() {
    std::ofstream file("data.txt");
    if(file.is_open()) {
        int fd = fileno(file.rdbuf());  // 获取底层描述符
        write(fd, "Direct write\n", 12);  // 混合使用流和系统调用
        file << "Stream write\n";
        file.close();
    }
    return 0;
}

5.2 性能对比

操作方式优点缺点适用场景
系统调用无额外开销,高效需手动管理缓冲区高性能需求、底层开发
C++ 流类型安全、异常处理有封装开销常规开发、快速原型

六、错误处理指南

6.1 常见错误码

错误码描述解决方案
EBADF无效文件描述符检查描述符是否已关闭
EINTR系统调用被信号中断重启被中断的系统调用
ENFILE系统文件表溢出检查资源泄漏,关闭无用描述符

6.2 错误处理示例

ssize_t ret = write(fd, buf, len);
if(ret == -1) {
    if(errno == EINTR) {
        // 重新尝试写入
        ret = write(fd, buf, len);
    }
    else if(errno == ENOSPC) {
        fprintf(stderr, "Disk full!\n");
    }
}

七、最佳实践建议

  1. 资源管理

    // RAII 式资源管理
    class FileHandle {
    public:
        FileHandle(const char* path) : fd(open(path, O_RDWR)) {}
        ~FileHandle() { if(fd != -1) close(fd); }
        // ...其他方法
    private:
        int fd;
    };
    
  2. 性能优化

    // 使用 sendfile 零拷贝传输
    #include <sys/sendfile.h>
    
    int out_fd = open("output", O_WRONLY);
    int in_fd = open("input", O_RDONLY);
    off_t offset = 0;
    sendfile(out_fd, in_fd, &offset, file_size);
    
  3. 调试技巧

    # 跟踪文件操作
    strace -e trace=open,close,read,write ./program
    
    # 监控描述符泄漏
    valgrind --track-fds=yes ./program
    

附录:文件标志位速查表

标志位描述
O_RDONLY只读模式
O_WRONLY只写模式
O_RDWR读写模式
O_CREAT文件不存在时创建
O_APPEND追加模式
O_TRUNC打开时清空文件内容
O_SYNC同步写入(直接刷盘)
O_NONBLOCK非阻塞模式

本手册完整展示了 Linux 文件系统与网络编程的核心机制,结合示例代码和实用技巧,可帮助开发者深入理解系统级 I/O 操作,编写高性能、高可靠性的应用程序。


http://www.niftyadmin.cn/n/5869520.html

相关文章

Java HTTP 请求的四种实现方式:Apache HttpClient、OkHttp、WebClient 与 Java 11 HttpClient

目录 1.使用Java11内置的HttpClient 2.使用OkHttp 3.使用SpringWebClient 4.使用ApacheHttpClient5.x 在现代Java开发中&#xff0c;HttpClient和PostMethod是较旧的ApacheHttpClient3.x的API。推荐使用更现代的技术来替代这些代码&#xff0c;例如Java11内置的HttpClient或…

ubuntu配置jmeter

1.前提准备 系统 ubuntu server 22.04 前提条件&#xff1a;服务器更新apt与安装lrzsz&#xff1a;更新apt&#xff1a; sudo apt update安装lrzsz: 命令行下的上传下载文件工具 sudo apt install lrzszsudo apt install zip2.安装jemeter 2.1.下载jdk17 输入命令&#xf…

网络渗透作业

第一题&#xff1a;使用Xpath对Order by 语句进行布尔盲注 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml&quo…

游戏引擎学习第125天

仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾并为今天的内容做准备。 昨天&#xff0c;当我们离开时&#xff0c;工作队列已经完成了基本的功能。这个队列虽然简单&#xff0c;但它能够执行任务&#xff0c;并且我们已经为各种操作编写了测试。字符串也能够正常推送到队…

初会学习记录

【25初级会计《实务》】第一章&#xff1a;权责发生制举例_哔哩哔哩_bilibili 务实&#xff1a; 第一章 (1)会计概念&#xff0c;职能和目标&#xff1a; 2025年2月25日&#xff1a; (2)会计假设&#xff1a; 2025年2月26日&#xff1a; (3)会计核算基础&#xff1a; 202…

centos和ubuntu安装mysql教程

1&#xff0c;安装包准备 cd /usr/local/ mkdir mysql cd mysql 将压缩包复制进mysql文件夹下 tar -xvf mysql-8.0.21-linux-glibc2.12-x86_64.tar.xz mv mysql-8.0.21-linux-glibc2.12-x86_64 mysql8 cd /usr/local/mysql mkdir data 2&#xff0c;创建用户组以及用户密码&…

ComfyUI:Stable Diffusion 及 LoRA、VAE 、ControlNet模型解析

目录 Stable Diffusion流程 扩散过程 去噪过程 checkpoints LoRA LoRA 位置与结构 LoRA 层与原层的关系 LoRA 层的参数拆解 VAE 训练特定 VAE 时更新的参数部分 ControlNet ControlNet 位置与结构 ControlNet 的训练过程 ControlNet 的参数处理与信息融合 Contr…

Python 编程题 第二节:组合数字、乘法口诀表、水仙花数、反向输出四位数、判断三角形

组合数字 1-4不重复组成三位数&#xff0c;利用集合的去重 lst[] for i in range(1,5):for j in range(1,5):for m in range(1,5):s{i,j,m}if len(s)3:lst.append(i*100j*10m) print(lst) 乘法口诀表 修改换行符 for i in range(1,10):for j in range(1,i1):print(f"…