2. Network programming - UDP

1. Introduction

User Datagram Protocol is an unreliable and connectionless protocol. Before data is sent, it does not need to establish a connection in advance, which can transmit data more efficiently (send emails)

2. Scenarios used

(1) Send small size data

(2) In a network where it is difficult to receive data and give a response

(3) Broadcast/multicast communication

(4) QQ/WeChat and other instant communication software point-to-point file communication and audio and video calls

(5) Live room

 2. API interface

1. socket interface 

 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 (binding address)

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

 3. Address structure

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 (send data to 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 (receive data from 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:地址长度
返回值:
        成功:已接收字节数

3. The process of UDP creation

1. Server (receiving end)

(1) Create a mailbox (socket)

(2) Set the address

(3) Bind the address with the mind (bind)

(4) Waiting for a letter (recvfrom)

2. Client (sender)

(1) Create a mailbox (socket)

(2) Set the sending address

(3) Write the content of the letter

(4) Send a letter (the content is sent together with the address information)

4. Code

1. Server (receiving end)

#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. Client (sender)

#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;
}

Guess you like

Origin blog.csdn.net/weixin_45981798/article/details/129902888