嵌入式笔记 Linux—— 《UDP网络编程》

UDP协议

:UDP是面向无连接的用户数据报协议,在传输数前不需要先建立连接。目地主机的运输层收到UDP报文后,不需要给出任何确认

UDP协议与TCP协议的差异:

如何在TCP和UDP之间取舍

  1. 广播和多播应用必须使用UDP
  2. 简单的请求-应答应用程序可以使用UDP
  3. 对于海量数据传输不应该使用UDP

UDP的使用场合:
DNS、NFS、流媒体传输等等

基本UDP编程

recvfrom()函数

ssize_t recvfrom(int sockfd, void *buf,size_t nbytes,int flags,structsockaddr *from, socklen_t *addrlen);

功能:用于接收数据

参数:

  1. sockfd:套接字
  2. buf: 接收数据缓冲区
  3. nbytes:接收数据缓冲区的大小
  4. flags: 套接字标志(常为0)
  5. from: 用于存放发送方信息的地址结构体指针
  6. addrlen: from所指内容的长度

返回值:成功:接收到的字符数 失败:-1

注意:

struct sockaddr *from, socklen_t *addrlen
类似于accept函数的最后两个参数
通过from和addrlen参数存放数据来源
可以为NULL, 表示不关心数据来源

sendto()函数

ssize_t sendto(int sockfd,const void*buf, size_t nbytes,int flags, conststructsockaddr *to,socklen_t addrlen);

功能:用于发送数据

参数:

  1. sockfd:套接字
  2. buf:发送数据缓冲区
  3. nbytes:发送数据缓冲区的大小
  4. flags:一般为0
  5. to:指向目的主机地址结构体的指针
  6. addrlen:to所指向内容的长度

注意:

const struct sockaddr *to,socklen_t addrlen
类似于connect函数的最后两个参数
通过to和addrlen确定目的地址
发送一个0长度的UDP数据包是可行的

返回值:成功:发送的字符数 失败:-1


UDP_server示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc,char *agrv[])
{
    int sockfd = 0;//存放套接字
    int error_log = 0;//存放错误标志
    struct sockaddr_in bind_addr;//存放不通用地址结构体
    unsigned short port = 8000;//存放端口号

    if(argc > 1)//通过传参 更改端口号
    {
        port = atoi(argv[1]);
    }

    printf("UDP Server Started!\n");

    sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字 : IPv4 数据报式套接字
    if(sockfd < 0)
    {
        perror("socket error");
        exit(-1);
    }

    /* 清空结构体,并赋值进行绑定 */
    bzero(&bind_addr,sizeof(bind_addr));
    bind_addr.sin_family = AF_INET;
    bind_addr.sin_port = htos(port);
    bind_addr.sin_addr.s_addr = htol(INADDR_ANY);

    printf("Binding server to port %d\n",port);
    /* 建立绑定 */
    error_log = bind(sockfd,(struct sockaddr*)&bind_addr,szieof(bind_addr));
    if(error_log != 0)
    {
        perror("bind error");
        close(sockfd);
        exit(-1);
    }

    printf("Waiting data form other client...\n");

    while(1)
    {
        char recv_buf[1024] = "";//接收缓存区
        char cli_ip[INET_ADDRSTRLEN] = "";
        int recv_len = 0;

        struct sockaddr_in client_addr;
        socklen_t client_addr_len = sizeof(client_addr);

        /* 接收 */
        recv_len = recvfrom(sockfd,recv_buf,sizeof(recv_buf),0,\
        (struct sockaddr*)&client_addr,&client_addr_len);

        /* 将IP地址转换为点分十进制的形式 */
        inet_ntop(AF_INET,&client_addr.sin_addr,cli_ip,INET_ADDRSTRLEN);
        printf("client ip = %s\n",cli_ip);

        /* 发送 */
        sendto(sockfd,recv_buf,recv_len,0,\
        (struct sockaddr*)&client_addr,client_addr_len);

        /* 服务器将接收到的数据 打印出来 并重新发回去 */
    }

    close(sockfd);

    return 0;
}


