Linux-UDP编码接口以及代码实现

Posted 天津 唐秙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux-UDP编码接口以及代码实现相关的知识,希望对你有一定的参考价值。

1. udp的编码接口

1.1 udp的编程流程

在这里插入图片描述

1.2 绑定地址信息

  int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
在这里插入图片描述
  sockfd:socked函数返回的套接字描述符,将创建出来的套接字和网卡、端口进行绑定
  addr:地址信息结构
在这里插入图片描述
  sa_family_t sa_family;//地址域信息,占用2个字节,地址信息决定了当前使用什么网络协议(AF_INET, AF_INET6, AF_UNIX)
  char sa_data[14];//本质上任何网络程序都不会直接填充这14个字节的字符数组,定义第二个参数,总共占用14个字节。
  addrlen:地址信息结构的长度

ipv4:
  struct sockaddr_in{…}
  路径:vim/usr/include/netinet/in.h
在这里插入图片描述
  地址域信息,AF_INET,AF_INET6, AF_UNIX:2字节
  端口信息:2字节
  ipv4版本的ip地址:4字节

通用的数据结构:
在这里插入图片描述
ipv4的数据结构:
在这里插入图片描述
  网络协议栈通过地址域信息来判断传递进来的是哪一个数据结构,判断出来之后,就知道后面的字节如何进行解析。

在这里插入图片描述

1.3 发送接口

  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,port)
  addrlen:目标主机地址信息结构的长度
返回值:
  成功:返回具体发送的字节数量
  失败:-1

问题:客户端为什么不推荐绑定地址信息?
  本质上是不想让客户端程序端口写死,因为一个端口只能被一个一个进程所绑定,如果客户端在启动的时候绑定的都是一个端口,那么当其他客户端进行绑定的时候,就会绑定失败,而如果客户端没有主动绑定端口,udp客户端在调用sendto的时候,会自动绑定一个空闲的端口,这个端口是操作系统分配的一个空闲的端口。

1.4 接收接口

  ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
在这里插入图片描述
  sockfd:套接字描述符
  buf:将数据接收到buf当中
  len:buf的最大接收能力
  flags:
    0阻塞接收
  src_addr:这个数据来源的主机的地址信息结构
  addrlen:输入输出型参数
  输入:在接收之前准备的对端地址信息结构的长度
  输出:实际接收回来的地址信息长度
返回值:
  接收的字节数量
  -1

命令: netstat -anp | grep [端口]
功能: 查看端口信息

  close(int sockfd)

1.5 主机字节序和网络字节序之间转换的函数

主机字节序转换成为网络字节序

uint32_t htonl(uint32_t hostlong);//host to network long
uint16_t htons(uint16_t hostshort);//host to network short

网络字节序转换成为主机字节序

uint32_t ntohl(uint32_t netlong);//network to host long
uint16_t ntohs(uint16_t netshort);//network to host short

将字符串的点分十进制的ip地址转换成为uint32_t

in_addr_t inet_addr(const char *cp);

将uint32_t从主机字节序转换成为网络字节序

char *inet_ntoa(struct in_addr in);

1.6 代码

客户端(client)

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

int main()
{
    //int socket(int domain, int type, int protocol);
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0)
    {
        perror("sockfd failed");
        return 0;
    }

    while(1)
    {
        sleep(1);
        char buf[1024] = "i am client";

        //通用结构体
        struct sockaddr_in  dest_addr;
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(18989);
        dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

        //ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
        //                      const struct sockaddr *dest_addr, socklen_t addrlen);
        int sendto_ret = sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));

        if(sendto_ret < 0)
        {
            perror("sendto failed");
            return 0;
        }

        memset(buf, '\\0', sizeof(buf));
        //ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
        //                        struct sockaddr *src_addr, socklen_t *addrlen);
        ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, NULL, NULL);
        if(recv_size < 0)
        {
            perror("recvfrom failed");
            continue;
        }
        printf("server say: %s\\n", buf);
    }
    return 0;
}

服务端(server)

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

int main()
{
    // int socket(int domain, int type, int protocol);
    // damain 当前的地址域使用什么协议
    //        AF_INET:ipv4
    //        AF_INET6:ipv6
    //        AF_UNIX:本地域套接字
    // type 创建套接字类型
    //        SOCK_DGRAM 用户数据报套接字 UDP协议
    //        SOCK_STREAM 流式套接字 TCP协议
    // protocol 使用什么类型的套接字协议
    //        0:表示套接字类型默认的协议
    //        IPPROTO_UDP:17
    //        IPPROTO_TCP:6
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0)
    {
        perror("socket failed");
        return 0;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    //主机字节序转换成为网络字节序
    addr.sin_port = htons(18989);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int bind_ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(bind_ret < 0)
    {
        perror("bind failed");
        return 0;
    }

    while(1)
    {
        //接收
    
        char buf[1024] = {0};
    
        sockaddr_in addr_recvfrom;
        socklen_t addr_recvfromlen = sizeof(addr_recvfrom);
        //ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
        //                        struct sockaddr *src_addr, socklen_t *addrlen);
        ssize_t recvfrom_ret = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&addr_recvfrom, &addr_recvfromlen);
        if(recvfrom_ret < 0)
        {
            continue;
        }
        printf("i am server, i recv %s from %s:%d\\n", buf, inet_ntoa(addr_recvfrom.sin_addr), ntohs(addr_recvfrom.sin_port));

        //发送
        memset(buf, '\\0', sizeof(buf));
        sprintf(buf, "client: %s : %d, i am server", inet_ntoa(addr_recvfrom.sin_addr), ntohs(addr_recvfrom.sin_port));

        sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr_recvfrom, sizeof(addr_recvfrom));
    }
    return 0;
}

以上是关于Linux-UDP编码接口以及代码实现的主要内容,如果未能解决你的问题,请参考以下文章

java 代码片段

从父片段到选项卡片段的接口侦听器不起作用

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情

markdown 打字稿...编码说明,提示,作弊,指南,代码片段和教程文章

是否可以动态编译和执行 C# 代码片段?

golang代码片段(摘抄)