C++ 基础

C++ 主页 C++ 概述 C++ 环境设置 C++ 基本语法 C++ 注释 C++ Hello World C++ 省略命名空间 C++ 标记 C++ 常量/字面量 C++ 关键字 C++ 标识符 C++ 数据类型 C++ 数字数据类型 C++ 字符数据类型 C++ 布尔数据类型 C++ 变量类型 C++ 变量作用域 C++ 多变量 C++ 基本输入/输出 C++ 修饰符类型 C++ 存储类 C++ 数字 C++ 枚举 C++ 枚举类 C++ 引用 C++ 日期和时间

C++ 运算符

C++ 运算符 C++ 算术运算符 C++ 关系运算符 C++ 逻辑运算符 C++ 位运算符 C++ 赋值运算符 C++ sizeof 运算符 C++ 条件运算符 C++ 逗号运算符 C++ 成员运算符 C++ 强制类型转换运算符 C++ 指针运算符 C++ 运算符优先级 C++ 一元运算符

C++ 控制语句

C++ 决策语句 C++ if 语句 C++ if else 语句 C++ 嵌套 if 语句 C++ switch 语句 C++ 嵌套 switch语句 C++ 循环类型 C++ while 循环 C++ for 循环 C++ do while 循环 C++ Foreach 循环 C++ 嵌套循环 C++ break 语句 C++ continue 语句 C++ goto 语句

C++ 字符串

C++ 字符串 C++ 循环遍历字符串 C++ 字符串长度 C++ 字符串连接 C++ 字符串比较

C++ 函数

C++ 函数 C++ 多函数参数 C++ 递归函数 C++ 返回值 C++ 函数重载 C++ 函数重写 C++ 默认参数

C++ 数组

C++ 数组 C++ 多维数组 C++ 指向数组的指针 C++ 将数组传递给函数 C++ 从函数返回数组

C++ 结构 &联合

C++ 结构 C++ 联合

C++ 指针

C++ 指针 C++ 解引用 C++ 修改指针

C++ 类和对象

C++ 面向对象 C++ 类 &对象 C++ 类成员函数 C++ 类访问修饰符 C++ 静态类成员 C++ 静态数据成员 C++ 静态成员函数 C++ 内联函数 C++ this 指针 C++ 友元函数 C++ 指向类的指针

C++ 构造函数

C++ 构造函数 &析构函数 C++ 默认构造函数 C++ 参数化构造函数 C++ 复制构造函数 C++ 构造函数重载 C++ 带默认参数的构造函数 C++ 委托构造函数 C++ 构造函数初始化列表 C++ 使用构造函数动态初始化

C++ 继承

C++ 继承 C++ 多重继承 C++ 多级继承

C++ 面向对象

C++ 重载 C++ 多态性 C++ 抽象 C++ 封装 C++ 接口 C++ 虚函数 C++ 纯虚函数与抽象类

C++ 文件处理

C++ 文件和流 C++ 文件读取

C++ 进阶

C++ 异常处理 C++ 动态内存 C++ 命名空间 C++ 模板 C++ 预处理器 C++ 信号处理 C++ 多线程 C++ Web 编程 C++ 套接字编程 C++ 并发 C++ 高级概念 C++ Lambda 表达式 C++ unordered_multiset

C++ 实用资源

C++ 问答 C++ 快速指南 C++ 速查表 C++ STL 教程 C++ 标准库 C++ 实用资源 C++ 讨论


C++ 套接字编程

C++ 套接字编程是使用 C++ 在网络上的两个套接字之间建立通信的方法。在本教程中,我们将学习使用 C++ 中不同类型的套接字进行套接字编程的全部知识。

什么是套接字?

套接字充当网络数据交换的接触点,就像在网络上发送和接收数据的端点。它们允许应用程序使用 TCP(传输控制协议)和 UDP(用户数据报协议)等协议相互通信。它们是大多数互联网通信的支柱,因为它使我们能够进行网页浏览和实时聊天。

套接字有两种类型:

  • 流套接字 (TCP):
    它提供可靠的面向连接的通信,数据以连续的流形式发送,确保数据包按顺序无错误地到达。
  • 数据报套接字 (UDP):
    它提供无连接通信。它以数据包的形式独立传输数据,但不保证顺序或交付,因此发送速度较快,但可靠性较低。