UDP_client示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
    int sockfd = 0;//定义套接字
    struct sockaddr_in server_addr;//非通用套接字地址结构体
    unsigned short port = 8000;//服务器的端口号
    char *server_ip = "192.168.222.183";//服务器的IP地址

    if( argc > 1)//利用传参,更改服务器的IP地址
    {
        server_ip = argv[1];
    }

    if( argc > 2)//利用传参,更改服务器的端口号
    {
        port = atoi(argv[2]);
    }

    sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建通信端点:UDP套接字
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    } 

    bzero(&server_addr,sizeof(server_addr));//初始化服务器地址
    server_addr.sin_family = AF_INET;//IPv4
    server_addr.sin_port = htons(port);//端口号
    inet_pton(AF_INET,server_ip,&server_addr.sin_addr);

    printf("ready send data to UDP server %s:%d!\n",server_ip,port);

    while(1)
    {
        char send_buf[2048] = "";
        int len = 0;

        /* 从键盘接收要发送的信息 */
        fgets(send_buf,sizeof(send_buf),stdin);
        send_buf[strlen(send_buf)-1] = '\0';

        /* 发送 */
        sendto(sockfd,send_buf,strlen(send_buf),0,\
        (struct sockaddr*)&server_addr,sizeof(server_addr));
        
        /* 接收 */
        len = recvfrom(sockfd,send_buf,sizeof(send_buf),0,NULL,NULL);
        printf("%s\n",send_buf);
    }

    close(sockfd);

    return 0;
}   

UDP广播

:由一台主机向该主机所在子网内的所有主机发送数据的方式

广播只能用UDP或原始IP实现,不能用TCP

广播的用途

  1. 单个服务器与多个客户主机通信时减少分组流通
  2. 地址解析协议(ARP)
  3. 动态主机配置协议(DHCP)
  4. 网络时间协议(NTP)

广播地址

{子网ID,主机ID}
子网ID表示由子网掩码中1覆盖的连续位
主机ID表示由子网掩码中0覆盖的连续位

子网定向广播地址:主机ID全1
例如:对于192.168.220.0/24子网,192.168.220.255即为其定向广播地址¾通常路由器不转发该广播

受限广播地址:255.255.255.255
路由器从不转发该广播
通常在DHCP等应用中把该地址当做宿主地址,因为此时客户主机还不知道所处子网的信息

UDP广播的特点

处于同一子网的所有主机都必须处理数据
UDP数据包会沿协议栈向上一直到UDP层
运行音视频等较高速率工作的应用,会带来大负担
局限于局域网内使用

设置套接口选项

int setsockopt(int sockfd, int level,int optname, const void *optval, socklen_t optlen);

返回值: 成功:返回0,否则返回:-1

UDP广播示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
    int sockfd = 0;//定义套接字
    char buf[1024] = "";

    unsigned short port = 8000;//端口号
    struct sockaddr_in send_addr;//非通用套接字地址结构体
    char *server_ip = "";//服务器的IP地址

    if( argc > 1)//利用传参,更改IP地址
    {
        server_ip = argv[1];
    }
    else
    {
        printf("not have a server IP \n");
        exit(-1);
    }     

    sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建通信端点:UDP套接字 数据报式
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }

    /* 初始化套接字地址结构体 */
    bzero(&send_addr,sizeof(send_addr));
    send_addr.sin_family = AF_INET;//IPv4
    send_addr.sin_port = htons(port);//端口号
    inet_pton(AF_INET,server_ip,&send_addr.sin_addr);

    int yes = 1;

    /* 设置套接字选项 :广播 */
    setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&yes,sizeof(yes));
    /* 要广播的数据 */
    strcpy(buf,"boardcast sucess !");
    /* 将信息发送到广播 */
    int len = sendto(sockfd,buf,strlen(buf),0,\
    (struct sockaddr *)&send_addr,sizeof(send_addr));

    if(len < 0)
    {
        printf("send error \n");
        close(sockfd);
        exit(-1);
    }   

    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

数字梦想家

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

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

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

打赏作者

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

抵扣说明:

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

余额充值