day-08 基于Linux的网络编程(套接字和标准I/O、分离I/O流、epoll、多线程服务器)

一.套接字和标准 I/O

(一)标准 I/O 函数的优点

标准I/O函数(stdio)是在C语言中用于进行输入和输出操作的库函数。它们包括了一组标准的输入和输出函数,如printf、scanf、fopen、fclose等。标准I/O函数具有以下优点:

  1. 简单易用:标准I/O函数提供了简洁的接口,使得输入和输出操作变得简单易用。开发人员无需自行处理底层的文件或设备操作,而是直接使用高级的函数调用来完成输入和输出任务。

  2. 跨平台性:标准I/O函数是C语言的标准库函数,在不同的操作系统上都可以使用。这意味着编写的代码可以在不同的平台上进行移植,而无需对底层的操作系统接口进行修改。

  3. 缓冲机制:标准I/O函数通过使用内部缓冲区来提高输入和输出的效率。当从文件读取数据或向文件写入数据时,数据会首先存储在内部缓冲区中,然后才会实际读取或写入到文件中。这种缓冲机制可以减少对文件的频繁访问,提高了程序的性能。

  4. 格式化输入输出:标准I/O函数提供了格式化输入输出的功能,例如printf函数可以按照指定的格式将数据输出到终端或文件中,而scanf函数可以按照指定的格式从终端或文件中读取数据。这样可以方便地对输入输出进行格式控制,使得代码更加清晰和易读。

  5. 支持多种类型:标准I/O函数支持多种不同的数据类型进行输入和输出操作,包括整数、浮点数、字符串等。开发人员可以根据需要选择适合的函数来处理不同类型的数据,提高了灵活性和可扩展性。

总之,标准I/O函数具有简单易用、跨平台性、缓冲机制、格式化输入输出和多种类型支持等优点,是进行输入和输出操作的常用工具之一。但在某些情况下,如果需要更底层的控制或对性能要求较高,可能需要使用更低级别的I/O函数或系统调用。

在C++中,标准I/O函数(iostream)是用于进行输入和输出操作的库函数。它们包括了一组标准的输入和输出类,如std::cout、std::cin、std::ifstream、std::ofstream等。与C语言中的标准I/O函数相比,C++标准I/O函数具有以下优点:

  1. 面向对象:C++标准I/O函数是面向对象的,使用类和对象来进行输入和输出操作。这使得代码更加模块化和可扩展,可以通过继承和多态等特性实现更复杂的功能。

  2. 类型安全:C++标准I/O函数提供了类型安全的输入和输出操作。它们对不同的数据类型提供了适当的重载,以确保正确的数据转换和格式化输出。这可以避免潜在的类型错误和内存溢出问题。

  3. 重定向支持:C++标准I/O函数支持流的重定向,可以将输入和输出重定向到文件、字符串、网络套接字等不同的设备。这使得代码具有更大的灵活性,可以方便地实现日志记录、测试和调试等功能。

  4. 操作符重载:C++标准I/O函数通过运算符重载提供了更直观和简洁的语法。例如,可以使用<<运算符将数据输出到流中,使用>>运算符从流中读取数据。这使得代码更加易读和易写。

  5. 异常处理:C++标准I/O函数使用异常来处理错误和异常情况。当发生错误时,它们可以抛出适当的异常,从而提供更好的错误处理和异常处理机制。这有助于编写更健壮和可靠的代码。

总之,C++标准I/O函数具有面向对象、类型安全、重定向支持、操作符重载和异常处理等优点。它们提供了一种高级且方便的方式来进行输入和输出操作,适用于大多数网络编程和应用场景。然而,在某些特殊情况下,如果需要更底层的控制或对性能要求较高,可能需要使用更底层的网络库或系统调用。

(二)使用标准 I/O 函数

1.fdopen转换为 FILE 结构体指针

#include <iostream>
#include <cstdio>

int main() {
    int fd = open("example.txt", O_WRONLY | O_CREAT, 0666);  // 打开文件获取文件描述符
    if (fd == -1) {
        perror("Failed to open file");
        return 1;
    }

    FILE* filePtr = fdopen(fd, "w");  // 将文件描述符转换为FILE结构体指针
    if (filePtr == NULL) {
        perror("Failed to convert file descriptor");
        close(fd);
        return 1;
    }

    fprintf(filePtr, "Hello, World!");  // 使用标准I/O函数向文件中写入数据

    fclose(filePtr);  // 关闭文件指针
    return 0;
}

2.fileno 函数转换为文件描述符

cpp 复制代码
#include <iostream>
#include <cstdio>

int main() {
    std::FILE* filePtr = std::fopen("example.txt", "r");  // 打开文件获取文件指针
    if (filePtr == nullptr) {
        perror("Failed to open file");
        return 1;
    }

    int fd = fileno(filePtr);  // 将文件指针转换为文件描述符

    // 使用文件描述符进行其他操作...

    std::fclose(filePtr);  // 关闭文件指针
    return 0;
}

3.iostream

