TCP與UDP協(xié)議的Socket編程詳解(附詳細示例)
前言
在網(wǎng)絡(luò)通信中,Socket是實現(xiàn)不同主機間進程通信的核心接口。TCP和UDP作為傳輸層的兩大核心協(xié)議,其Socket編程方式因協(xié)議特性差異而有所不同。本文將詳細介紹兩種協(xié)議的Socket編程流程、實現(xiàn)示例及核心差異。
一、TCP協(xié)議的Socket編程
TCP(傳輸控制協(xié)議)是面向連接的可靠傳輸協(xié)議,其Socket編程需遵循"建立連接-數(shù)據(jù)傳輸-釋放連接"的流程,類似現(xiàn)實中的"打電話"場景。
1. 核心流程
服務(wù)器端流程:
- 創(chuàng)建Socket:生成用于通信的文件描述符,指定協(xié)議族和套接字類型。
- 綁定地址:將Socket與特定IP地址和端口綁定,確定通信端點。
- 監(jiān)聽連接:進入被動監(jiān)聽狀態(tài),等待客戶端連接請求。
- 接受連接:從等待隊列中取出客戶端連接,創(chuàng)建新的Socket用于數(shù)據(jù)傳輸。
- 收發(fā)數(shù)據(jù):通過新Socket與客戶端進行雙向數(shù)據(jù)交互。
- 關(guān)閉連接:通信結(jié)束后,關(guān)閉Socket釋放資源。
客戶端流程:
- 創(chuàng)建Socket:生成與服務(wù)器通信的Socket。
- 發(fā)起連接:向服務(wù)器的IP和端口發(fā)送連接請求。
- 收發(fā)數(shù)據(jù):連接建立后,與服務(wù)器進行數(shù)據(jù)交互。
- 關(guān)閉連接:通信結(jié)束后,關(guān)閉Socket。
2. TCP Socket編程示例
服務(wù)器端代碼(C語言):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
const char *response = "Hello from TCP server";
// 1. 創(chuàng)建TCP套接字(SOCK_STREAM表示流式套接字)
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 設(shè)置套接字選項,允許端口復用
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// 配置地址信息(IPv4、任意本地IP、端口8080)
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 2. 綁定套接字到指定端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 3. 監(jiān)聽連接請求(最大等待隊列長度為5)
if (listen(server_fd, 5) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("TCP Server listening on port %d...\n", PORT);
// 4. 接受客戶端連接(阻塞等待)
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 5. 讀取客戶端數(shù)據(jù)
ssize_t valread = read(new_socket, buffer, BUFFER_SIZE);
printf("Received from client: %s\n", buffer);
// 向客戶端發(fā)送響應(yīng)
send(new_socket, response, strlen(response), 0);
printf("Response sent to client\n");
// 6. 關(guān)閉連接
close(new_socket);
close(server_fd);
return 0;
}
客戶端代碼(C語言):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
const char *message = "Hello from TCP client";
// 1. 創(chuàng)建TCP套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\nSocket creation error\n");
return -1;
}
// 配置服務(wù)器地址信息
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 轉(zhuǎn)換IP地址(字符串轉(zhuǎn)二進制)
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
printf("\nInvalid address/ Address not supported\n");
return -1;
}
// 2. 連接服務(wù)器(三次握手在此過程完成)
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection failed\n");
return -1;
}
// 3. 向服務(wù)器發(fā)送數(shù)據(jù)
send(sock, message, strlen(message), 0);
printf("Message sent to server\n");
// 接收服務(wù)器響應(yīng)
ssize_t valread = read(sock, buffer, BUFFER_SIZE);
printf("Received from server: %s\n", buffer);
// 4. 關(guān)閉連接
close(sock);
return 0;
}
二、UDP協(xié)議的Socket編程
UDP(用戶數(shù)據(jù)報協(xié)議)是無連接的不可靠傳輸協(xié)議,其Socket編程無需建立連接,直接發(fā)送數(shù)據(jù)報,類似現(xiàn)實中的"寄明信片"場景。
1. 核心流程
服務(wù)器端流程:
- 創(chuàng)建Socket:生成數(shù)據(jù)報套接字。
- 綁定地址:將Socket與特定IP和端口綁定。
- 收發(fā)數(shù)據(jù):通過
recvfrom()接收客戶端數(shù)據(jù)(含客戶端地址),通過sendto()向客戶端發(fā)送響應(yīng)。 - 關(guān)閉Socket:通信結(jié)束后釋放資源。
客戶端流程:
- 創(chuàng)建Socket:生成數(shù)據(jù)報套接字。
- 收發(fā)數(shù)據(jù):通過
sendto()向服務(wù)器發(fā)送數(shù)據(jù)(指定服務(wù)器地址),通過recvfrom()接收服務(wù)器響應(yīng)。 - 關(guān)閉Socket:通信結(jié)束后釋放資源。
2. UDP Socket編程示例
服務(wù)器端代碼(C語言):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sockfd;
char buffer[BUFFER_SIZE];
struct sockaddr_in servaddr, cliaddr;
const char *response = "Hello from UDP server";
// 1. 創(chuàng)建UDP套接字(SOCK_DGRAM表示數(shù)據(jù)報套接字)
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// 配置服務(wù)器地址信息
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
// 2. 綁定套接字到指定端口
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("UDP Server listening on port %d...\n", PORT);
socklen_t len = sizeof(cliaddr);
// 3. 接收客戶端數(shù)據(jù)報(阻塞等待,同時獲取客戶端地址)
ssize_t n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL,
(struct sockaddr *)&cliaddr, &len);
buffer[n] = '\0';
printf("Received from client: %s\n", buffer);
// 向客戶端發(fā)送響應(yīng)(需指定客戶端地址)
sendto(sockfd, (const char *)response, strlen(response),
MSG_CONFIRM, (const struct sockaddr *)&cliaddr, len);
printf("Response sent to client\n");
// 4. 關(guān)閉套接字
close(sockfd);
return 0;
}
客戶端代碼(C語言):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sockfd;
char buffer[BUFFER_SIZE];
struct sockaddr_in servaddr;
const char *message = "Hello from UDP client";
// 1. 創(chuàng)建UDP套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// 配置服務(wù)器地址信息
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len = sizeof(servaddr);
// 2. 向服務(wù)器發(fā)送數(shù)據(jù)報(需指定服務(wù)器地址)
sendto(sockfd, (const char *)message, strlen(message),
MSG_CONFIRM, (const struct sockaddr *)&servaddr, sizeof(servaddr));
printf("Message sent to server\n");
// 接收服務(wù)器響應(yīng)
ssize_t n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE,
MSG_WAITALL, (struct sockaddr *)&servaddr, &len);
buffer[n] = '\0';
printf("Received from server: %s\n", buffer);
// 3. 關(guān)閉套接字
close(sockfd);
return 0;
}
三、TCP與UDP Socket編程的核心差異
| 對比維度 | TCP Socket編程 | UDP Socket編程 |
|---|---|---|
| 套接字類型 | 使用SOCK_STREAM(流式套接字) | 使用SOCK_DGRAM(數(shù)據(jù)報套接字) |
| 連接處理 | 需connect()(客戶端)和accept()(服務(wù)器)建立連接 | 無需建立連接,直接傳輸數(shù)據(jù) |
| 數(shù)據(jù)傳輸函數(shù) | 使用read()/write()、send()/recv() | 使用sendto()/recvfrom()(需指定地址) |
| 地址關(guān)聯(lián) | 連接建立后自動關(guān)聯(lián)地址,無需重復指定 | 每次傳輸都需顯式指定目標地址 |
| 可靠性保障 | 協(xié)議層保證可靠傳輸,編程無需處理丟失重傳 | 需手動實現(xiàn)重傳、排序等可靠性機制 |
| 服務(wù)器并發(fā) | 需為每個客戶端創(chuàng)建新Socket(或用多線程) | 單Socket即可處理多客戶端(通過地址區(qū)分) |
四、實踐建議
- 協(xié)議選擇:對可靠性要求高的場景(如文件傳輸、登錄認證)用TCP;對實時性要求高的場景(如視頻通話、游戲)用UDP。
- 錯誤處理:實際編程中需強化錯誤處理(如連接超時、數(shù)據(jù)截斷等)。
- 性能優(yōu)化:TCP可通過調(diào)整緩沖區(qū)大小優(yōu)化性能;UDP需控制數(shù)據(jù)報大?。ū苊釯P分片)。
- 安全性:敏感場景需在Socket通信基礎(chǔ)上添加加密(如SSL/TLS)。
通過掌握兩種協(xié)議的Socket編程差異,可根據(jù)實際需求選擇合適的通信方式,構(gòu)建高效、可靠的網(wǎng)絡(luò)應(yīng)用。
到此這篇關(guān)于TCP與UDP協(xié)議的Socket編程的文章就介紹到這了,更多相關(guān)TCP與UDP協(xié)議的Socket編程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能
這篇文章主要介紹了C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能,幫助大家提高程序運行速度,感興趣的朋友可以了解下2020-10-10
C語言光標旋轉(zhuǎn)與倒計時功能實現(xiàn)示例詳解
這篇文章主要為大家介紹了C語言實現(xiàn)光標旋轉(zhuǎn)與倒計時功能的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2021-11-11