C++ 套接字编程

C++ 套接字编程是一种强大的方法,可用于创建网络应用程序,允许使用套接字 API 通过网络在设备之间进行通信。此过程涉及在客户端和服务器之间建立连接,从而支持通过 TCP 或 UDP 等协议进行数据交换。

C++ 服务器端套接字(监听连接)

以下方法用于处理服务器端通信:

1. socket()

socket() 是网络编程中的一个系统调用,用于在 C++ 中创建一个新的 TCP 套接字,该套接字定义在 <sys/socket.h> 头文件中。

语法

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

其中

  • int sockfd 声明一个整型变量,用于存储套接字文件描述符。
  • AF_INET表示套接字将使用 IPv4 地址族。
  • SOCK_STREAM指定套接字将使用 TCP(一种面向流的协议),
  • 0让系统为指定的地址族和套接字类型选择默认协议(在本例中为 TCP)。

2. bind()

bind() 方法与一个套接字关联,该套接字具有特定的本地地址和端口号,允许套接字监听该地址上的传入连接。

语法

bind(sockfd, (struct sockaddr*)&address, sizeof(address));

其中:

  • sockfd 是程序中代表套接字的文件描述符,用于执行各种套接字操作。
  • (struct sockaddr)&address 将地址结构转换为 bind 函数的通用指针类型。
  • sizeof(address) 指定地址结构的大小,以告知系统预期的数据量。

3. listen()

listen() 函数将套接字标记为被动套接字,该套接字准备接受传入的连接请求(针对服务器)。

语法

listen(sockfd, 10);

其中:

  • sockfd 是程序中代表套接字的文件描述符,用于执行各种套接字操作。
  • 10 是 backlog 参数,指定服务器繁忙时排队的最大待处理连接数。

4. accept()

accept() 函数接受来自客户端(对于服务器)的新连接。它提取待处理连接队列中的第一个连接请求,并为该连接创建一个新的套接字。

语法

int clientSocket = accept(sockfd, (struct sockaddr*)&clientAddress, &clientLen);

其中,

  • sockfd:它是套接字的文件描述符,用于执行各种套接字操作。
  • (struct sockaddr)&address:这是一个类型转换,将clientAddress的指针类型转换为structsockaddr*类型的指针。
  • &lientLen:它是一个指向保存clientAddress大小的变量的指针。

C++ 客户端套接字(连接到服务器)

以下方法用于客户端通信:

1. connect()

此函数是一个系统调用,用于尝试使用套接字与指定服务器(对于客户端)建立连接。

语法

connect(sockfd, (struct sockaddr*)&ser​​verAddress, sizeof(serverAddress));

其中:

  • sockfd 是程序中代表套接字的文件描述符,用于执行各种套接字操作。
  • (struct sockaddr*)&ser​​verAddress serverAddress 转换为 struct sockaddr* 指针,从而与需要通用套接字地址类型的函数兼容。
  • sizeof(serverAddress) 指定 serverAddress 的大小

2. send()

send() 函数是套接字编程中的系统调用,用于向已连接的套接字发送数据。

语法

send(sockfd, "Hello", strlen("Hello"), 0);

其中:

  • sockfd 是程序中代表套接字的文件描述符,用于执行各种套接字操作。
  • strlen("Hello") 函数返回字符串"Hello"的长度(5 个字节),显示要发送的数据字节数。
  • 0 让系统为指定的地址族和套接字类型选择默认协议(在本例中为 TCP)。

3. recv()

recv() 函数是一个系统调用,用于从已连接的套接字接收数据,允许客户端或服务器读取传入的消息。

语法

recv(sockfd, buffer, sizeof(buffer), 0);

其中:

  • sockfd 是程序中代表套接字的文件描述符,用于执行各种套接字操作。
  • buffer 是指向存储接收数据的内存位置的指针。此缓冲区应足够大,以容纳传入的数据。
  • sizeof(buffer) 指定从套接字读取的最大字节数,通常是缓冲区的大小。

关闭客户端套接字

close() 方法关闭打开的套接字。

语法

close(sockfd);

