首页 > 编程语言 >学懂C++(三十七):深入详解C++网络编程开发

学懂C++(三十七):深入详解C++网络编程开发

时间:2024-08-19 13:24:45浏览次数:12  
标签:std addr 编程 三十七 server 学懂 client C++ 接字

        

目录

一、网络编程基础概念与原理

1.1 套接字(Socket)

1.2 IP地址和端口

1.3 TCP/IP协议

二、C++网络编程核心技术

2.1 套接字编程

2.1.1 创建套接字

2.1.2 绑定地址

2.1.3 监听和接受连接

2.1.4 发送和接收数据

三、C++网络编程高级技术

3.1 异步I/O

3.2 多线程网络编程

3.3 Boost.Asio库

运行结果

总结


        网络编程是现代软件开发的重要组成部分,尤其在开发分布式系统、客户端-服务器应用以及互联网应用时尤为关键。C++作为一门强大且高效的编程语言,在网络编程中也有广泛应用。本文将深入阐述C++网络编程的概念、原理、本质及需要掌握的核心技术,并通过经典实例进行详细讲解。

一、网络编程基础概念与原理

1.1 套接字(Socket)

概念:套接字是网络编程中的基本抽象,用于描述网络连接的端点。它本质上是一个文件描述符,用于在网络中进行数据传输。

原理:套接字提供了应用层与传输层之间的接口,使得操作系统能够管理网络通信。套接字API包括创建、绑定、监听、连接、发送和接收数据等操作。

核心点

  • Socket类型:常见的有流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM),分别对应TCP和UDP协议。
  • Socket地址:包含IP地址和端口号,用于标识网络中的设备和应用。
1.2 IP地址和端口

IP地址:标识网络中的主机,例如IPv4(如192.168.1.1)和IPv6(如2001:db8::1)地址。

端口号:标识主机上的应用程序,范围为0到65535。常见的端口号如HTTP的80端口、HTTPS的443端口等。

1.3 TCP/IP协议

概念:TCP/IP协议族是互联网的基础协议,包含传输层的TCP和UDP、网络层的IP等。

原理

  • TCP:提供可靠的字节流服务,包括连接建立、数据传输、连接终止等过程。TCP通过三次握手建立连接,四次挥手终止连接。
  • UDP:提供不可靠的消息传递服务,数据报可能无序到达或丢失。UDP适用于实时性要求较高的应用,如视频传输、在线游戏等。

二、C++网络编程核心技术

2.1 套接字编程
2.1.1 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
    perror("socket creation failed");
    exit(EXIT_FAILURE);
}

解析

  • socket(AF_INET, SOCK_STREAM, 0):创建一个TCP套接字。AF_INET表示使用IPv4,SOCK_STREAM表示使用TCP协议。
  • 返回值:成功时返回套接字文件描述符,失败时返回-1。
2.1.2 绑定地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);

if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
    perror("bind failed");
    close(sockfd);
    exit(EXIT_FAILURE);
}

解析

  • struct sockaddr_in:定义IP地址和端口,sin_family设为AF_INETsin_addr.s_addr设为INADDR_ANY表示接受任何IP地址,sin_port设为端口(通过htons函数转换为网络字节序)。
  • bind:将地址绑定到套接字。
2.1.3 监听和接受连接
if (listen(sockfd, 5) < 0) {
    perror("listen failed");
    close(sockfd);
    exit(EXIT_FAILURE);
}

struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int newsockfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (newsockfd < 0) {
    perror("accept failed");
    close(sockfd);
    exit(EXIT_FAILURE);
}

解析

  • listen:将套接字置于监听模式,准备接受连接。第二个参数为连接队列的最大长度。
  • accept:接受客户端连接,返回新的套接字用于通信。client_addr保存客户端地址信息。
2.1.4 发送和接收数据
const char* message = "Hello, Client!";
send(newsockfd, message, strlen(message), 0);

char buffer[1024];
int n = recv(newsockfd, buffer, sizeof(buffer), 0);
buffer[n] = '\0';
printf("Received: %s\n", buffer);

解析

  • send:发送数据,第四个参数为标志位,通常为0。
  • recv:接收数据,返回接收的字节数。

三、C++网络编程高级技术

3.1 异步I/O

概念:异步I/O允许程序在等待I/O操作完成时继续执行其他任务,提高程序的并发性能。

实现方式

  • 多线程:每个I/O操作分配一个线程处理。
  • 事件驱动:使用事件循环处理I/O事件,例如selectpollepoll等。
3.2 多线程网络编程

核心点

  • 线程同步:避免多个线程同时访问共享资源,使用互斥锁、条件变量等。
  • 线程池:复用线程,减少线程创建和销毁的开销,提高性能。

实例:使用线程池处理多个客户端连接。

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define PORT 8080

