【C++】udp通信协议详解和示例

前言

UDP(‌User Datagram Protocol,‌用户数据报协议)‌是一种无连接的、‌不可靠的、‌面向数据报的传输层协议。‌它广泛应用于需要高实时性且对数据传输可靠性要求不高的场景,‌如实时音视频传输、‌在线游戏等。‌本文将详细介绍UDP通讯协议的原理、‌各关键函数的作用及其实现实例,‌并最后进行总结。‌

UDP协议原理

无连接性

UDP是无连接的协议,‌通信双方在传输数据之前不需要建立连接,‌也不需要维护连接状态。‌这种特性使得UDP在处理实时数据和快速数据传输时非常高效。‌

不可靠性

UDP不提供数据包的确认和重传机制,‌也不保证数据包的顺序性,‌因此数据传输过程中可能会出现丢包、‌重复或乱序等情况。‌UDP的设计目标之一就是降低通信的开销,‌以支持高吞吐量和低延迟的通信需求。‌

面向数据报

UDP以数据报为基本单位进行通信,‌每个数据报是一个独立的、‌完整的消息,‌具有独立的头部和数据部分。‌UDP数据报格式包括源端口号、‌目的端口号、‌长度和校验和等字段。‌
-‌源端口号(‌16 bits)‌‌:‌标识发送端的端口号。‌
-‌目的端口号(‌16 bits)‌‌:‌标识接收端的端口号。‌
-‌长度(‌16 bits)‌‌:‌表示UDP数据报的总长度,‌包括UDP头部和数据部分。‌
-‌校验和(‌16 bits)‌‌:‌用于检测数据报的完整性,‌以确保数据在传输过程中没有被损坏。‌
-‌数据(‌可变长度)‌‌:‌包含了应用程序要传输的实际数据。‌

相应函数

socket()

socket()函数用于创建一个套接字,‌它是网络通信的端点。‌对于UDP通信,‌socket()函数的第三个参数应设置为SOCK_DGRAM,‌表示创建的是数据报套接字。‌

int socket(int domain, int type, int protocol);
‌作用‌:‌创建一个新的套接字。‌

参数说明:
-‌domain‌:‌指定协议族,‌对于IPv4使用AF_INET。‌
-‌type‌:‌指定套接字类型,‌对于UDP使用SOCK_DGRAM。‌
-‌protocol‌:‌通常设置为0,‌自动选择协议。‌

bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
‌作用‌:‌将套接字绑定到一个特定的地址和端口上。‌

参数说明:
-‌sockfd‌:‌由socket()返回的套接字描述符。‌
-‌addr‌:‌指向sockaddr结构的指针,‌包含地址和端口信息。‌
-‌addrlen‌:‌地址结构体的长度。‌

sendto()

‌函数原型‌:‌

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
‌作用‌:‌向指定的地址发送数据报。‌
参数说明:
-‌sockfd‌:‌套接字描述符。‌
-‌buf‌:‌指向要发送数据的缓冲区。‌
-‌len‌:‌缓冲区中数据的长度。‌
-‌flags‌:‌发送选项,‌通常设置为0。‌
-‌dest_addr‌:‌指向目标地址的sockaddr结构指针。‌
-‌addrlen‌:‌目标地址结构体的长度。‌

recvfrom()

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
‌作用‌:‌从套接字接收数据报,‌并获取发送方的地址信息。‌
参数说明:
-‌sockfd‌:‌套接字描述符。‌
-‌buf‌:‌指向接收数据的缓冲区。‌
-‌len‌:‌缓冲区的大小。‌
-‌flags‌:‌接收选项,‌通常设置为0。‌
-‌src_addr‌:‌指向sockaddr结构的指针,‌用于存储发送方的地址信息。‌
-‌addrlen‌:‌地址结构体的长度指针。‌

示例

本实例在windows 11环境下的vs2019中运行,ubuntu环境中的头文件与此不同。但各函数功能基本一样。在同一台电脑中进行发送和接受,进行两个实例。

字符串整体发送示例

服务端

#include <iostream>
#include <cstring>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "12345" // 你可以根据需要更改这个端口号,但要确保它没有被其他应用占用

int main() {
   
   
    WSADATA wsaData;
    SOCKET sock = INVALID_SOCKET;
    struct addrinfo hints, * res = NULL, * p = NULL;
    int iResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;
    struct sockaddr_storage their_addr;
    socklen_t addr_size = sizeof(their_addr);

    // 初始化Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
   
   
        std::cerr << "WSAStartup failed with error: " << iResult << std::endl;
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET; // 使用IPv4
    hints.ai_socktype = SOCK_DGRAM; // UDP
    hints.ai_protocol = IPPROTO_UDP;
    hints.ai_flags = AI_PASSIVE; // 允许绑定到任意地址

    // 解析监听地址和端口
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &res);
    if (iResult != 0) {
   
   
        std::cerr << "getaddrinfo failed with error: " << iResult << std::endl;
        WSACleanup();
        return 1;
    }

    // 创建套接字
    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sock == INVALID_SOCKET) {
   
   
        std::cerr << "socket failed with error: " << WSAGetLastError() << std::endl;
        freeaddrinfo(res);
        WSACleanup();
        return 1;
    }

    // 绑定套接字到地址和端口
    iResult = bind(sock, res->ai_addr, (int)res->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
   
   
        std::cerr << "bind failed with error: " << WSAGetLastError() << std::endl;
        freeaddrinfo(res);
        closesocket(sock);
        WSACleanup();
        return 1;
    }

    freeaddrinfo(res); // 不再需要地址信息

    std::cout << "UDP Server listening on port " << DEFAULT_PORT << std::endl;

    // 接收数据
    while (true) {
   
   
        iResult = recvfrom(sock, recvbuf, recvbuflen, 0,
            (struct sockaddr*)&their_addr, &addr_size);
        if (iResult == SOCKET_ERROR) {
   
   
            std::cerr << "recvfrom failed with error: " << WSAGetLastError() << std::endl;
            closesocket(soc
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木彳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值