当前位置: 首页 > article >正文

c++开发实战之网络编程(一)

C++ 网络编程可以使用多种库和技术,可以使用标准的 Berkeley Sockets API 来编写跨平台的 C++ 网络编程代码。通过使用不同的系统头文件和函数,我们可以实现一个 Linux 和 Windows 兼容的客户端和服务器。以下示例展示了如何使用 Berkeley Sockets 在 Linux 和 Windows 上实现 TCP 通信。

在 Windows 上,你需要使用 Winsock2,而在 Linux 上使用标准的 POSIX Sockets API。

1. 编写跨平台代码的注意事项

在编写跨平台代码时,主要需要处理以下问题:

  • 头文件:Linux 和 Windows 的头文件不同,Windows 需要包含 Winsock2.h 和初始化 Winsock
  • 网络库的链接:在 Windows 上需要链接 Ws2_32.lib
  • 系统特有函数:如 close()closesocket() 的差异。

2. 服务器代码(支持 Linux 和 Windows)

#include <iostream>
#include <cstring>

// 处理跨平台的头文件和函数差异
#ifdef _WIN32
    #include <winsock2.h>
    #pragma comment(lib, "ws2_32.lib") // Winsock 库
    typedef int socklen_t;
#else
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #define closesocket close
#endif

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

void initialize_sockets()
{
#ifdef _WIN32
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        std::cerr << "Failed to initialize Winsock" << std::endl;
        exit(EXIT_FAILURE);
    }
#endif
}

void cleanup_sockets()
{
#ifdef _WIN32
    WSACleanup();
#endif
}

int main()
{
    initialize_sockets();

    int server_fd, new_socket;
    struct sockaddr_in server_addr, client_addr;
    char buffer[BUFFER_SIZE] = {0};

    // 创建套接字
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1)
    {
        std::cerr << "Socket creation failed" << std::endl;
        cleanup_sockets();
        return -1;
    }

    // 初始化服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    // 绑定套接字到端口
    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        std::cerr << "Bind failed" << std::endl;
        closesocket(server_fd);
        cleanup_sockets();
        return -1;
    }

    // 监听连接
    if (listen(server_fd, 3) < 0)
    {
        std::cerr << "Listen failed" << std::endl;
        closesocket(server_fd);
        cleanup_sockets();
        return -1;
    }

    std::cout << "Server listening on port " << PORT << std::endl;

    socklen_t addr_len = sizeof(client_addr);

    // 接受客户端连接
    new_socket = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
    if (new_socket < 0)
    {
        std::cerr << "Accept failed" << std::endl;
        closesocket(server_fd);
        cleanup_sockets();
        return -1;
    }

    // 读取客户端消息
    int valread = recv(new_socket, buffer, BUFFER_SIZE, 0);
    if (valread > 0)
    {
        std::cout << "Message from client: " << buffer << std::endl;
    }

    // 发送回应
    const char *response = "Hello from server";
    send(new_socket, response, strlen(response), 0);

    std::cout << "Response sent to client" << std::endl;

    // 关闭套接字
    closesocket(new_socket);
    closesocket(server_fd);

    cleanup_sockets();
    return 0;
}

3. 客户端代码(支持 Linux 和 Windows)

#include <iostream>
#include <cstring>

// 处理跨平台的头文件和函数差异
#ifdef _WIN32
    #include <winsock2.h>
    #pragma comment(lib, "ws2_32.lib") // Winsock 库
    typedef int socklen_t;
#else
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <unistd.h>
    #define closesocket close
#endif

const int PORT = 8080;
const char *SERVER_IP = "127.0.0.1";
const int BUFFER_SIZE = 1024;

void initialize_sockets()
{
#ifdef _WIN32
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        std::cerr << "Failed to initialize Winsock" << std::endl;
        exit(EXIT_FAILURE);
    }
#endif
}

void cleanup_sockets()
{
#ifdef _WIN32
    WSACleanup();
#endif
}

