MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C++网络编程与Socket应用

2024-02-175.4k 阅读

C++ 网络编程基础

网络编程概述

网络编程是指编写程序使联网的设备能够相互通信和交换数据。在现代软件开发中,网络编程至关重要,无论是开发 Web 应用、分布式系统,还是物联网设备的软件,都离不开网络编程技术。C++ 作为一种强大的编程语言,为网络编程提供了高效且灵活的实现方式。

网络通信模型

  1. OSI 模型:开放系统互连参考模型(OSI 模型)将网络通信分为七层,从下到上分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。每层都有特定的功能和职责,相邻层之间通过接口进行交互。例如,物理层负责处理物理介质上的信号传输,而数据链路层则负责将物理层接收到的信号转换为数据帧,并进行错误检测和纠正。
  2. TCP/IP 模型:TCP/IP 模型是实际应用中广泛使用的网络通信模型,它由四层组成,分别是网络接口层、网际层、传输层和应用层。虽然它与 OSI 模型的分层有所不同,但基本功能类似。网络接口层负责与物理网络的交互,网际层主要处理 IP 地址和路由选择,传输层提供可靠的(TCP)或不可靠的(UDP)数据传输服务,应用层则包含了各种网络应用协议,如 HTTP、FTP 等。

Socket 编程基础

Socket 简介

Socket(套接字)是网络编程中一个重要的概念,它是应用层与传输层之间的接口。可以将 Socket 看作是不同主机上的应用程序进行通信的端点。Socket 提供了一种通用的机制,使得不同操作系统、不同编程语言开发的应用程序能够在网络上进行数据交换。

Socket 类型

  1. 流式 Socket(SOCK_STREAM):基于 TCP 协议,提供可靠的、面向连接的字节流传输服务。在数据传输前,需要建立连接(三次握手),传输完成后,需要关闭连接(四次挥手)。由于其可靠性,适用于对数据准确性要求较高的应用,如文件传输、远程登录等。
  2. 数据报式 Socket(SOCK_DGRAM):基于 UDP 协议,提供不可靠的、无连接的数据报传输服务。数据以独立的数据包形式发送,不保证数据的顺序和完整性。虽然 UDP 不如 TCP 可靠,但它具有传输速度快、开销小的特点,适用于对实时性要求较高、对数据准确性要求相对较低的应用,如实时视频流、音频流传输等。

Socket 地址结构

在进行 Socket 编程时,需要使用地址结构来指定通信的端点。在 IPv4 中,常用的地址结构是 sockaddr_in,定义如下:

struct sockaddr_in {
    sa_family_t sin_family; // 地址族,通常为 AF_INET
    in_port_t sin_port;     // 端口号
    struct in_addr sin_addr; // IP 地址
    char sin_zero[8];       // 填充字段,使 sockaddr_in 与 sockaddr 结构大小相同
};
struct in_addr {
    in_addr_t s_addr; // 32 位的 IP 地址
};

在 IPv6 中,使用 sockaddr_in6 结构:

struct sockaddr_in6 {
    sa_family_t sin6_family; // 地址族,AF_INET6
    in_port_t sin6_port;     // 端口号
    uint32_t sin6_flowinfo;  // 流信息
    struct in6_addr sin6_addr; // IPv6 地址
    uint32_t sin6_scope_id;  // 范围 ID
};
struct in6_addr {
    unsigned char s6_addr[16]; // 128 位的 IPv6 地址
};

C++ 中使用 Socket 进行网络编程

初始化 Winsock(Windows 平台)

在 Windows 平台上使用 Socket 编程,首先需要初始化 Winsock 库。可以使用 WSAStartup 函数来完成这一操作:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    // 初始化 Winsock
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }
    // 在此处进行 Socket 编程操作

    // 清理 Winsock 库
    WSACleanup();
    return 0;
}

创建 Socket

在 C++ 中,可以使用 socket 函数来创建 Socket。该函数的原型如下:

SOCKET socket(int af, int type, int protocol);
  • af:指定地址族,如 AF_INET(IPv4)或 AF_INET6(IPv6)。
  • type:指定 Socket 类型,如 SOCK_STREAMSOCK_DGRAM
  • protocol:指定协议,通常为 0,由系统根据 aftype 自动选择合适的协议。

以下是创建一个基于 IPv4 的流式 Socket 的示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    // 创建 Socket
    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    // 在此处进行后续的 Socket 操作

    closesocket(listenSocket);
    WSACleanup();
    return 0;
}