通过使用 iostream,可以直接使用类似于**std::ifstreamstd::ofstream**的对象来进行文件的读写操作,无需显式地处理文件描述符。这样可以更好地利用 C++ 的面向对象特性,使代码更简洁、可读性更好,并且具有更好的类型安全性和异常处理机制。

cpp 复制代码
#include <iostream>
#include <fstream>

int main() {
    std::ofstream file("example.txt");  // 打开文件进行写入操作

    if (!file) {
        std::cerr << "Failed to open file" << std::endl;
        return 1;
    }

    file << "Hello, World!";  // 向文件中写入数据

    file.close();  // 关闭文件

    std::ifstream inputFile("example.txt");  // 打开文件进行读取操作

    if (!inputFile) {
        std::cerr << "Failed to open file" << std::endl;
        return 1;
    }

    std::string content;
    inputFile >> content;  // 从文件中读取数据

    inputFile.close();  // 关闭文件

    std::cout << "Read from file: " << content << std::endl;

    return 0;
}

(三)基于套接字的标准 I/O 函数使用(C++ iostream)

注意:关联套接字和iostream仅适用于流式套接字,报文套接字和原始套接字需要用到相应的套接字API。

1.ios_server.cpp

cpp 复制代码
 

2.ios_client.cpp

cpp 复制代码
 

3.流式套接字

流式套接字(stream socket)是一种在网络通信中常用的套接字类型,它提供了可靠的、基于字节流的双向数据传输

流式套接字使用TCP(Transmission Control Protocol)作为底层传输协议,它提供了以下特性:

  • 可靠性:TCP使用确认和重传机制来确保数据可靠地传输,以及流量控制和拥塞控制来调整发送速率。
  • 顺序性:TCP保证数据按照发送的顺序到达目标,不会发生乱序。
  • 面向连接:在进行数据传输之前,需要在客户端和服务器之间建立连接,通过三次握手来建立可靠的通信链路。
  • 全双工通信:流式套接字允许同时进行读取和写入操作,实现双向通信。
  • 高效性:TCP使用滑动窗口机制和缓冲区来优化数据传输效率。

使用流式套接字进行网络通信可以实现可靠的、面向连接的通信,适合对数据的完整性和顺序有严格要求的场景,如文件传输、视频流传输等。

在C++中,使用流式套接字进行网络通信通常会结合std::ostreamstd::istream类以及套接字API来进行输入输出操作。通过将套接字与iostream对象关联,可以使用C++标准库提供的高级输入输出函数和操作符来进行方便的数据传输。

4.报文套接字

报文套接字(datagram socket)和原始套接字(raw socket)是在网络通信中使用的两种特殊类型的套接字。

  • 报文套接字:报文套接字基于数据报协议(如UDP),它提供了一种无连接、不可靠的传输方式。通过报文套接字发送的数据以离散的数据报形式传输,每个数据报都包含了完整的目标地址和数据。报文套接字适用于那些不需要建立连接和保证可靠性的应用场景,如实时音视频传输、广播等。

在C++中,可以使用socket()函数创建一个报文套接字,并使用sendto()recvfrom()函数进行数据的发送和接收。

5.原始套接字

  • 原始套接字:原始套接字提供了对网络协议的底层访问,它允许应用程序通过自定义的协议头部来发送和接收数据。使用原始套接字,可以直接操作IP层、传输层或更低层的协议头部,实现更高级别的网络功能。原始套接字一般需要在特权模式(如管理员权限)下运行。

在C++中,可以使用socket()函数创建一个原始套接字,并使用sendto()recvfrom()函数进行数据的发送和接收。

需要注意的是,使用原始套接字需要谨慎,因为它可以绕过网络协议栈的一些安全机制,可能造成网络攻击或不当操作。在实际应用中,使用原始套接字需要具备足够的网络知识和相应的权限,并且应遵循相关的法律和规定。

(四)总结

二.分离 I/O 流

(一)分离 I/O 流

(二)文件描述符的复制和半关闭

(三)总结

三.优于select的epoll

(一)epoll 理解和应用

(二)条件触发和边缘触发

(三)总结

四.多线程服务器端的实现

(一)线程概念

(二)线程创建及运行

(三)线程存在的问题和临界区

(四)线程同步

(五)线程的销毁和多线程并发服务器端的实现

五.制作HTTP服务器端

相关推荐
xuanyu2230 分钟前
Linux常用指令
linux·运维·人工智能
Ylucius34 分钟前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
KookeeyLena51 小时前
云手机可以挂在服务器使用吗?
运维·服务器·智能手机
是店小二呀1 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
ephemerals__1 小时前
【c++】动态内存管理
开发语言·c++
老汉忒cpp1 小时前
手动部署并测试内网穿透(ssh 和 nginx)
运维·服务器
有时间要学习1 小时前
Linux——应用层自定义协议与序列化
linux·服务器·网络
CVer儿1 小时前
条件编译代码记录
开发语言·c++
hardStudy_h1 小时前
Linux——常用系统设置和快捷键操作指令
linux·运维·服务器
程序猿练习生2 小时前
C++速通LeetCode简单第18题-杨辉三角(全网唯一递归法)
c++·算法·leetcode