Administrator
Administrator
发布于 2024-10-16 / 21 阅读
0
0

UDP套接字编程的主要步骤

UDP(用户数据报协议)是无连接的传输层协议,与TCP不同,UDP不需要建立连接,也不保证数据的顺序和可靠性。因此,UDP的通信模型要简单得多,适合那些对实时性要求较高但可以容忍部分数据丢失的应用,如视频流、DNS查询等。

UDP套接字编程的主要系统调用:

  • socket()
  • bind()
  • sendto() / recvfrom()
  • close()

这些调用与TCP类似,但不需要 listen()accept(),因为UDP是无连接的。下面是详细介绍:


1. socket()

socket() 用于创建一个UDP套接字。

int socket(int domain, int type, int protocol);
  • domain:协议族,通常为 AF_INET(IPv4)或 AF_INET6(IPv6)。
  • type:套接字类型,UDP使用 SOCK_DGRAM
  • protocol:一般设为0,表示默认协议(UDP协议)。

返回值:成功时返回套接字的文件描述符,失败时返回 -1。

示例:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
    perror("socket creation failed");
    exit(EXIT_FAILURE);
}

2. bind()

bind() 用于将UDP套接字绑定到一个本地地址(IP地址和端口号)。在服务器端程序中,通常需要bind()来确保数据发送到正确的端口。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfdsocket()函数返回的套接字文件描述符。
  • addrstruct sockaddr_in,包含要绑定的本地地址(IP和端口)。
  • addrlenaddr结构体的大小。

返回值:成功时返回0,失败时返回-1。

示例:

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;  // 绑定到本地IP地址
server_addr.sin_port = htons(8080);        // 绑定到端口8080

if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}

3. sendto()

sendto() 用于发送数据到指定的目标地址(IP地址和端口)。因为UDP是无连接的,所以每次发送时都要指定目标。

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:目标地址(包含目标IP和端口的 struct sockaddr_in)。
  • addrlen:目标地址结构体的大小。

返回值:成功时返回发送的字节数,失败时返回-1。

示例:

struct sockaddr_in client_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &client_addr.sin_addr);

const char *message = "Hello, UDP!";
sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&client_addr, sizeof(client_addr));

4. recvfrom()

recvfrom() 用于从指定的源地址接收数据。此函数适用于UDP,因为UDP是无连接的,数据可能从任何源地址到达。

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:发送方的地址,存储发送方的IP地址和端口。
  • addrlen:src_addr结构体的大小。

返回值:成功时返回接收的字节数,失败时返回-1。

示例:

char buffer[1024];
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);

int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, &addr_len);
buffer[n] = '\0';  // 确保接收到的数据以\0结束
printf("Received from client: %s\n", buffer);

5. close()

close() 用于关闭UDP套接字,释放与之关联的资源。

int close(int sockfd);
  • sockfd:套接字文件描述符。

返回值:成功时返回0,失败时返回-1。

示例:

close(sockfd);

完整的UDP服务器和客户端示例

UDP服务器端示例

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080

int main() {
    int sockfd;
    char buffer[1024];
    struct sockaddr_in server_addr, client_addr;

    // 创建UDP套接字
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 绑定服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    int len, n;
    len = sizeof(client_addr);

    // 接收客户端发送的数据
    n = recvfrom(sockfd, (char *)buffer, 1024, 0, (struct sockaddr *)&client_addr, &len);
    buffer[n] = '\0';
    printf("Client : %s\n", buffer);

    // 向客户端发送回复
    const char *reply = "Hello from server";
    sendto(sockfd, reply, strlen(reply), 0, (const struct sockaddr *)&client_addr, len);
    printf("Hello message sent.\n");

    // 关闭套接字
    close(sockfd);
    return 0;
}

UDP客户端示例

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080

int main() {
    int sockfd;
    char buffer[1024];
    struct sockaddr_in server_addr;

    // 创建UDP套接字
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    // 向服务器发送消息
    const char *message = "Hello from client";
    sendto(sockfd, message, strlen(message), 0, (const struct sockaddr *)&server_addr, sizeof(server_addr));
    printf("Hello message sent.\n");

    // 接收服务器的回复
    int n, len = sizeof(server_addr);
    n = recvfrom(sockfd, (char *)buffer, 1024, 0, (struct sockaddr *)&server_addr, &len);
    buffer[n] = '\0';
    printf("Server : %s\n", buffer);

    // 关闭套接字
    close(sockfd);
    return 0;
}

总结:UDP套接字编程的主要步骤

  1. socket():创建UDP套接字。
  2. bind():在服务器端绑定本地地址和端口。
  3. sendto():发送数据到目标地址。
  4. recvfrom():从指定的源地址接收数据。
  5. close():关闭套接字。

UDP编程的逻辑相对简单,但没有连接管理和传输保证。因此,程序设计时需要考虑数据丢失和无序到达的问题。


评论