TCP(傳輸控制協議)
TCP 是一種面向連接的、可靠的、基于字節流的傳輸層通信協議。它的主要特點包括:
-
面向連接:在傳輸數據之前,需要通過“三次握手”建立連接;傳輸結束后,通過“四次揮手”斷開連接。
-
可靠傳輸:使用確認機制、重傳機制和滑動窗口機制等確保數據無差錯、不丟失、不重復且按序到達。
-
全雙工通信:允許通信雙方在同一時刻互相發送和接收數據。
UDP(用戶數據報協議)
UDP 是一種無連接的傳輸層協議,它的特點如下:
-
無連接:發送數據前無需建立連接,直接將數據報發送到目標地址。
-
不可靠傳輸:不保證數據的可靠到達,可能會出現數據丟失、重復或亂序的情況。
-
開銷小:UDP 首部只有 8 個字節,相較于 TCP 20 字節的首部,開銷更小,傳輸效率更高。
三次握手和四次揮手
三次握手(建立 TCP 連接)
-
客戶端向服務器發送 SYN 包:客戶端選擇一個初始序列號?
seq = x
,向服務器發送一個 SYN 包,請求建立連接。 -
服務器響應 SYN + ACK 包:服務器收到 SYN 包后,選擇自己的初始序列號?
seq = y
,并將客戶端的序列號加 1(ack = x + 1
),然后發送一個 SYN + ACK 包給客戶端。 -
客戶端發送 ACK 包:客戶端收到 SYN + ACK 包后,將服務器的序列號加 1(
ack = y + 1
),并發送一個 ACK 包給服務器,此時連接建立成功。 -
如圖:
-
四次揮手(關閉 TCP 連接)
-
客戶端發送 FIN 包:客戶端完成數據傳輸后,向服務器發送一個 FIN 包,表示請求關閉連接。
-
服務器發送 ACK 包:服務器收到 FIN 包后,發送一個 ACK 包給客戶端,表示同意關閉客戶端到服務器的連接。
-
服務器發送 FIN 包:服務器完成數據傳輸后,向客戶端發送一個 FIN 包,表示請求關閉服務器到客戶端的連接。
-
客戶端發送 ACK 包:客戶端收到 FIN 包后,發送一個 ACK 包給服務器,表示同意關閉服務器到客戶端的連接,此時連接關閉。
-
如圖:
C 語言代碼實例
TCP 服務器端代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8888
#define BUFFER_SIZE 1024int main() {int server_socket, client_socket;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);char buffer[BUFFER_SIZE];// 創建套接字server_socket = socket(AF_INET, SOCK_STREAM, 0);if (server_socket == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服務器地址結構memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(PORT);// 綁定套接字到指定地址和端口if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(server_socket);exit(EXIT_FAILURE);}// 監聽連接if (listen(server_socket, 5) == -1) {perror("listen failed");close(server_socket);exit(EXIT_FAILURE);}printf("Server listening on port %d...\n", PORT);// 接受客戶端連接client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);if (client_socket == -1) {perror("accept failed");close(server_socket);exit(EXIT_FAILURE);}printf("Client connected.\n");// 接收和發送數據while (1) {memset(buffer, 0, BUFFER_SIZE);ssize_t recv_len = recv(client_socket, buffer, BUFFER_SIZE - 1, 0);if (recv_len <= 0) {break;}printf("Received from client: %s\n", buffer);// 回顯數據給客戶端send(client_socket, buffer, recv_len, 0);}// 關閉套接字close(client_socket);close(server_socket);return 0;
}
TCP 客戶端代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define SERVER_IP "127.0.0.1"
#define PORT 8888
#define BUFFER_SIZE 1024int main() {int client_socket;struct sockaddr_in server_addr;char buffer[BUFFER_SIZE];// 創建套接字client_socket = socket(AF_INET, SOCK_STREAM, 0);if (client_socket == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服務器地址結構memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);server_addr.sin_port = htons(PORT);// 連接到服務器if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("connect failed");close(client_socket);exit(EXIT_FAILURE);}printf("Connected to server.\n");// 發送和接收數據while (1) {memset(buffer, 0, BUFFER_SIZE);printf("Enter message to send (or 'quit' to exit): ");fgets(buffer, BUFFER_SIZE - 1, stdin);if (strcmp(buffer, "quit\n") == 0) {break;}// 發送數據到服務器send(client_socket, buffer, strlen(buffer), 0);// 接收服務器的響應memset(buffer, 0, BUFFER_SIZE);ssize_t recv_len = recv(client_socket, buffer, BUFFER_SIZE - 1, 0);if (recv_len > 0) {printf("Received from server: %s", buffer);}}// 關閉套接字close(client_socket);return 0;
}
UDP 服務器端代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 9999
#define BUFFER_SIZE 1024int main() {int server_socket;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);char buffer[BUFFER_SIZE];// 創建套接字server_socket = socket(AF_INET, SOCK_DGRAM, 0);if (server_socket == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服務器地址結構memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(PORT);// 綁定套接字到指定地址和端口if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(server_socket);exit(EXIT_FAILURE);}printf("UDP Server listening on port %d...\n", PORT);// 接收和發送數據while (1) {memset(buffer, 0, BUFFER_SIZE);ssize_t recv_len = recvfrom(server_socket, buffer, BUFFER_SIZE - 1, 0, (struct sockaddr *)&client_addr, &client_addr_len);if (recv_len > 0) {printf("Received from client: %s\n", buffer);// 回顯數據給客戶端sendto(server_socket, buffer, recv_len, 0, (struct sockaddr *)&client_addr, client_addr_len);}}// 關閉套接字close(server_socket);return 0;
}
UDP 客戶端代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define SERVER_IP "127.0.0.1"
#define PORT 9999
#define BUFFER_SIZE 1024int main() {int client_socket;struct sockaddr_in server_addr;socklen_t server_addr_len = sizeof(server_addr);char buffer[BUFFER_SIZE];// 創建套接字client_socket = socket(AF_INET, SOCK_DGRAM, 0);if (client_socket == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服務器地址結構memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);server_addr.sin_port = htons(PORT);// 發送和接收數據while (1) {memset(buffer, 0, BUFFER_SIZE);printf("Enter message to send (or 'quit' to exit): ");fgets(buffer, BUFFER_SIZE - 1, stdin);if (strcmp(buffer, "quit\n") == 0) {break;}// 發送數據到服務器sendto(client_socket, buffer, strlen(buffer), 0, (struct sockaddr *)&server_addr, server_addr_len);// 接收服務器的響應memset(buffer, 0, BUFFER_SIZE);ssize_t recv_len = recvfrom(client_socket, buffer, BUFFER_SIZE - 1, 0, NULL, NULL);if (recv_len > 0) {printf("Received from server: %s", buffer);}}// 關閉套接字close(client_socket);return 0;
}
代碼解釋
TCP 代碼
-
服務器端:
-
創建 TCP 套接字。
-
綁定到指定的地址和端口。
-
監聽客戶端連接請求。
-
接受客戶端連接。
-
接收客戶端發送的數據,并將數據回顯給客戶端。
-
關閉連接。
-
-
客戶端:
-
創建 TCP 套接字。
-
連接到服務器。
-
發送數據給服務器。
-
接收服務器的響應。
-
關閉連接。
-
UDP 代碼
-
服務器端:
-
創建 UDP 套接字。
-
綁定到指定的地址和端口。
-
接收客戶端發送的數據,并將數據回顯給客戶端。
-
-
客戶端:
-
創建 UDP 套接字。
-
發送數據到服務器。
-
接收服務器的響應。
-
關閉套接字。
-