2、网络编程——UDP

一、简介

用户数据报协议,是一种不可靠无连接的协议,在数据发送前,不需要提前建立连接,可以更高效传输数据(发邮件)

2、使用的场景

(1)发送小尺寸数据

(2)在收到数据,给出应答比较困难的网络中

(3)广播/组播式通信

(4)QQ/微信等及时通信软件地点对点文件通讯以及音视频通话

(5)直播间

 二、API接口

1、socket接口 

 int socket(int domain, int type, int protocol);
参数:
domain:域。
    AF_INET/PF_INET: 网际协议
    AF_UNIX/PF_UNIX:本地协议,可写成 AF_LOCAL/PF_LOCAL
type:类型。
    SOCK_STREAM:流式套接字 TCP协议
    SOCK_DGRAM:数据报套接字 UDP协议
protocol:协议。
    一般为 0
返回值:
    成功:待连接套接字
    失败:-1

2、bind(绑定地址)

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
    sockfd:待连接套接字
    addr:包含本地地址(IP+PORT)的通用地址结构体的指针
    addrlen:地址结构体大小
返回值:
    成功:0
    失败:-1

 3、地址结构体

struct sockaddr // 通用IP信息结构体
{
    sa_family_t sa_family;
    char sa_data[14];
}

struct sockaddr_in // IPV4地址结构体
{
    u_short sin_family;// 地址族
    u_short sin_port;// 端口
    struct in_addr sin_addr;// IPV4 地址
    char sin_zero[8];
};

struct in_addr // IP地址结构体
{
    in_addr_t s_addr;// 无符号 32 位网络地址
};

4、sendto( 发送数据到UDP)

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
    const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
    sockfd:UDP 套接字
    buf:即将发送的数据
    len:数据的长度
    flags:发送标志,与函数 send 的 flags 完全一致
    dest_addr:对端网络地址
    addr_len:地址长度
返回值:
    成功:已发送字节数
    失败:-1

5、recvfrom(从UDP中接收数据)

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
    参数:
        sockfd:UDP 套接字
        buf:储存数据缓冲区
        len:缓冲区大小
        flags:接收标志,与函数 send 的 flags 完全一致
        src_addr:对端网络地址
        addrlen:地址长度
返回值:
        成功:已接收字节数

三、UDP创建的流程

1、服务器(接收端)

(1)创建邮箱(socket)

(2)设置好地址

(3)将地址与心想绑定(bind)

(4)坐等来信(recvfrom)

2、客户端(发送端)

(1)创建一个邮箱(socket)

(2)设置发送的地址

(3)编写信件内容

(4)发送信件(内容与地址信息一起发送)

四、代码

1、服务器(接收端)

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  // 包含了地址结构体的定义声明
#include <netinet/in.h>

int main(int argc, char const *argv[])
{
    // 创建一个邮箱
    int socket_fd =  socket(AF_INET, SOCK_DGRAM , 0 );
    if (-1 == socket_fd)
    {
        perror("socket error");
        return -1 ;
    }
   
    // 设置好IP+端口号等信息
    // struct sockaddr_in   // IPV4地址结构体
    // {
    //     u_short sin_family;// 地址族
    //     u_short sin_port;// 端口
    //     struct in_addr sin_addr;// IPV4 地址
    //     char sin_zero[8];
    // };

    struct sockaddr_in my_addr = {0} ;
    int addr_len = sizeof(struct sockaddr_in);

    my_addr.sin_family = AF_INET ; // 网际协议 IPV4
    my_addr.sin_port = htons(65000); // 设置端口号为65000 并转换为网络字节序,范围:0~65535
    my_addr.sin_addr.s_addr = inet_addr("192.168.102.2");  // 设置IP地址, 并转换为32位的网络地址
    //在终端ifconfig取地址

    // 把设置好的信息与信箱进行帮定
    int ret_val = bind(socket_fd , (struct sockaddr *)&my_addr,addr_len);
    if (-1 == ret_val)
    {
        perror("bind error");
        return -1 ;
    }
    
    char * msg = calloc(128,1);//char型的指针
    struct sockaddr_in from_addr ;

    // 坐等来信
    while(1)
    {
        // ret_val = read(socket_fd , msg , 128); // 可以直接使用读取函数来读取数据,但是没办法得知数据来自哪里
        ret_val = recvfrom(socket_fd , msg , 128 , 0 , (struct sockaddr *)&from_addr, &addr_len );
        if (ret_val == -1 )
        {
            perror("recv error");
            continue ;
        }
        else{

            printf("recv succeed \n addr:%s \nport:%d \nmsg : %s \n" ,
                       inet_ntoa( from_addr.sin_addr ) ,  // 把二进制的网络字节序的IP地址转换点分十进制的字符串地址
                       ntohs(from_addr.sin_port) , // 把网络字节序的端口号转换位 本地字节序的短整型
                        msg ); 
        }
    }
    return 0;
}

2、客户端(发送端)

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  // 包含了地址结构体的定义声明
#include <netinet/in.h>


int main(int argc, char const *argv[])
{
    // 创建一个邮箱
    int socket_fd =  socket(AF_INET, SOCK_DGRAM , 0 );
    if (-1 == socket_fd)
    {
        perror("socket error");
        return -1 ;
    }
    
    // 设置好IP+端口号等信息
    // struct sockaddr_in   // IPV4地址结构体
    // {
    //     u_short sin_family;// 地址族
    //     u_short sin_port;// 端口
    //     struct in_addr sin_addr;// IPV4 地址
    //     char sin_zero[8];
    // };

    struct sockaddr_in dest_addr = {0} ;
    int addr_len = sizeof(struct sockaddr_in);

    dest_addr.sin_family = AF_INET ; // 网际协议 IPV4
    dest_addr.sin_port = htons(65000); // 设置端口号为65000 并转换为网络字节序
    dest_addr.sin_addr.s_addr = inet_addr("192.168.102.2");  // 设置IP地址, 并转换为32位的网络地址


    char * msg = calloc(128,1);
    struct sockaddr_in from_addr ;
    int ret_val = -1 ;

    // 坐等来信
    while(1)
    {
        printf("请输入需要发送的消息:\n");
        fgets(msg , 128 , stdin) ;//stdin指的是从哪里输入

        ret_val = sendto(socket_fd , msg , strlen(msg) , 0 , 
                    (struct sockaddr * )&dest_addr, addr_len );
    
        if ( -1 == ret_val )
        {
            perror("send to error ");
            continue ;
        }
        else{
            printf("send succeed %d byte ! \n" , ret_val);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45981798/article/details/129902888