【网络】UDP协议创建流程详解(第三篇)

目录

1.创建套接字 socket()

函数原型

参数

返回值

2.bind()

一、网络编程中的 bind() 函数

主要用途

函数原型

返回值

3.recvfrom()

一、函数原型

二、返回值

三、常见错误

4.sendto()

一、函数原型

二、参数说明

三、返回值

四、使用注意事项


1.创建套接字 socket()

socket() 函数是网络编程中的一个基本且关键的函数,它用于创建一个新的套接字(socket)。套接字是网络通信中的端点,它允许不同主机上的进程之间进行数据的发送和接收。socket() 函数在多种编程语言中都有对应的实现,但最基础和广泛使用的是在C语言中的定义,它也被许多其他语言通过标准库或扩展库间接支持。

函数原型

在C语言中,socket() 函数的原型定义在 <sys/socket.h> 头文件中(在Windows上可能是 <winsock2.h> 并且需要初始化Winsock DLL):

#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
  
int socket(int domain, int type, int protocol);

参数

  • domain:指定了协议族(protocol family)。最常用的协议族是 AF_INET,它代表IPv4协议;另一个是 AF_INET6,它代表IPv6协议。
  • type:指定了套接字类型。最常用的类型是 SOCK_STREAM,它表示面向连接的TCP协议;另一个是 SOCK_DGRAM,它表示无连接的UDP协议。
  • protocol:指定了具体的协议。大多数情况下,这个参数可以设置为0,让系统自动选择该类型套接字和协议族对应的默认协议。

返回值

  • 成功时,socket() 函数返回一个非负整数,它是套接字的描述符(socket descriptor)。这个描述符在后续的套接字操作中(如绑定、监听、接受连接、发送和接收数据)将被使用。
  • 失败时,socket() 函数返回-1,并设置全局变量 errno 以指示错误原因。

2.bind()

bind() 函数在不同的编程环境和上下文中具有不同的作用,但主要可以分为两大类:网络编程中的 bind() 函数和编程语言(如C++、JavaScript)中的 bind() 函数。

一、网络编程中的 bind() 函数

在网络编程中,bind() 函数主要用于将一个套接字(socket)与一个地址(包括IP地址和端口号)绑定在一起。这是服务器端编程中的一个关键步骤,通常在创建套接字后使用。

主要用途
  • 在服务器端,bind() 函数通过给一个未命名套接口分配一个本地名字(即主机地址/端口号)来为套接口建立本地捆绑。
函数原型

在UNIX/Linux环境下,bind() 函数的原型如下:

#include <sys/types.h>  
#include <sys/socket.h>  
  
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

 

  • sockfd:标识要绑定的套接字的文件描述符。
  • addr:指向包含地址信息的结构体的指针,通常使用 struct sockaddr_in 表示IPv4地址,或 struct sockaddr_in6 表示IPv6地址。
  • addrlen:地址结构体的大小,可以用 sizeof 操作符获得。
返回值
  • 成功时,bind() 函数返回0。
  • 失败时,返回-1,并设置全局变量 errno 以指示错误原因。

3.recvfrom()

recvfrom() 函数是网络编程中的一个重要函数,特别是在使用UDP协议进行通信时。它用于从套接字接收数据,并可以返回发送数据的源地址。以下是recvfrom()函数的详细解析:

一、函数原型

在C语言中,recvfrom()函数的原型通常如下(以UNIX/Linux系统为例):

recvfrom() 函数是网络编程中的一个重要函数,特别是在使用UDP协议进行通信时。它用于从套接字接收数据,并可以返回发送数据的源地址。以下是recvfrom()函数的详细解析:

一、函数原型
在C语言中,recvfrom()函数的原型通常如下(以UNIX/Linux系统为例):

 

其中,参数的含义如下:

  • sockfd:已打开并绑定的套接字的文件描述符。
  • buf:指向缓冲区的指针,用于存储接收到的数据。
  • len:缓冲区的大小(以字节为单位),表示函数最多可以接收的数据量。
  • flags:控制接收行为的标志位,如MSG_DONTWAIT(非阻塞模式)、MSG_WAITALL(阻塞模式,直到接收到指定大小的数据)等。
  • src_addr:一个指向sockaddr结构体的指针,用于存储发送方的地址信息。如果不需要获取发送方的地址,可以设置为NULL。
  • addrlen:一个指向整型的指针,用于指定src_addr结构体的大小。在调用recvfrom()之前,应该将其设置为src_addr结构体的大小,在调用之后,它将被设置为新接收到的地址的实际大小。

二、返回值

  • 如果成功,recvfrom()返回接收到的字节数。
  • 如果连接被对方优雅地关闭,返回0。
  • 如果发生错误,返回-1,并设置全局变量errno来指示错误的原因。

三、常见错误

使用recvfrom()时,需要处理可能出现的各种错误情况,例如:

  • 如果套接字处于非阻塞模式并且没有数据可读,recvfrom()可能会返回EAGAINEWOULDBLOCK错误。
  • 其他可能的错误包括EBADF(无效的套接字描述符)、EFAULT(参数中有一指针指向无法存取的内存空间)、ENOTSOCK(参数s为一文件描述词,非socket)等。

4.sendto()

sendto 函数是网络编程中用于向指定的目的地址发送数据报的函数,特别是在使用UDP协议时非常常见。以下是对 sendto 函数的详细解析:

一、函数原型

