int …int close(int socket);
环境准备
在 MacOS 系统下进行 UDP 套接字编程,需要使用 C++ 编译器(如 g++)和系统提供的网络编程头文件。主要包含以下头文件:
sys/socket.h:套接字相关函数netinet/in.h:网络地址结构定义arpa/inet.h:IP 地址转换函数unistd.h:系统调用函数stdio.h:标准输入输出函数string.h:字符串处理函数
服务器端代码
UDP 服务器端程序负责接收客户端发送的数据报文,并向客户端发送响应。
#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
#define SERVER_PORT 5051 //端口号
#define SERVER_IP "127.0.0.1" //服务器 ip
#define BUFFER_SIZE 256 //缓冲区大小
int main(int argc, char *argv[])
{
//创建一个 UDP 套接字
int sockSer;
sockSer = socket(AF_INET, SOCK_DGRAM, 0);
if(sockSer == -1){
perror("socket");
return -1;
}
//设置端口可以重用
int yes = 1;
if(setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("setsockopt");
return -1;
}
//创建一个记录地址信息的结构体
struct sockaddr_in addrSer, addrCli;
addrSer.sin_family = AF_INET; //使用 AF_INET 协议族
addrSer.sin_port = htons(SERVER_PORT); //设置端口号
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //设置服务器 ip
//将套接字地址与所创建的套接字号联系起来
socklen_t addrlen = sizeof(struct sockaddr);
int res = ::bind(sockSer, (struct sockaddr*)&addrSer, addrlen);
if(res == -1) {
perror("bind");
return -1;
}
printf("UDP Server is running on %s:%d\n", SERVER_IP, SERVER_PORT);
printf("Waiting for messages...\n");
char recvbuf[BUFFER_SIZE]; //接收缓冲区
char sendbuf[BUFFER_SIZE]; //发送缓冲区
while(1)
{
// 接收来自客户端的数据
memset(recvbuf, 0, sizeof(recvbuf));
socklen_t client_len = sizeof(addrCli);
int recv_len = recvfrom(sockSer, recvbuf, sizeof(recvbuf)-1, 0,
(struct sockaddr*)&addrCli, &client_len);
if (recv_len > 0) {
printf("Received from Client %s:%d: %s\n",
inet_ntoa(addrCli.sin_addr), ntohs(addrCli.sin_port), recvbuf);
// 准备响应数据
sprintf(sendbuf, "Server received: %s", recvbuf);
// 发送响应给客户端
if (sendto(sockSer, sendbuf, strlen(sendbuf)+1, 0,
(struct sockaddr*)&addrCli, client_len) <= 0) {
printf("发送响应失败\n");
} else {
printf("Sent response to Client %s:%d\n",
inet_ntoa(addrCli.sin_addr), ntohs(addrCli.sin_port));
}
} else if (recv_len == 0) {
printf("客户端已断开连接\n");
break;
} else {
perror("recvfrom");
break;
}
}
close(sockSer); //关闭套接字
return 0;
}
客户端代码
UDP 客户端程序负责向服务器发送数据报文,并接收服务器的响应。
#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
#define SERVER_PORT 5051 //端口号
#define SERVER_IP "127.0.0.1" //服务器 ip
#define BUFFER_SIZE 256 //缓冲区大小
int main(int argc, char *argv[])
{
//创建一个 UDP 套接字
int sockCli;
sockCli = socket(AF_INET, SOCK_DGRAM, 0);
if(sockCli == -1){
perror("socket");
return -1;
}
//创建一个记录服务器地址信息的结构体
struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET; //使用 AF_INET 协议族
addrSer.sin_port = htons(SERVER_PORT); //设置端口号
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //设置服务器 ip
printf("UDP Client is ready\n");
printf("Enter 'quit' to exit\n");
char sendbuf[BUFFER_SIZE]; //发送缓冲区
char recvbuf[BUFFER_SIZE]; //接收缓冲区
while(1)
{
// 显示客户端提示符,等待用户输入
printf("Cli:>");
// 使用 fgets 读取整行输入,包括空格
if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL) {
printf("读取输入失败\n");
break;
}
// 移除换行符
sendbuf[strcspn(sendbuf, "\n")] = 0;
if(strcmp(sendbuf,"quit") == 0) //如果客户端发送的数据为"quit",则退出。
break;
// 发送数据给服务器
socklen_t server_len = sizeof(addrSer);
if (sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0,
(struct sockaddr*)&addrSer, server_len) <= 0) {
printf("发送数据失败\n");
break;
}
// 接收来自服务器的响应
memset(recvbuf, 0, sizeof(recvbuf));
int recv_len = recvfrom(sockCli, recvbuf, sizeof(recvbuf)-1, 0,
(struct sockaddr*)&addrSer, &server_len);
if (recv_len > 0) {
printf("Ser:> %s\n",recvbuf);
} else if (recv_len == 0) {
printf("服务器已断开连接\n");
break;
} else {
perror("recvfrom");
break;
}
}
close(sockCli); //关闭套接字
return 0;
}
编译和运行
在 MacOS 系统下,可以使用以下命令编译这两个程序:
g++ -o UdpServer udpServer.cpp
g++ -o UdpClient udpClient.cpp
然后分别运行服务器和客户端程序:
./UdpServer
./UdpClient
UDP 与 TCP 的区别
- 连接性:TCP 是面向连接的,需要先建立连接;UDP 是无连接的,直接发送数据报文。
- 可靠性:TCP 提供可靠的数据传输,保证数据顺序和完整性;UDP 不保证数据的可靠传输。
- 速度:UDP 比 TCP 更快,因为没有建立连接和确认的开销。
- 数据边界:UDP 保持数据报文的边界;TCP 将数据看作字节流,没有边界。
- 应用场景:TCP 适用于对数据可靠性要求高的场景,如文件传输、网页浏览等;UDP 适用于对实时性要求高、能容忍少量数据丢失的场景,如视频通话、在线游戏等。
代码特点说明
UDP 套接字编程与 TCP 套接字编程有以下主要区别:
- 套接字类型:UDP 使用 SOCK_DGRAM 类型,TCP 使用 SOCK_STREAM 类型。
- 连接处理:UDP 不需要建立连接,直接使用 sendto 和 recvfrom 函数发送和接收数据。
- 地址信息:UDP 在每次发送和接收数据时都需要指定或获取地址信息。
- 数据传输:UDP 保持数据报文的边界,每个 sendto 调用对应一个 recvfrom 接收。
通过以上代码示例,可以了解 UDP 套接字编程的基本流程和实现方式。 收起