TCP与UDP协议的Socket编程详解(附详细示例)
目录
- 前言
- 一、TCP协议的Socket编程
- 1. 核心流程
- 服务器端流程:
- 客户端流程:
- 2. TCP Socket编程示例
- 服务器端代码(C语言):
- 客户端代码(C语言):
- 二、UDP协议的Socket编程
- 1. 核心流程
- 服务器端流程:
- 客户端流程:
- 2. UDP Socket编程示例
- 服务器端代码(C语言):
- 客户端代码(C语言):
- 三、TCP与UDP Socket编程的核心差异
- 四、实践建议
前言
在网络通信中,Socket是实现不同主机间进程通信的核心接口。TCP和UDP作为传输层的两大核心协议,其Socket编程方式因协议特性差异而有所不同。本文将详细介绍两种协议的Socket编程流程、实现示例及核心差异。
一、TCP协议的Socket编程
TCP(传输控制协议)是面向连接的可靠传输协议,其Socket编程需遵循"建立连接-数据传输-释放连接"的流程,类似现实中的"打电话"场景。
1. 核心流程
服务器端流程:
- 创建Socket:生成用于通信的文件描述符,指定协议族和套接字类型。
- 绑定地址:将Socket与特定IP地址和端口绑定,确定通信端点。
- 监听连接:进入被动监听状态,等待客户端连接请求。
- 接受连接:从等待队列中取出客户端连接,创建新的Socket用于数据传输。
- 收发数据:通过新Socket与客户etjPivfPEL端进行双向数据交互。
- 关闭连接:通信结束后,关闭Socket释放资源。
客户端流程:
- 创建Socket:生成与服务器通信的Socket。
- 发起连接:向服务器的IP和端口发送连接请求。
- 收发数据:连接建立后,与服务器进行数据交互。
- 关闭连接:通信结束后,关闭Socket。
2. TCP Socket编程示例
服务器端代码(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. 创建TCP套接字(SOCK_STREAM表示流式套接字) if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置套接字选项,允许端口复用 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. 监听连接请求(最大等待队列长度为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_FAILUREetjPivfPEL); } // 5. 读取客户端数据 ssize_t valread = read(new_socket, buffer, BUFFER_SIZE); printf("Received from client: %s\n", buffer); // 向客户端发送响应 send(new_socket, response, strlen(response), 0); printf("Response sent to client\n"); // 6. 关闭连接 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. 创建TCP套接字 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\nSocket creation error\n"); return -1; } // 配置服务器地址信息 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 转换IP地址(字符串转二进制) if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { printf("\nInvalid address/ Address not supported\n"); return -1; } // 2. 连接服务器(三次握手在此过程完成) if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("\nConnection failed\n"); return -1; } // 3. 向服务器发送数据 send(sock, message, strlen(message), 0); printf("Message sent to server\n"); // 接收服务器响应 ssize_t valread = read(sock, buffer, BUFFER_SIZE); printf("Received from server: %s\n", buffer); // 4. 关闭连接 close(sock); return 0; }
二、UDP协议的Socket编程
UDP(用户数据报协议)是无连接的不可靠传输协议,其Socket编程无需建立连接,直接发送数据报,类似现实中的"寄明信片"场景。
1. 核心流程
服务器端流程:
- 创建Socket:生成数据报套接字。
- 绑定地址:将Socket与特定IP和端口绑定。
- 收发数据:通过
recvfrom()
接收客户端数据(含客户端地址),通过sendto()
向客户端发送响应。 - 关闭Socket:通信结束后释放资源。
客户端流程:
- 创建Socket:生成数据报套接字。
- 收发数据:通过
sendto()
向服务器发送数据(指定服务器地址),通过recvfrom()
接收服务器响应。 - 关闭Socket:通信结束后释放资源。
2. UDP Socket编程示例
服务器端代码(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. 创建UDP套接字(SOCK_DGRAM表示数据报套接字) 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)); // 配置服务器地址信息 servaddr.sin_family = AF_INET; servjsaddr.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. 接收客户端数据报(阻塞等待,同时获取客户端地址) 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); // 向客户端发送响应(需指定客户端地址) sendto(sockfd, (const char *)response, strlen(response), MSG_CONFIRM, (const struct sockaddr *)&cliaddr, len); printf("Response sent to client\n"); // 4. 关闭套接字 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 sjavascriptockfd; char buffer[BUFFER_SIZE]; struct sockaddr_in servaddr; const char *message = "Hello from UDP client"; // 1. 创建UDP套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } memset(&servaddr, 0, sizeof(servaddr)); // 配置服务器地址信息 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. 向服务器发送数据报(需指定服务器地址) sendto(sockfd, (const char *)message, strlen(message), MSG_CONFIRM, (const struct sockaddr *)&servaddr, sizeof(servaddr)); printf("Message sent to server\n"); // 接收服务器响应 ssize_t n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct androidsockaddr *)&servaddr, &len); buffer[n] = '\0'; printf("Received from server: %s\n", buffer); // 3. 关闭套接字 close(sockfd); return 0; }
三、TCP与UDP Socket编程的核心差异
对比维度 | TCP Socket编程 | UDP Socket编程 |
---|---|---|
套接字类型 | 使用SOCK_STREAM (流式套接字) | 使用SOCK_DGRAM (数据报套接字) |
连接处理 | 需connect() (客户端)和accept() (服务器)建立连接 | 无需建立连接,直接传输数据 |
数据传输函数 | 使用read() /write() 、send() /recv() | 使用sendto() /recvfrom() (需指定地址) |
地址关联 | 连接建立后自动关联地址,无需重复指定 | 每次传输都需显式指定目标地址 |
可靠性保障 | 协议层保证可靠传输,编程无需处理丢失重传 | 需手动实现重传、排序等可靠性机制 |
服务器并发 | 需为每个客户端创建新Socket(或用多线程) | 单Socket即可处理多客户端(通过地址区分) |
四、实践建议
- 协议选择:对可靠性要求高的场景(如文件传输、登录认证)用TCP;对实时性要求高的场景(如视频通话、游戏)用UDP。
- 错误处理:实际编程中需强化错误处理(如连接超时、数据截断等)。
- 性能优化:TCP可通过调整缓冲区大小优化性能;UDP需控制数据报大小(避免IP分片)。
- 安全性:敏感场景需在Socket通信基础上添加加密(如SSL/TLS)。
通过掌握两种协议的Socket编程差异,可根据实际需求选择合适的通信方式,构建高效、可靠的网络应用。
到此这篇关于TCP与UDP协议的Socket编程的文章就介绍到这了,更多相关TCP与UDP协议的Socket编程内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论