TCP與UDP協(xié)議的Socket編程詳解(附詳細(xì)示例)
前言
在網(wǎng)絡(luò)通信中,Socket是實(shí)現(xiàn)不同主機(jī)間進(jìn)程通信的核心接口。TCP和UDP作為傳輸層的兩大核心協(xié)議,其Socket編程方式因協(xié)議特性差異而有所不同。本文將詳細(xì)介紹兩種協(xié)議的Socket編程流程、實(shí)現(xiàn)示例及核心差異。
一、TCP協(xié)議的Socket編程
TCP(傳輸控制協(xié)議)是面向連接的可靠傳輸協(xié)議,其Socket編程需遵循"建立連接-數(shù)據(jù)傳輸-釋放連接"的流程,類似現(xiàn)實(shí)中的"打電話"場(chǎng)景。
1. 核心流程
服務(wù)器端流程:
- 創(chuàng)建Socket:生成用于通信的文件描述符,指定協(xié)議族和套接字類型。
- 綁定地址:將Socket與特定IP地址和端口綁定,確定通信端點(diǎn)。
- 監(jiān)聽(tīng)連接:進(jìn)入被動(dòng)監(jiān)聽(tīng)狀態(tài),等待客戶端連接請(qǐng)求。
- 接受連接:從等待隊(duì)列中取出客戶端連接,創(chuàng)建新的Socket用于數(shù)據(jù)傳輸。
- 收發(fā)數(shù)據(jù):通過(guò)新Socket與客戶端進(jìn)行雙向數(shù)據(jù)交互。
- 關(guān)閉連接:通信結(jié)束后,關(guān)閉Socket釋放資源。
客戶端流程:
- 創(chuàng)建Socket:生成與服務(wù)器通信的Socket。
- 發(fā)起連接:向服務(wù)器的IP和端口發(fā)送連接請(qǐng)求。
- 收發(fā)數(shù)據(jù):連接建立后,與服務(wù)器進(jìn)行數(shù)據(jù)交互。
- 關(guān)閉連接:通信結(jié)束后,關(guān)閉Socket。
2. TCP Socket編程示例
服務(wù)器端代碼(C語(yǔ)言):
#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è)置套接字選項(xiàng),允許端口復(fù)用
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)聽(tīng)連接請(qǐng)求(最大等待隊(duì)列長(zhǎng)度為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語(yǔ)言):
#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)二進(jì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ù)器(三次握手在此過(guò)程完成)
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ù)報(bào)協(xié)議)是無(wú)連接的不可靠傳輸協(xié)議,其Socket編程無(wú)需建立連接,直接發(fā)送數(shù)據(jù)報(bào),類似現(xiàn)實(shí)中的"寄明信片"場(chǎng)景。
1. 核心流程
服務(wù)器端流程:
- 創(chuàng)建Socket:生成數(shù)據(jù)報(bào)套接字。
- 綁定地址:將Socket與特定IP和端口綁定。
- 收發(fā)數(shù)據(jù):通過(guò)
recvfrom()接收客戶端數(shù)據(jù)(含客戶端地址),通過(guò)sendto()向客戶端發(fā)送響應(yīng)。 - 關(guān)閉Socket:通信結(jié)束后釋放資源。
客戶端流程:
- 創(chuàng)建Socket:生成數(shù)據(jù)報(bào)套接字。
- 收發(fā)數(shù)據(jù):通過(guò)
sendto()向服務(wù)器發(fā)送數(shù)據(jù)(指定服務(wù)器地址),通過(guò)recvfrom()接收服務(wù)器響應(yīng)。 - 關(guān)閉Socket:通信結(jié)束后釋放資源。
2. UDP Socket編程示例
服務(wù)器端代碼(C語(yǔ)言):
#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ù)報(bào)套接字)
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ù)報(bào)(阻塞等待,同時(shí)獲取客戶端地址)
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語(yǔ)言):
#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ù)報(bào)(需指定服務(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編程的核心差異
| 對(duì)比維度 | TCP Socket編程 | UDP Socket編程 |
|---|---|---|
| 套接字類型 | 使用SOCK_STREAM(流式套接字) | 使用SOCK_DGRAM(數(shù)據(jù)報(bào)套接字) |
| 連接處理 | 需connect()(客戶端)和accept()(服務(wù)器)建立連接 | 無(wú)需建立連接,直接傳輸數(shù)據(jù) |
| 數(shù)據(jù)傳輸函數(shù) | 使用read()/write()、send()/recv() | 使用sendto()/recvfrom()(需指定地址) |
| 地址關(guān)聯(lián) | 連接建立后自動(dòng)關(guān)聯(lián)地址,無(wú)需重復(fù)指定 | 每次傳輸都需顯式指定目標(biāo)地址 |
| 可靠性保障 | 協(xié)議層保證可靠傳輸,編程無(wú)需處理丟失重傳 | 需手動(dòng)實(shí)現(xiàn)重傳、排序等可靠性機(jī)制 |
| 服務(wù)器并發(fā) | 需為每個(gè)客戶端創(chuàng)建新Socket(或用多線程) | 單Socket即可處理多客戶端(通過(guò)地址區(qū)分) |
四、實(shí)踐建議
- 協(xié)議選擇:對(duì)可靠性要求高的場(chǎng)景(如文件傳輸、登錄認(rèn)證)用TCP;對(duì)實(shí)時(shí)性要求高的場(chǎng)景(如視頻通話、游戲)用UDP。
- 錯(cuò)誤處理:實(shí)際編程中需強(qiáng)化錯(cuò)誤處理(如連接超時(shí)、數(shù)據(jù)截?cái)嗟龋?/li>
- 性能優(yōu)化:TCP可通過(guò)調(diào)整緩沖區(qū)大小優(yōu)化性能;UDP需控制數(shù)據(jù)報(bào)大?。ū苊釯P分片)。
- 安全性:敏感場(chǎng)景需在Socket通信基礎(chǔ)上添加加密(如SSL/TLS)。
通過(guò)掌握兩種協(xié)議的Socket編程差異,可根據(jù)實(shí)際需求選擇合適的通信方式,構(gòu)建高效、可靠的網(wǎng)絡(luò)應(yīng)用。
到此這篇關(guān)于TCP與UDP協(xié)議的Socket編程的文章就介紹到這了,更多相關(guān)TCP與UDP協(xié)議的Socket編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能
這篇文章主要介紹了C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能,幫助大家提高程序運(yùn)行速度,感興趣的朋友可以了解下2020-10-10
C++ Primer 標(biāo)準(zhǔn)庫(kù)vector示例詳解
該文章主要介紹了C++標(biāo)準(zhǔn)庫(kù)中的vector類型,包括其定義、初始化、成員函數(shù)以及常見(jiàn)操作,文章詳細(xì)解釋了如何使用vector來(lái)存儲(chǔ)和操作對(duì)象集合,并提供了代碼示例來(lái)說(shuō)明vector的使用方法,感興趣的朋友一起看看吧2025-03-03
基于OpenCv的運(yùn)動(dòng)物體檢測(cè)算法
這篇文章主要為大家詳細(xì)介紹了基于OpenCv的運(yùn)動(dòng)物體檢測(cè)算法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
C語(yǔ)言光標(biāo)旋轉(zhuǎn)與倒計(jì)時(shí)功能實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了C語(yǔ)言實(shí)現(xiàn)光標(biāo)旋轉(zhuǎn)與倒計(jì)時(shí)功能的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2021-11-11
C語(yǔ)言每日練習(xí)之進(jìn)制轉(zhuǎn)換
這篇文章主要介紹了C語(yǔ)言進(jìn)制轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-11-11
C語(yǔ)言SQLite3事務(wù)和鎖的操作實(shí)例
這篇文章主要介紹了C語(yǔ)言SQLite3事務(wù)和鎖的操作,結(jié)合完整實(shí)例形式分析了C語(yǔ)言針對(duì)SQLite3數(shù)據(jù)庫(kù)的事務(wù)與鎖相關(guān)操作技巧,需要的朋友可以參考下2017-07-07