int main()
{
    initialize_sockets();

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        std::cerr << "Socket creation failed" << std::endl;
        cleanup_sockets();
        return -1;
    }

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);

    // 将 IP 地址转换为网络格式
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0)
    {
        std::cerr << "Invalid address/Address not supported" << std::endl;
        closesocket(sock);
        cleanup_sockets();
        return -1;
    }

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        std::cerr << "Connection failed" << std::endl;
        closesocket(sock);
        cleanup_sockets();
        return -1;
    }

    // 发送消息给服务器
    const char *message = "Hello from client";
    send(sock, message, strlen(message), 0);
    std::cout << "Message sent to server" << std::endl;

    // 接收服务器的回应
    char buffer[BUFFER_SIZE] = {0};
    int valread = recv(sock, buffer, BUFFER_SIZE, 0);
    if (valread > 0)
    {
        std::cout << "Response from server: " << buffer << std::endl;
    }

    // 关闭套接字
    closesocket(sock);

    cleanup_sockets();
    return 0;
}

4. 编译与运行

Linux 上编译

在 Linux 上可以使用 g++ 编译:

g++ -o server server.cpp
g++ -o client client.cpp
Windows 上编译

在 Windows 上使用 MinGW 或 Visual Studio 进行编译。

  • MinGW 编译: 使用 MinGW 的 g++ 时需要链接 Ws2_32.lib

    g++ -o server server.cpp -lws2_32
    g++ -o client client.cpp -lws2_32
    

  • Visual Studio 编译

    • 创建新项目并添加源文件。
    • 确保链接 Ws2_32.lib
  • 5. 代码说明

  • 跨平台支持

    • 使用 #ifdef _WIN32 来区分 Windows 和 Linux 平台。
    • 在 Windows 上初始化和清理 Winsock,使用 WSAStartup()WSACleanup() 函数。
  • 服务器端

    • 服务器使用 socket()bind()listen()accept() 等函数来建立连接。
    • 一旦客户端连接上,服务器接收消息并发送响应。
  1. 服务器端

    • 使用 socket() 创建 TCP 套接字。
    • 使用 bind() 将套接字绑定到特定的端口。
    • 使用 listen() 开始监听连接。
    • 使用 accept() 接受客户端连接并与其通信。
    • 使用 read()send() 接收客户端数据和发送响应。
  2. 客户端

    • 使用 socket() 创建 TCP 套接字。
    • 使用 connect() 连接到服务器。
    • 使用 send() 向服务器发送消息。
    • 使用 read() 接收服务器的回应。

这些示例展示了基本的 TCP 网络通信。在生产环境中,你还需要处理更多的异常情况和错误处理,比如超时、并发连接等。

6. 注意事项

  • 这段代码是同步的,适用于简单的网络通信。如果要支持并发或异步通信,可以使用线程或异步 I/O。
  • 处理错误情况,例如 recv() 返回 0(表示连接已关闭)和超时等。
    • 客户端

      • 客户端使用 socket()connect() 连接到服务器。
      • 发送消息给服务器并接收服务器的回应。

http://www.kler.cn/a/319808.html

相关文章:

  • 批量重命名Excel文件并排序
  • 华为云前台用户可挂载数据盘和系统盘是怎么做到的?
  • git入门环境搭建
  • 飞牛云fnOS本地部署WordPress个人网站并一键发布公网远程访问
  • redis7.x源码分析:(1) sds动态字符串
  • 【MySQL 保姆级教学】事务的隔离级别(详细)--下(13)
  • 三维重建的几何评价指标
  • 面试算法题精讲:求数组两组数差值和的最大值
  • 只出现一次的数字 II
  • Redis:事务
  • Hive 的窗口函数 详解
  • 代码随想录算法训练营| 454.四数相加II 、 383. 赎金信 、 15. 三数之和 、 18. 四数之和
  • 有威胁的武器武装检测系统源码分享
  • WebGL渲染与创建2D内容
  • 树——数据结构
  • 移动端如何实现智能语音交互
  • 【LGR-200-Div.4】洛谷入门赛 #27 A - H题解,包含(C++, Go语言)
  • Mybatis中sql数组为空判断
  • SpringBoot 整合docker,执行容器服务
  • Qt系统相关——事件
  • JavaScript --模版字符串用反引号
  • Qt (19)【Qt 线程安全 | 互斥锁QMutex QMutexLocker | 条件变量 | 信号量】
  • python学习笔记(3)——控制语句
  • Java获取Object中Value的方法
  • 数据结构-3.链表
  • 无人机在隧道中如何实现在无卫星信号下的自主导航