云计算百科
云计算领域专业知识百科平台

C++网络编程:TCP服务器与客户端的实现

C++网络编程:TCP服务器与客户端的实现

在这里插入图片描述

一、学习目标与重点

本章将深入探讨C++网络编程的核心知识,帮助你掌握TCP服务器与客户端的实现。通过学习,你将能够:

  • 理解网络编程的基本概念,掌握TCP/IP协议的核心要点
  • 学会使用套接字编程,实现简单的TCP服务器与客户端
  • 理解网络编程中的错误处理,提高程序的健壮性
  • 学会使用Boost.Asio库,简化网络编程的复杂流程
  • 培养网络编程思维,设计高效且稳定的网络应用
  • 二、网络编程基础

    2.1 TCP/IP协议简介

    TCP/IP(传输控制协议/互联网协议)是互联网的核心协议,它提供了可靠的、面向连接的通信服务。TCP协议确保数据的可靠传输,通过三次握手建立连接,四次挥手关闭连接。

    2.2 套接字编程基础

    套接字(Socket)是网络编程的基础,它提供了进程间通信的接口。在C++中,可以使用<sys/socket.h>头文件提供的套接字API进行网络编程。

    三、TCP服务器的实现

    3.1 基本服务器结构

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <arpa/inet.h>

    const int PORT = 8080;
    const int BUFFER_SIZE = 1024;

    int main() {
    std::cout << "=== TCP服务器示例 ===" << std::endl;

    // 1. 创建套接字
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
    std::cerr << "套接字创建失败" << std::endl;
    return 1;
    }

    // 2. 设置套接字选项,允许地址复用
    int opt = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
    std::cerr << "设置套接字选项失败" << std::endl;
    close(server_fd);
    return 1;
    }

    // 3. 绑定地址和端口
    sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
    std::cerr << "绑定地址和端口失败" << std::endl;
    close(server_fd);
    return 1;
    }

    // 4. 监听连接
    if (listen(server_fd, 3) < 0) {
    std::cerr << "监听连接失败" << std::endl;
    close(server_fd);
    return 1;
    }
    std::cout << "服务器正在监听端口 " << PORT << "…" << std::endl;

    // 5. 接受连接
    int addrlen = sizeof(address);
    int new_socket;

    while (true) {
    std::cout << "等待客户端连接…" << std::endl;
    new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
    if (new_socket < 0) {
    std::cerr << "接受连接失败" << std::endl;
    continue;
    }

    char client_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &address.sin_addr, client_ip, INET_ADDRSTRLEN);
    std::cout << "客户端 " << client_ip << " 已连接" << std::endl;

    // 6. 处理连接
    char buffer[BUFFER_SIZE] = {0};
    int valread = read(new_socket, buffer, BUFFER_SIZE);
    if (valread < 0) {
    std::cerr << "读取数据失败" << std::endl;
    close(new_socket);
    continue;
    }

    std::cout << "收到来自客户端的消息: " << buffer << std::endl;

    // 7. 发送响应
    std::string response = "服务器已收到消息: " + std::string(buffer);
    send(new_socket, response.c_str(), response.length(), 0);
    std::cout << "响应已发送" << std::endl;

    // 8. 关闭连接
    close(new_socket);
    std::cout << "客户端连接已关闭" << std::endl;
    }

    // 9. 关闭服务器套接字
    close(server_fd);

    return 0;
    }

    3.2 多线程服务器

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <thread>
    #include <vector>

    const int PORT = 8080;
    const int BUFFER_SIZE = 1024;

    void handleClient(int client_socket, const std::string& client_ip) {
    char buffer[BUFFER_SIZE] = {0};

    while (true) {
    int valread = read(client_socket, buffer, BUFFER_SIZE);
    if (valread < 0) {
    std::cerr << "客户端 " << client_ip << " 读取数据失败" << std::endl;
    break;
    } else if (valread == 0) {
    std::cout << "客户端 " << client_ip << " 已断开连接" << std::endl;
    break;
    }

    std::cout << "收到来自客户端 " << client_ip << " 的消息: " << buffer << std::endl;

    std::string response = "服务器已收到消息: " + std::string(buffer);
    send(client_socket, response.c_str(), response.length(), 0);
    std::cout << "响应已发送给客户端 " << client_ip << std::endl;

    memset(buffer, 0, BUFFER_SIZE);
    }

    close(client_socket);
    }

    int main() {
    std::cout << "=== 多线程TCP服务器示例 ===" << std::endl;

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
    std::cerr << "套接字创建失败" << std::endl;
    return 1;
    }

    int opt = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
    std::cerr << "设置套接字选项失败" << std::endl;
    close(server_fd);
    return 1;
    }

    sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
    std::cerr << "绑定地址和端口失败" << std::endl;
    close(server_fd);
    return 1;
    }

    if (listen(server_fd, 3) < 0) {
    std::cerr << "监听连接失败" << std::endl;
    close(server_fd);
    return 1;
    }
    std::cout << "服务器正在监听端口 " << PORT << "…" << std::endl;

    int addrlen = sizeof(address);
    int new_socket;
    std::vector<std::thread> client_threads;

    while (true) {
    std::cout << "等待客户端连接…" << std::endl;
    new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
    if (new_socket < 0) {
    std::cerr << "接受连接失败" << std::endl;
    continue;
    }

    char client_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &address.sin_addr, client_ip, INET_ADDRSTRLEN);
    std::cout << "客户端 " << client_ip << " 已连接" << std::endl;

    client_threads.emplace_back(handleClient, new_socket, client_ip);
    }

    for (auto& thread : client_threads) {
    if (thread.joinable()) {
    thread.join();
    }
    }

    close(server_fd);

    return 0;
    }

    四、TCP客户端的实现

    4.1 基本客户端结构

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <thread>

    const int PORT = 8080;
    const int BUFFER_SIZE = 1024;
    const std::string SERVER_IP = "127.0.0.1";

    void receiveMessages(int sock) {
    char buffer[BUFFER_SIZE] = {0};
    while (true) {
    int valread = read(sock, buffer, BUFFER_SIZE);
    if (valread < 0) {
    std::cerr << "读取服务器响应失败" << std::endl;
    break;
    } else if (valread == 0) {
    std::cout << "服务器已断开连接" << std::endl;
    break;
    }

    std::cout << "服务器响应: " << buffer << std::endl;
    memset(buffer, 0, BUFFER_SIZE);
    }
    }

    int main() {
    std::cout << "=== TCP客户端示例 ===" << std::endl;

    // 1. 创建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
    std::cerr << "套接字创建失败" << std::endl;
    return 1;
    }

    // 2. 配置服务器地址
    sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    if (inet_pton(AF_INET, SERVER_IP.c_str(), &serv_addr.sin_addr) <= 0) {
    std::cerr << "无效的服务器地址" << std::endl;
    close(sock);
    return 1;
    }

    // 3. 连接服务器
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
    std::cerr << "连接服务器失败" << std::endl;
    close(sock);
    return 1;
    }
    std::cout << "已连接到服务器 " << SERVER_IP << ":" << PORT << std::endl;

    // 4. 接收服务器消息的线程
    std::thread recv_thread(receiveMessages, sock);
    recv_thread.detach();

    // 5. 发送消息
    std::string message;
    while (true) {
    std::cout << "请输入消息(输入\\"quit\\"退出): ";
    std::getline(std::cin, message);

    if (message == "quit") {
    break;
    }

    send(sock, message.c_str(), message.length(), 0);
    }

    // 6. 关闭套接字
    close(sock);

    return 0;
    }

    五、Boost.Asio库的使用

    5.1 Boost.Asio基础

    Boost.Asio是一个跨平台的网络编程库,它简化了网络编程的复杂流程,提供了异步、同步、单线程和多线程的编程模型。

    5.2 使用Boost.Asio实现TCP服务器

    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>

    using boost::asio::ip::tcp;
    using namespace std;

    const int PORT = 8080;

    void handleClient(tcp::socket socket) {
    try {
    char buffer[1024];

    // 读取客户端消息
    size_t len = socket.read_some(boost::asio::buffer(buffer));
    string message(buffer, len);
    cout << "收到客户端消息: " << message << endl;

    // 发送响应
    string response = "服务器已收到消息: " + message;
    boost::asio::write(socket, boost::asio::buffer(response));
    cout << "响应已发送" << endl;
    } catch (const std::exception& e) {
    cerr << "处理客户端连接时出错: " << e.what() << endl;
    }
    }

    int main() {
    cout << "=== Boost.Asio TCP服务器示例 ===" << endl;

    try {
    boost::asio::io_service io_service;

    // 创建acceptor
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), PORT));
    cout << "服务器正在监听端口 " << PORT << "…" << endl;

    while (true) {
    // 接受连接
    tcp::socket socket(io_service);
    acceptor.accept(socket);

    // 处理连接
    thread t(handleClient, move(socket));
    t.detach();
    }
    } catch (const std::exception& e) {
    cerr << "服务器出错: " << e.what() << endl;
    return 1;
    }

    return 0;
    }

    5.3 使用Boost.Asio实现TCP客户端

    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>

    using boost::asio::ip::tcp;
    using namespace std;

    const int PORT = 8080;
    const string SERVER_IP = "127.0.0.1";

    void receiveMessages(tcp::socket& socket) {
    try {
    char buffer[1024];
    while (true) {
    size_t len = socket.read_some(boost::asio::buffer(buffer));
    string response(buffer, len);
    cout << "服务器响应: " << response << endl;
    }
    } catch (const std::exception& e) {
    cerr << "读取服务器响应时出错: " << e.what() << endl;
    }
    }

    int main() {
    cout << "=== Boost.Asio TCP客户端示例 ===" << endl;

    try {
    boost::asio::io_service io_service;

    // 解析服务器地址
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(tcp::v4(), SERVER_IP, to_string(PORT));
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

    // 连接服务器
    tcp::socket socket(io_service);
    boost::asio::connect(socket, endpoint_iterator);
    cout << "已连接到服务器 " << SERVER_IP << ":" << PORT << endl;

    // 接收服务器消息的线程
    thread t(receiveMessages, ref(socket));
    t.detach();

    // 发送消息
    string message;
    while (true) {
    cout << "请输入消息(输入\\"quit\\"退出): ";
    getline(cin, message);

    if (message == "quit") {
    break;
    }

    boost::asio::write(socket, boost::asio::buffer(message));
    }
    } catch (const std::exception& e) {
    cerr << "客户端出错: " << e.what() << endl;
    return 1;
    }

    return 0;
    }

    六、综合案例:实现一个简单的聊天服务器

    6.1 项目结构

    ChatServer/
    ├── include/
    │ └── ChatServer.h
    ├── src/
    │ ├── ChatServer.cpp
    │ └── main.cpp
    └── build/

    6.2 核心代码

    // include/ChatServer.h
    #ifndef CHATSERVER_H
    #define CHATSERVER_H

    #include <iostream>
    #include <string>
    #include <vector>
    #include <thread>
    #include <mutex>
    #include <boost/asio.hpp>

    using boost::asio::ip::tcp;
    using namespace std;

    class ChatServer {
    private:
    boost::asio::io_service io_service;
    tcp::acceptor acceptor;
    vector<shared_ptr<tcp::socket>> clients;
    mutex clients_mutex;

    void acceptConnection();
    void handleClient(shared_ptr<tcp::socket> socket);
    void broadcastMessage(const string& message, const tcp::endpoint& sender_endpoint);

    public:
    ChatServer(int port);
    void run();
    };

    #endif // CHATSERVER_H

    // src/ChatServer.cpp
    #include "ChatServer.h"

    ChatServer::ChatServer(int port) : acceptor(io_service, tcp::endpoint(tcp::v4(), port)) {
    }

    void ChatServer::run() {
    cout << "聊天服务器正在运行…" << endl;
    acceptConnection();
    io_service.run();
    }

    void ChatServer::acceptConnection() {
    shared_ptr<tcp::socket> socket = make_shared<tcp::socket>(io_service);
    acceptor.async_accept(*socket, [this, socket](const boost::system::error_code& ec) {
    if (!ec) {
    cout << "客户端 " << socket->remote_endpoint() << " 已连接" << endl;

    {
    lock_guard<mutex> lock(clients_mutex);
    clients.push_back(socket);
    }

    handleClient(socket);
    } else {
    cerr << "接受连接失败: " << ec.message() << endl;
    }

    acceptConnection();
    });
    }

    void ChatServer::handleClient(shared_ptr<tcp::socket> socket) {
    auto endpoint = socket->remote_endpoint();
    char buffer[1024];

    socket->async_read_some(boost::asio::buffer(buffer), [this, socket, endpoint](const boost::system::error_code& ec, size_t bytes_transferred) {
    if (!ec) {
    string message(buffer, bytes_transferred);
    cout << "收到来自 " << endpoint << " 的消息: " << message << endl;
    broadcastMessage(message, endpoint);
    handleClient(socket);
    } else {
    cout << "客户端 " << endpoint << " 已断开连接" << endl;

    {
    lock_guard<mutex> lock(clients_mutex);
    clients.erase(remove(clients.begin(), clients.end(), socket), clients.end());
    }
    }
    });
    }

    void ChatServer::broadcastMessage(const string& message, const tcp::endpoint& sender_endpoint) {
    string full_message = sender_endpoint.address().to_string() + ":" + to_string(sender_endpoint.port()) + " 说: " + message;

    {
    lock_guard<mutex> lock(clients_mutex);
    for (auto socket : clients) {
    if (socket->remote_endpoint() != sender_endpoint) {
    boost::asio::async_write(*socket, boost::asio::buffer(full_message), [](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
    if (ec) {
    cerr << "发送消息失败: " << ec.message() << endl;
    }
    });
    }
    }
    }
    }

    // src/main.cpp
    #include <iostream>
    #include "ChatServer.h"

    int main() {
    std::cout << "=== 简单的聊天服务器 ===" << std::endl;

    try {
    const int PORT = 8080;
    ChatServer server(PORT);
    server.run();
    } catch (const std::exception& e) {
    std::cerr << "服务器出错: " << e.what() << std::endl;
    return 1;
    }

    return 0;
    }

    6.3 项目构建与运行

    # 创建构建目录
    mkdir -p build && cd build

    # 配置CMake
    cmake -DCMAKE_BUILD_TYPE=Release ..

    # 编译项目
    cmake –build . –config Release

    # 运行服务器
    ./ChatServer

    # 在另一个终端运行客户端
    ./ChatClient

    七、总结与练习

    7.1 本章总结

    本章介绍了C++网络编程的核心知识,包括:

  • 网络编程的基本概念与TCP/IP协议
  • 套接字编程基础
  • TCP服务器的实现
  • 多线程服务器的实现
  • TCP客户端的实现
  • Boost.Asio库的使用
  • 综合案例:实现一个简单的聊天服务器
  • 7.2 练习题

  • 写一个程序,使用套接字编程实现一个简单的HTTP服务器。
  • 编写一个函数,使用Boost.Asio库实现一个简单的FTP客户端。
  • 写一个程序,使用多线程实现一个高并发的网络服务器。
  • 实现一个类,使用Boost.Asio库实现一个简单的邮件服务器。
  • 写一个程序,使用网络编程实现一个简单的实时数据传输系统。
  • 7.3 进阶挑战

  • 研究如何使用C++的无锁数据结构优化网络服务器的性能。
  • 学习如何使用C++的协程(C++20及以后)与网络编程结合。
  • 研究如何使用C++的并发编程优化一个大型网络应用。
  • 学习如何使用C++的加密库(如OpenSSL)实现安全的网络通信。
  • 研究如何使用C++的网络编程实现一个高可用性的分布式系统。
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » C++网络编程:TCP服务器与客户端的实现
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!