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是积压参数,它指定服务器繁忙时可以排队的最大待处理连接数。

4. accept()

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

语法

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

其中,

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

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

以下方法用于客户端通信

1. connect()

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

语法

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

其中,

  • sockfd是表示程序中套接字的文件描述符,用于执行各种套接字操作。
  • (struct sockaddr*)&serverAddressserverAddress转换为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++套接字编程示例

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

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};

  // Create socket
  server_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (server_fd == 0) {
    perror("socket failed");
    exit(EXIT_FAILURE);
  }

  // Attach socket to the port
  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);
  }

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

  // Read data
  read(new_socket, buffer, 1024);
  std::cout << "Message from client: " << buffer << std::endl;

  // Close socket
  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";

  // Create socket
  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);

  // Convert IPv4 and IPv6 addresses from text to binary
  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;
  }

  // Connect to server
  if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
    std::cerr << "Connection Failed" << std::endl;
    return -1;
  }

  // Send data
  send(sock, hello, strlen(hello), 0);
  std::cout << "Message sent" << std::endl;

  // Close socket
  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应用程序:套接字实时传输音频和视频数据以进行通信。
  • 流媒体服务:套接字将连续的音频和视频内容传递给用户。
  • 物联网设备:套接字促进智能设备和服务器之间的通信。
  • 实时协作工具:套接字允许用户之间即时共享编辑和消息。
  • 数据同步服务:套接字管理设备和服务器之间的文件上传和下载。
  • 天气监测系统:套接字将实时天气数据发送到中央服务器进行分析。
  • 支付处理系统:套接字安全地传输客户端和银行之间的交易数据。
  • 聊天机器人:套接字能够在对话界面中实现即时消息传递和响应。
广告