std::mutex mtx;  // 互斥锁用于保护共享资源
std::condition_variable cv;  // 条件变量用于线程间同步
std::queue<int> clients;  // 用于存储待处理的客户端套接字

// 处理客户端连接的函数
void handle_client(int client_sock) {
    char buffer[1024];
    int n = recv(client_sock, buffer, sizeof(buffer), 0);  // 接收客户端消息
    if (n > 0) {
        buffer[n] = '\0';  // 添加字符串终止符
        std::cout << "Received: " << buffer << std::endl;
        const char* response = "Message received";
        send(client_sock, response, strlen(response), 0);  // 发送响应给客户端
    }
    close(client_sock);  // 关闭客户端连接
}

// 工作线程函数,用于处理客户端连接
void worker() {
    while (true) {
        int client_sock;
        {
            std::unique_lock<std::mutex> lock(mtx);  // 获取锁
            cv.wait(lock, [] { return !clients.empty(); });  // 等待条件变量通知
            client_sock = clients.front();  // 从队列中取出客户端套接字
            clients.pop();  // 移除队列中的套接字
        }
        handle_client(client_sock);  // 处理客户端连接
    }
}

int main() {
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);  // 创建TCP套接字
    if (server_sock < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;  // 使用IPv4
    server_addr.sin_addr.s_addr = INADDR_ANY;  // 绑定所有可用的网络接口
    server_addr.sin_port = htons(PORT);  // 绑定端口

    if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        close(server_sock);
        exit(EXIT_FAILURE);
    }

    if (listen(server_sock, 5) < 0) {  // 设置监听队列的最大长度为5
        perror("listen failed");
        close(server_sock);
        exit(EXIT_FAILURE);
    }

    // 创建多个工作线程
    std::vector<std::thread> workers;
    for (int i = 0; i < 4; ++i) {
        workers.emplace_back(worker);  // 启动工作线程
    }

    while (true) {
        struct sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
        int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_len);  // 接受客户端连接
        if (client_sock < 0) {
            perror("accept failed");
            continue;
        }
        {
            std::lock_guard<std::mutex> lock(mtx);  // 获取锁
            clients.push(client_sock);  // 将客户端套接字添加到队列中
        }
        cv.notify_one();  // 通知一个工作线程
    }

    for (auto& t : workers) {
        t.join();  // 等待所有工作线程结束
    }

    close(server_sock);  // 关闭服务器套接字
    return 0;
}

解析

  • 线程池:创建多个工作线程,等待处理客户端连接。
  • 同步机制:使用互斥锁和条件变量同步对客户端队列的访问。
3.3 Boost.Asio库

概念:Boost.Asio是一个高效的C++网络编程库,提供了同步和异步I/O操作,支持多平台。

核心点

  • I/O对象:如ip::tcp::socketip::udp::socket等。
  • I/O操作:如async_readasync_write等,支持异步操作。
  • 事件循环:通过io_context管理异步操作。

实例:使用Boost.Asio实现异步TCP服务器。

#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <utility>

using boost::asio::ip::tcp;

// 会话类,用于管理单个客户端连接
class Session : public std::enable_shared_from_this<Session> {
public:
    explicit Session(tcp::socket socket) : socket_(std::move(socket)) {}

    // 开始会话
    void start() {
        do_read();  // 开始读取数据
    }

private:
    // 异步读取数据
    void do_read() {
        auto self(shared_from_this());
        socket_.async_read_some(boost::asio::buffer(data_, max_length),
            [this, self](boost::system::error_code ec, std::size_t length) {
                if (!ec) {
                    std::cout << "Received: " << data_ << std::endl;
                    do_write(length);  // 读取完成后写入数据
                }
            });
    }

    // 异步写入数据
    void do_write(std::size_t length) {
        auto self(shared_from_this());
        boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
            [this, self](boost::system::error_code ec, std::size_t /*length*/) {
                if (!ec) {
                    do_read();  // 写入完成后继续读取数据
                }
            });
    }

    tcp::socket socket_;  // 套接字
    enum { max_length = 1024 };
    char data_[max_length];  // 数据缓冲区
};

// 服务器类,用于管理客户端连接
class Server {
public:
    Server(boost::asio::io_context& io_context, short port)
        : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) {
        do_accept();  // 开始接受连接
    }

private:
    // 异步接受连接
    void do_accept() {
        acceptor_.async_accept(
            [this](boost::system::error_code ec, tcp::socket socket) {
                if (!ec) {
                    std::make_shared<Session>(std::move(socket))->start();
                }
                do_accept();  // 继续接受下一次连接
            });
    }

    tcp::acceptor acceptor_;  // 接受器,用于监听连接
};

