Linux UDP套接字编程

概述

相较于TCP而言,UDP通信的形式更像是发短信。不需要在数据传输之前建立、维护连接。只专心获取数据就好。省去了三次握手的过程,通信速度可以大大提高,但与之伴随的通信的稳定性和正确率便得不到保证。因此,我们称UDP为“无连接的不可靠报文传递”。
由于无需创建连接,所以UDP开销较小,数据传输速度快,实时性较强。多用于对实时性要求较高的通信场合,如视频会议、电话会议等。但随之也伴随着数据传输不可靠,传输数据的正确率、传输顺序和流量都得不到控制和保证。所以,通常情况下,使用UDP协议进行数据传输,为保证数据的正确性,我们需要在应用层添加辅助校验协议来弥补UDP的不足,以达到数据可靠传输的目的。


UDP客户/服务器程序所使用的套接字函数

这里写图片描述


UDP缓冲区问题

与TCP类似的,UDP也有可能出现缓冲区被填满后,再接收数据时丢包的现象。由于它没有TCP滑动窗口的机制,通常采用如下两种方法解决:

  1. 服务器应用层设计流量控制,控制发送数据速度。
  2. 借助setsockopt函数改变接收缓冲区大小。如:
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
int n = 220x1024
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));

UDP处理模型

UDP处理模型


recvfrom和sendto函数

recvfrom和sendto函数类似于read和write,但需要三个额外的参数

#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);

ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t addrlen);

recvfrom参数
- sockfd : 套接字描述符
- buff : 用于存放数据的缓冲区
- nbytes : 缓冲区大小
- flags : 暂时总设置为0
- from : 用于存放UDP对端的套接字协议地址
- addrlen : UDP对端的套接字协议地址字节大小

sendto参数

  • sockfd : 套接字描述符
  • buff : 要发送的数据内存
  • nbytes : 数据大小
  • flags : 暂时总设置为0
  • to : 指向接收者的套接字地址结构
  • addrlen : 上述套接字地址结构to的字节大小

recvfrom的最后两个参数类似于accept的最后两个参数:返回时套接口地址结构的内容告诉我们是谁发送了数据报(UDP)或是谁发起了连接(TCP)。sendto的最后两个参数类似于connect的最后两个参数:我们用数据报将发往(UDP)或与之建立连接(TCP)的协议地址来装填套接口地址结构。
写一个长度为0的数据报是可行的,这也意味着对于数据报协议,recvfrom返回0值也是可行的;它不表示对方已经关闭了连接,这与TCP套接口上的read返回0的情况不同。由于UDP是无连接的,这就没有诸如关闭UDP连接之类的事情。


代码示例

发送端

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{
    // 创建UDP socket
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10010);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(sock, (struct sockaddr*)&addr, sizeof(addr));

    // 使用sendto,带上目标地址
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(10011);
    sendto(sock,"hello udp",10,0,(struct sockaddr*)&addr,sizeof(addr));


    char buf[1024];
    read(sock,buf,sizeof(buf));

    printf("%s\n",buf);

    return 0;
}

接收端

int main()
{
    // 创建
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    // 绑定端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10011);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(sock, (struct sockaddr*)&addr, sizeof(addr));

    struct sockaddr peer_addr;
    socklen_t addr_len = sizeof(peer_addr);

    // recvfrom,得到数据的同时,也得到对方的地址
    char buf[1024];
    memset(buf,0,sizeof(buf));
    recvfrom(sock,buf,sizeof(buf),0,&peer_addr, &addr_len);

    printf("%s\n",buf);

    // 通过recvfrom获取到的地址发送数据给对方
    sendto(sock,"hello",6,0,&peer_addr,sizeof(peer_addr));

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值