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應用程式:套接字即時傳輸音訊和影片資料以進行通訊。
  • 流媒體服務:套接字將連續的音訊和影片內容傳遞給使用者。
  • 物聯網裝置:套接字促進智慧裝置和伺服器之間的通訊。
  • 即時協作工具:套接字允許使用者之間即時共享編輯和訊息。
  • 資料同步服務:套接字管理裝置和伺服器之間的檔案上傳和下載。
  • 天氣監測系統:套接字將即時天氣資料傳送到中央伺服器進行分析。
  • 支付處理系統:套接字安全地傳輸客戶端和銀行之間的交易資料。
  • 聊天機器人:套接字能夠在對話介面中實現即時訊息傳遞和響應。
廣告