在C语言中,sendto 函数的原型通常如下(以UNIX/Linux系统为例):

#include <sys/types.h>  
#include <sys/socket.h>  
  
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,  
               const struct sockaddr *dest_addr, socklen_t addrlen);

 

二、参数说明

  • sockfd:已打开的套接字文件描述符,该套接字必须已经通过 socket() 函数创建,并且对于UDP协议来说,不需要通过 connect() 函数建立连接。
  • buf:指向要发送数据的缓冲区的指针。
  • len:要发送的数据的长度(以字节为单位)。
  • flags:发送选项的标志位,通常为0,表示使用默认行为。但也可以设置其他标志位,如 MSG_DONTROUTE(绕过本地路由表)、MSG_DONTWAIT(非阻塞模式)等。
  • dest_addr:指向目的地址的指针,该地址必须是一个 sockaddr 结构体(或其特定类型的变体,如 sockaddr_in 用于IPv4),包含了目标主机的IP地址和端口号。
  • addrlen:目的地址的长度,对于 sockaddr_in 结构体,其长度通常为 sizeof(struct sockaddr_in)

三、返回值

  • 如果成功,sendto 返回发送的字节数。
  • 如果发生错误,返回-1,并设置全局变量 errno 来指示错误的原因。

四、使用注意事项

  1. 确保套接字有效:在调用 sendto 之前,必须确保 sockfd 是一个有效的、已经打开的套接字文件描述符。
  2. 目标地址正确:必须确保 dest_addr 指向的地址是正确的,包括IP地址和端口号。
  3. 检查返回值:在调用 sendto 后,应该检查其返回值以确保数据成功发送。
  4. 非阻塞模式:如果需要,可以通过设置 flags 参数为 MSG_DONTWAIT 来使 sendto 调用在非阻塞模式下工作。
  5. UDP特性:由于UDP是无连接的协议,sendto 函数不会关心目标地址是否可达或是否存在,只是简单地将数据报发送到网络上。

UDP协议的特点

UDP(User Datagram Protocol,用户数据报协议)是一种无连接的传输层协议,它基于IP协议,提供了一种将数据包发送到网络上的方式。UDP协议的特点主要包括以下几个方面:

1. 无连接性

  • UDP在发送数据之前不需要在源端和终端之间建立连接。发送方可以直接将数据包发送到目标主机,而无需等待确认或建立连接的过程。这减少了建立连接和断开连接的开销,并提高了传输效率。

2. 高效性

  • UDP的头部开销较小,只有8字节,相比TCP的20字节头部开销更小。这使得UDP在传输大量数据时效率更高,尤其是在对实时性要求较高的应用中。

3. 不可靠性

  • UDP不提供数据包的可靠性保证。数据包发送后,即使丢失也不会重新发送,也不保证数据包的顺序和完整性。这使得UDP在实时性要求高、丢失一些数据包不会影响整体传输效果的应用场景中更加适用。

4. 快速性

  • 由于UDP不需要等待建立连接,数据包可以立即发送到目标主机,因此UDP的延迟较低。这使得UDP非常适用于对实时性要求较高的应用,如音频和视频的实时传输。

5. 面向报文

  • UDP是面向报文的协议。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这意味着应用程序需要选择合适的报文大小。

6. 支持多种交互通信

  • UDP支持一对一、一对多、多对一和多对多的交互通信。这使得UDP在需要向多个客户端广播同一消息的应用场景中非常有用,如视频和音频的实时传输。

7. 无拥塞控制

  • UDP不使用拥塞控制机制。这意味着当网络出现拥塞时,UDP的发送速率不会降低,这可能导致更多的数据包丢失。但在某些实时应用中,如在线游戏和视频直播,这种特性反而有利于减少延迟和提高用户体验。

8. 适用于特定应用场景

  • 尽管UDP的不可靠性使得它不适用于所有类型的网络应用,但在实时性要求高、数据量较小且数据丢失对整体传输效果影响不大的场景中,UDP却表现出色。例如,UDP常用于DNS查询、DHCP服务器和客户端之间的通信、在线游戏和流媒体应用等。

综上所述,UDP协议以其无连接性、高效性、快速性等特点在特定应用场景中发挥着重要作用。然而,由于其不可靠性,UDP并不适用于所有类型的网络应用,特别是在对数据可靠性要求较高的场景中。

应用

#include <iostream>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <unistd.h>  
#include <cstring>  
  
#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};  
  
    // 创建 socket 文件描述符  
    if ((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0) {  
        perror("socket failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 绑定 socket 到端口 8080  
    address.sin_family = AF_INET;  
    address.sin_addr.s_addr = INADDR_ANY;  
    address.sin_port = htons(PORT);  
  
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {  
        perror("bind failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 设置 socket 选项  
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {  
        perror("setsockopt");  
        exit(EXIT_FAILURE);  
    }  
  
    while (true) {  
        // 接收数据  
        int len = recvfrom(server_fd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *)&address, (socklen_t*)&addrlen);  
        buffer[len] = '\0';  
        std::cout << "Message from client: " << buffer << std::endl;  
  
        // 发送响应(如果需要)  
        // sendto(server_fd, (const char *)buffer, strlen(buffer), MSG_CONFIRM, (const struct sockaddr *)&address, addrlen);  
    }  
  
    return 0;  
}

 

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱编程的小猴

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

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

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

打赏作者

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

抵扣说明:

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

余额充值