其中,

  • close函数是一个系统调用,用于关闭与套接字关联的文件描述符。

套接字编程所需的头文件

使用 C 或 C++ 进行套接字编程时,必须包含特定的头文件以进行必要的声明。

对于 Linux/Unix系统

  • <sys/socket.h>
  •  <netinet/in.h>
  •  <arpa/inet.h>
  •  <unistd.h>
  •  <string.h>
  •  <errno.h>

Windows 系统

  • <winsock2.h>
  • <ws2tcpip.h>
  • <windows.h>

C++ 套接字编程示例

这里有一个简单的示例,用于说明 TCP 服务器和客户端C++:

TCP 服务器代码

#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstring>
#include <iostream>

#define PORT 8080

int main() {
  int server_fd, new_socket;
  struct sockaddr_in address;
  int opt = 1;
  int addrlen = sizeof(address);
  char buffer[1024] = {0};

  // 创建套接字
  server_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (server_fd == 0) {
    perror("socket failed");
    exit(EXIT_FAILURE);
  }

  // 将套接字连接到端口
  setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = INADDR_ANY;
  address.sin_port = htons(PORT);

  // Bind
  if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
  }

  // Listen
  if (listen(server_fd, 3) < 0) {
    perror("listen");
    exit(EXIT_FAILURE);
  }

  // 接受连接
  new_socket =
      accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen);
  if (new_socket < 0) {
    perror("accept");
    exit(EXIT_FAILURE);
  }

  // 读取数据
  read(new_socket, buffer, 1024);
  std::cout << "Message from client: " << buffer << std::endl;

  // 关闭套接字
  close(new_socket);
  close(server_fd);
  return 0;
}

TCP 客户端代码

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

#include <cstring>
#include <iostream>

#define PORT 8080

int main() {
  int sock = 0;
  struct sockaddr_in serv_addr;
  const char *hello = "Hello from client";

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

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(PORT);

  // 将 IPv4 和 IPv6 地址从文本转换为二进制
  if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
    std::cerr << "Invalid address/ Address not supported" << std::endl;
    return -1;
  }

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

  // 发送数据
  send(sock, hello, strlen(hello), 0);
  std::cout << "Message sent" << std::endl;

  // 关闭套接字
  close(sock);
  return 0;
}

编译和运行步骤

编译和运行客户端套接字程序的步骤如下:

编译服务器和客户端代码文件:

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

运行服务器:

./server

运行客户端(在另一个终端):

./client

最佳实践

  • 错误处理:务必检查套接字函数的返回值,以便正确处理错误。
  • 阻塞与非阻塞模式相比:默认情况下,套接字以阻塞模式运行。因此,请考虑使用非阻塞套接字或多路复用(例如 select 或 poll)来处理多个连接。
  • 跨平台问题:此示例适用于 Unix/Linux。对于 Windows,您需要包含 <winsock2.h>并使用 WSAStartup() 初始化 Winsock。

实际应用

套接字在实际用途和工具等方面有各种各样的应用,这里仅列举其中几种。

以下示例演示了如何在不同的应用中使用套接字:

  • 回显服务器:一个用于回显收到消息的简单服务器。
  • 聊天应用程序:一个允许多个客户端聊天的多线程服务器。
  • FTP 客户端/服务器:一个用于通过网络传输文件的简单实现。
  • Web 服务器:套接字处理 HTTP 请求和响应,以提供 Web 内容。
  • 在线多人游戏:套接字支持玩家和游戏服务器之间的实时通信。
  • 远程访问工具:套接字为服务器的远程管理提供安全连接。
  • VoIP 应用:套接字实时传输音频和视频数据以进行通信。
  • 流媒体服务:套接字向用户提供连续的音频和视频内容。
  • 物联网设备:套接字促进智能设备与服务器之间的通信。
  • 实时协作工具:套接字允许用户之间即时共享编辑和消息。
  • 数据同步服务:套接字管理设备和服务器之间的文件上传和下载。
  • 天气监测系统:套接字将实时天气数据发送到中央服务器进行分析。
  • 支付处理系统:套接字在客户端和银行。
  • 聊天机器人:Sockets 可在对话界面中实现即时消息传递和响应。