int main(int argc, char* argv[]) {
    try {
        if (argc != 2) {
            std::cerr << "Usage: async_tcp_echo_server <port>\n";
            return 1;
        }

        boost::asio::io_context io_context;  // IO上下文
        Server s(io_context, std::atoi(argv[1]));  // 创建服务器
        io_context.run();  // 运行IO上下文
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

运行结果

在终端运行服务器实例:

./BoostServer 8080

然后在另一个终端使用telnet连接服务器:

telnet localhost 8080

输入消息时,服务器将回显收到的消息。

输出结果

Connected to localhost.
Escape character is '^]'.
Hello, Server!
Received: Hello, Server!

总结

        本文详细阐述了C++网络编程的基本概念、原理和核心技术,结合实际项目示例,涵盖了多线程处理多个客户端连接和使用Boost.Asio库进行异步编程的实现方式。通过这些技术和示例代码,高级C++开发者可以深入理解和掌握C++网络编程的关键技术,为开发高效、可扩展的网络应用打下坚实的基础。希望这些代码和注释能够帮助你更好地理解和应用C++网络编程。

标签:std,addr,编程,三十七,server,学懂,client,C++,接字
From: https://blog.csdn.net/martian665/article/details/141322633

相关文章

  • 学懂C++(三十八):深入详解C++网络编程:套接字(Socket)开发技术
    目录一、概述与基础概念1.1套接字(Socket)概念1.2底层原理与网络协议1.2.1网络协议1.2.2套接字工作原理二、C++套接字编程核心技术2.1套接字编程的基本步骤2.2套接字编程详细实现2.2.1创建套接字2.2.2绑定地址2.2.3监听和接受连接(服务端)2.2.4客户端连接2.......
  • C++实现设计模式——Builder模式
    C++实现设计模式——Builder模式建造者模式定义建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品......
  • Linux C++ 开发4 - 入门makefile一篇文章就够了
    1.make和Makefile1.1.什么是make?1.2.什么是Makefile?1.3.make与Makefile的关系2.Makefile的语法2.1.基本语法2.2.变量2.3.伪目标2.4.模式规则2.5.自动变量2.6.条件判断3.示例演示3.1.编译HelloWorld程序3.2.编译多文件项目3.2.1.项目......
  • C++-练习-21
     题目:编写一个程序,它要求用户输入其名,然后输入其姓。然后程序使用一个逗号和空格将姓和名组合起来,并存储和显示结果。请使用string对象和头文string中的函数。源代码:#define_CRT_SECURE_NO_WARNINGS //vs版本不加这个无法使用strcat等函数#include<iostream>#include......
  • C++-练习-22
    题目:结构CandyBar包含3个成员,第一个成员存储了糖块的品牌;第二个成员存储糖块的重量(可以有小数);第三个成员存储了糖块的卡路里含量(整数)。创建一个包含3个元素的CandyBar数组(使用new来动态分配数组),并将它们初始化为所选择的值,然后显示每个结构的内容源代码:#define_CRT_SECURE_......
  • C++ 各种初始化方法总结
    在各种编程语言中,初始化都是非常重要的步骤,用于确保对象在使用前具有确定的初始状态。C++提供了多种初始化方法,每种方法都有其特定的使用场景和注意事项。以下是一些主要的初始化方法及其注意事项:默认初始化(Default-initialization):形如Tobj、newT等方式的初始化,其中T为类......
  • 关于c++使用toml plusplus(俗称toml++)的使用(4)
    链接toml++-githubtoml++-帮助文档使用要求:c++17及以上版本toml语法-英文toml语法-中文toml读取参见官方给出的范例toml写入目标:表嵌套子表数组的写入比如:文件内容[NET_INTERFACE]bool=falsebool_arr=[false,false]complex_arr......
  • 关于c++使用toml plusplus(俗称toml++)的使用(3)
    链接toml++-githubtoml++-帮助文档使用要求:c++17及以上版本toml语法-英文toml语法-中文toml读取参见官方给出的范例toml写入目标:数组的写入文件内容[NET_INTERFACE]bool=falsebool_arr=[false,false]complex_arr=[false,'456'......
  • C++——new对象
    new对象与之前C的"类对象"方式有所不同,"类对象"方式并不会调用构造函数和析构函数,而new对象则会调用两个函数,释放该空间时用delete。数组申请int类型的数组#define_CRT_SECURE_NO_WARNINGS#include<iostream>usingnamespacestd;intmain(){ int*a=newint[10];......
  • C++中的多线程编程和锁机制
    二、多线程、锁2.1C语言线程库pthread(POSIXthreads)2.2.1线程创建pthread_create#include<pthread.h>pthread_tthread;ThreadDataargs={1,"Hellofromparameterizedthread"};intresult=pthread_create(&thread,attr,function,args); //线程创建即......