绑定 Socket 到地址

创建 Socket 后,需要将其绑定到一个特定的地址和端口,以便接收来自客户端的连接。使用 bind 函数来完成绑定操作,其原型如下:

int bind(SOCKET s, const struct sockaddr *name, int namelen);
  • s:要绑定的 Socket。
  • name:指向包含地址和端口信息的 sockaddr 结构的指针。
  • namelenname 结构的长度。

以下是绑定 Socket 到本地地址和端口 8080 的示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    // 绑定 Socket
    int bindResult = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (bindResult == SOCKET_ERROR) {
        std::cout << "Bind failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    // 在此处进行后续操作

    closesocket(listenSocket);
    WSACleanup();
    return 0;
}

监听连接(服务器端)

对于服务器端的流式 Socket,在绑定地址后,需要开始监听客户端的连接请求。使用 listen 函数来实现监听功能,其原型如下:

int listen(SOCKET s, int backlog);
  • s:要监听的 Socket。
  • backlog:指定等待连接队列的最大长度。

以下是开始监听的示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    int bindResult = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (bindResult == SOCKET_ERROR) {
        std::cout << "Bind failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    // 开始监听
    int listenResult = listen(listenSocket, 5);
    if (listenResult == SOCKET_ERROR) {
        std::cout << "Listen failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Server is listening on port 8080..." << std::endl;

    // 在此处进行接受连接操作

    closesocket(listenSocket);
    WSACleanup();
    return 0;
}

接受连接(服务器端)

服务器端在监听连接后,需要使用 accept 函数来接受客户端的连接请求。accept 函数的原型如下:

SOCKET accept(SOCKET s, struct sockaddr *addr, int *addrlen);
  • s:监听的 Socket。
  • addr:用于存储客户端地址信息的 sockaddr 结构指针。
  • addrlen:指向 addr 结构长度的指针。

以下是接受客户端连接的示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    int bindResult = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (bindResult == SOCKET_ERROR) {
        std::cout << "Bind failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    int listenResult = listen(listenSocket, 5);
    if (listenResult == SOCKET_ERROR) {
        std::cout << "Listen failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Server is listening on port 8080..." << std::endl;

    sockaddr_in clientAddr;
    int clientAddrLen = sizeof(clientAddr);
    // 接受客户端连接
    SOCKET clientSocket = accept(listenSocket, (sockaddr*)&clientAddr, &clientAddrLen);
    if (clientSocket == INVALID_SOCKET) {
        std::cout << "Accept failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Client connected!" << std::endl;

    // 在此处进行数据收发操作

    closesocket(clientSocket);
    closesocket(listenSocket);
    WSACleanup();
    return 0;
}

连接服务器(客户端)

客户端需要使用 connect 函数来连接到服务器。connect 函数的原型如下:

int connect(SOCKET s, const struct sockaddr *name, int namelen);
  • s:要连接的 Socket。
  • name:指向服务器地址信息的 sockaddr 结构指针。
  • namelenname 结构的长度。

以下是客户端连接到服务器的示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serverAddr.sin_port = htons(8080);

    // 连接服务器
    int connectResult = connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (connectResult == SOCKET_ERROR) {
        std::cout << "Connect failed: " << WSAGetLastError() << std::endl;
        closesocket(clientSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Connected to server!" << std::endl;

    // 在此处进行数据收发操作

    closesocket(clientSocket);
    WSACleanup();
    return 0;
}

数据发送与接收

  1. 发送数据:在建立连接后,可以使用 send 函数(对于流式 Socket)或 sendto 函数(对于数据报式 Socket)来发送数据。send 函数的原型如下:
int send(SOCKET s, const char *buf, int len, int flags);
  • s:要发送数据的 Socket。
  • buf:指向要发送数据的缓冲区指针。
  • len:要发送数据的长度。
  • flags:通常设置为 0。

以下是服务器端向客户端发送数据的示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    int bindResult = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (bindResult == SOCKET_ERROR) {
        std::cout << "Bind failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    int listenResult = listen(listenSocket, 5);
    if (listenResult == SOCKET_ERROR) {
        std::cout << "Listen failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Server is listening on port 8080..." << std::endl;

    sockaddr_in clientAddr;
    int clientAddrLen = sizeof(clientAddr);
    SOCKET clientSocket = accept(listenSocket, (sockaddr*)&clientAddr, &clientAddrLen);
    if (clientSocket == INVALID_SOCKET) {
        std::cout << "Accept failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Client connected!" << std::endl;

    const char* message = "Hello, client!";
    // 发送数据
    int sendResult = send(clientSocket, message, strlen(message), 0);
    if (sendResult == SOCKET_ERROR) {
        std::cout << "Send failed: " << WSAGetLastError() << std::endl;
    }

    closesocket(clientSocket);
    closesocket(listenSocket);
    WSACleanup();
    return 0;
}
  1. 接收数据:使用 recv 函数(对于流式 Socket)或 recvfrom 函数(对于数据报式 Socket)来接收数据。recv 函数的原型如下:
int recv(SOCKET s, char *buf, int len, int flags);
  • s:接收数据的 Socket。
  • buf:用于存储接收数据的缓冲区指针。
  • len:缓冲区的长度。
  • flags:通常设置为 0。

以下是客户端接收服务器数据的示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serverAddr.sin_port = htons(8080);

    int connectResult = connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (connectResult == SOCKET_ERROR) {
        std::cout << "Connect failed: " << WSAGetLastError() << std::endl;
        closesocket(clientSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Connected to server!" << std::endl;

    char buffer[1024];
    // 接收数据
    int recvResult = recv(clientSocket, buffer, sizeof(buffer), 0);
    if (recvResult > 0) {
        buffer[recvResult] = '\0';
        std::cout << "Received from server: " << buffer << std::endl;
    } else if (recvResult == 0) {
        std::cout << "Connection closed by server." << std::endl;
    } else {
        std::cout << "Receive failed: " << WSAGetLastError() << std::endl;
    }

    closesocket(clientSocket);
    WSACleanup();
    return 0;
}

基于 UDP 的 Socket 编程

UDP 服务器端示例

UDP 服务器端不需要像 TCP 那样建立连接,直接接收和发送数据报。以下是一个简单的 UDP 服务器端示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8081);

    int bindResult = bind(udpSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (bindResult == SOCKET_ERROR) {
        std::cout << "Bind failed: " << WSAGetLastError() << std::endl;
        closesocket(udpSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "UDP server is listening on port 8081..." << std::endl;

    char buffer[1024];
    sockaddr_in clientAddr;
    int clientAddrLen = sizeof(clientAddr);
    // 接收数据报
    int recvResult = recvfrom(udpSocket, buffer, sizeof(buffer), 0, (sockaddr*)&clientAddr, &clientAddrLen);
    if (recvResult > 0) {
        buffer[recvResult] = '\0';
        std::cout << "Received from client: " << buffer << std::endl;

        const char* response = "Message received!";
        // 发送响应数据报
        int sendResult = sendto(udpSocket, response, strlen(response), 0, (sockaddr*)&clientAddr, clientAddrLen);
        if (sendResult == SOCKET_ERROR) {
            std::cout << "Send failed: " << WSAGetLastError() << std::endl;
        }
    } else {
        std::cout << "Receive failed: " << WSAGetLastError() << std::endl;
    }

    closesocket(udpSocket);
    WSACleanup();
    return 0;
}

UDP 客户端示例

UDP 客户端同样不需要建立连接,直接向服务器发送数据报并接收响应。以下是 UDP 客户端示例:

#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serverAddr.sin_port = htons(8081);

    const char* message = "Hello, UDP server!";
    // 发送数据报
    int sendResult = sendto(udpSocket, message, strlen(message), 0, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (sendResult == SOCKET_ERROR) {
        std::cout << "Send failed: " << WSAGetLastError() << std::endl;
    }

    char buffer[1024];
    int recvResult = recvfrom(udpSocket, buffer, sizeof(buffer), 0, nullptr, nullptr);
    if (recvResult > 0) {
        buffer[recvResult] = '\0';
        std::cout << "Received from server: " << buffer << std::endl;
    } else {
        std::cout << "Receive failed: " << WSAGetLastError() << std::endl;
    }

    closesocket(udpSocket);
    WSACleanup();
    return 0;
}

错误处理与优化

错误处理

在网络编程中,错误处理至关重要。在 Windows 平台上,使用 WSAGetLastError 函数可以获取最近一次的 Socket 操作错误代码。在 Linux 平台上,使用 errno 全局变量来获取错误信息。例如,在 sendrecv 操作失败后,可以通过以下方式获取错误信息:

// Windows 平台
int errorCode = WSAGetLastError();
std::cout << "Socket error: " << errorCode << std::endl;

// Linux 平台
#include <cerrno>
#include <iostream>
if (send(sockfd, buf, len, 0) == -1) {
    std::cout << "Send error: " << errno << std::endl;
}

常见的错误代码包括 WSAEINVAL(无效参数)、WSAECONNREFUSED(连接被拒绝)等。根据不同的错误代码,可以采取相应的处理措施,如重新尝试连接、提示用户等。

性能优化

  1. 缓冲区优化:合理设置发送和接收缓冲区的大小可以提高网络传输性能。在创建 Socket 后,可以使用 setsockopt 函数来设置缓冲区大小。例如,设置发送缓冲区大小为 8192 字节:
int sendBufSize = 8192;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&sendBufSize, sizeof(sendBufSize));
  1. 异步 I/O:使用异步 I/O 可以避免阻塞主线程,提高程序的并发性能。在 Windows 平台上,可以使用 WSAAsyncSelectWSAEventSelect 函数来实现异步 I/O。在 Linux 平台上,可以使用 epoll 等机制来实现高效的异步事件处理。
  2. 连接复用:在某些应用场景下,复用已经建立的连接可以减少连接建立和关闭的开销。例如,在 HTTP 1.1 中,支持持久连接,通过复用 TCP 连接可以提高页面加载速度。

高级话题

多线程网络编程

在实际应用中,为了处理多个客户端连接或同时进行多种网络操作,常常需要使用多线程技术。每个线程可以独立处理一个客户端连接或执行特定的网络任务,从而提高程序的并发处理能力。以下是一个简单的多线程服务器示例,使用 C++ 的 std::thread 库:

#include <winsock2.h>
#include <iostream>
#include <thread>
#include <vector>

void handleClient(SOCKET clientSocket) {
    char buffer[1024];
    int recvResult = recv(clientSocket, buffer, sizeof(buffer), 0);
    if (recvResult > 0) {
        buffer[recvResult] = '\0';
        std::cout << "Received from client: " << buffer << std::endl;

        const char* response = "Message received!";
        int sendResult = send(clientSocket, response, strlen(response), 0);
        if (sendResult == SOCKET_ERROR) {
            std::cout << "Send failed: " << WSAGetLastError() << std::endl;
        }
    } else {
        std::cout << "Receive failed: " << WSAGetLastError() << std::endl;
    }
    closesocket(clientSocket);
}

int main() {
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }

    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET) {
        std::cout << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    int bindResult = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (bindResult == SOCKET_ERROR) {
        std::cout << "Bind failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    int listenResult = listen(listenSocket, 5);
    if (listenResult == SOCKET_ERROR) {
        std::cout << "Listen failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "Server is listening on port 8080..." << std::endl;

    std::vector<std::thread> threads;
    while (true) {
        sockaddr_in clientAddr;
        int clientAddrLen = sizeof(clientAddr);
        SOCKET clientSocket = accept(listenSocket, (sockaddr*)&clientAddr, &clientAddrLen);
        if (clientSocket == INVALID_SOCKET) {
            std::cout << "Accept failed: " << WSAGetLastError() << std::endl;
            break;
        }
        threads.emplace_back(handleClient, clientSocket);
    }

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

    closesocket(listenSocket);
    WSACleanup();
    return 0;
}

网络安全

在网络编程中,网络安全是一个不容忽视的问题。常见的网络安全威胁包括数据泄露、中间人攻击、拒绝服务攻击等。为了保障网络通信的安全,可以采取以下措施:

  1. 加密传输:使用 SSL/TLS 协议对数据进行加密传输,防止数据在传输过程中被窃取或篡改。在 C++ 中,可以使用 OpenSSL 等库来实现 SSL/TLS 加密。
  2. 身份认证:通过用户名和密码、数字证书等方式对通信双方进行身份认证,确保通信的合法性。
  3. 防火墙与访问控制:设置防火墙规则,限制对服务器端口的访问,只允许合法的客户端连接。同时,实施访问控制策略,对不同用户或角色授予不同的权限。

通过以上对 C++ 网络编程与 Socket 应用的详细介绍,希望读者能够对这一领域有更深入的理解,并能够在实际项目中运用所学知识开发出高效、可靠且安全的网络应用程序。