UDP 编程

爷,独闯天下 提交于 2020-01-18 01:30:52

2.1 字节序,地址转换

        2.1.1字节序概述

               字节序概念:

                        多字节数据的存储顺序

               分类:

                   小端格式:低位字节数据存储在低地址

                  大端格式:高位字节数据存储在低地址

               注意:

                         LSB:低地址

                        MSB:高地址

 

 

        确定主机字节序程序:

#include <stdio.h> 
int main(int argc, char *argv[])
 { 
    union 
    { 
        short s; 
        char c[sizeof(short)]; 
    }un; 
    un.s = 0x0102; 
    if((un.c[0] == 1) && (un.c[1] == 2))
    { 
        printf("big-endian\n"); 
    } 
    else if((un.c[0] == 2) &&(un.c[1] == 1)) 
    {
         printf("little-endian\n"); 
    } 
    return 0; 
}

 

    特点:

  •             网络协议指定通讯字节序——大端
  •            多字节数据处理时考虑字节序
  •            运行在主机的进程互相通信,不用考虑字节序
  •             异构计算机通信,需要转换字节序为网络字节序

 

2.1.2 htonl函数

/*
功能:
将32位主机字节序数据转换成网络字节序数据

参数:
hostint32:待转换的32位主机字节序数据

返回值:
成功:返回网络字节序的值
*/
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostint32);

 

2.1.3 htons函数

/*
功能:
将16位主机字节序数据转换网络字节序数据

参数:
uint16_t:unsigned short int
hostint16:待转换16位主机字节序数据

返回值:
成功:返回网络字节序的值
*/
#include <arpa/inet.h>
uint16_t htons(uint16_t hostint16);

 

2.1.4 ntohl函数

/*
功能:
将32位网络字节序数据转换为主机字节序数据

参数:
uint32_t:usigned int
netint32_t:待转换32位网络字节序数据

返回值:
成功:返回主机字节序的值
*/

#include <arpa/inet.h>
uint32_t ntohl(uint32_t netint32);

 

2.1.5 ntohs函数

/*
功能:
将16位网络字节序数据转换主机字节序数据

参数:
uint16_t:unsigned short int
netint16:待转换的16位网络字节序数据

返回值:
成功:返回主机字节序的值
*/

#include <arpa/inet.h>
uint16_t ntohs(uint16_t netint16);

 

示例:

#include <stdio.h> 
#include <arpa/inet.h> 
int mian(int argc, char *argv[]) 
{ 
    int a = 0x01020304;
    short int b = 0x0102; 
    printf("htonl(0x%08x) = 0x%08x \n",a,htonl(a)); 
    printf("htonl(0x%04x) = 0x%04x \n",b,htons(b)); 
    return 0; 
}

 

2.1.6 地址转换函数

2.1.6.1 inet_pton函数

        字符串IP地址转整型数据

/*
功能:
将点分十进制数串转换32位无符号整型

参数:
family :协议族
strptr:点分十进制数串
addrptr:32位无符号整型的地址

返回值:
成功返回 1
失败返回 其他
*/
#include <arpa/inet.h>
int inet_pton(int family,const char *strptr,void *addrptr);

 

例:

#include <stdio.h> 
#include <arpa/inet.h> 
int main(int argc, char *argv[]) 
{ 
    char ip_str[] = "172.20.226.11"; 
    unsigned int ip_uint = 0; 
    unsigned char *ip_p = NULL; 
    inet_pton(AF_INET,ip_str,&ip_uint); 
    printf("ip_uint = %d\n",ip_uint); 
    ip_p = (unsigned char *)&ip_uint; 
    printf("ip_uint = %d.%d.%d\n",*ip_p ,*(ip_p + 1) ,*(ip_p +2) ,*(ip_p + 3)); 
    return 0; 
}

2.1.6.2 inet_ntop函数

          整型数据转换字符串格式IP地址

/*
功能:

参数:

返回值:

*/
#include <arpa/inet.h>
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

例:

#include <stdio.h> 
#include <arpa/inet.h> 
int mian() 
{ 
    unsigned char ip[] = {172,20,223,75};
    char ip_str[16] = "NULL";
    inet_ntop(AF_INET,(unsigned int *)ip,ip_str,16);
    printf("ip_str = %s\n",ip_str); 
    return 0; 
}

 

 

2.2 UDP介绍 编程流程

2.2.1 UDP概述

       UDP协议

           面向无连接的用户数据报协议,在传输数据前不用建立连接,目的主机的传输层收到UDP报文,不用做任何确认

       UDP特点

  •    比TCP速度快
  •    简单请求/应答应用程序用UDP
  •    海量数据传输不用UDP
  •    广播和多播应用用UDP

         UDP应用

              DNS(域名解析),NFS(网络文件系统),RTP(流媒体)

 

2.2.2 网络编程接口socket

       网络通信要解决的是不同主机进程间的通信

  •     网络间进程标识问题
  •     多重协议的识别问题

          socket应用

              提供不同主机的进程间通信

         socket特点

  •       socket 为套接字
  •         文件描述符,代表一个通信通道的一个端点
  •         read, write,close函数对套接字进行网络数据的收取和发送等操作

 

2.2.3 UDP编程 C/S架构

 

2.3UDP编程-创建套接字

 

2.3.1 创建socket套接字

/*
功能:
创建用于网络通信的socket套接字(描述符)

参数:
family:协议族 (AF_INET,AF_INET6,PACKET)
type:套接字类 (SOCK_STREAM,,SOCK_DGRAM,SOCK_RAM)
protocol:协议类别 (0,IPPROTO_TCP,IPPROTO_UDP)

返回值:

*/
#include <sys/socket.h> int socket(int family,int type,int protocol);

           套接字

              特点:

                  创建套接字,系统不分配端口

                  创建套接字默认属性为主动,主动发起服务的请求;当服务器,修改为被动

 

2.3.2 创建UDP套接字demo

/*
注意:
AF_INET:IPv4协议
SOCK_DGRAM:数据报套接字
0:family 和 type 组合的系统默认值
*/
int sockfd = 0; 
sockfd = socket(AF_INET,SOCK_DGRAM,0); 
if(sockfd < 0) 
{ 
    perror("socket"); 
    exit(-1); 
}

 

2.4 UDP编程-发送,绑定,接收数据

2.4.1 IPv4套接字地址结构

#include <netinet/in.h> 
struct in_addr 
{ 
    in_addr_t s_addr; //4字节 
}; 
struct sockaddr_in 
{ 
    sa_family_t sin_family;      //2字节 
    in_port_t sin_port;          //2字节 
    struct in_addr sin_addr;     //4字节 
    char sin_zero[8];            //8字节 
};

 

2.4.2 通用套接字地址结构

为了不同格式地址传入套接字,地址强制转换为通用套接字地址格式

#include <netinet/in.h> struct sockaddr { sa_family_t sa_family; //2字节 char sa_data[14]; //14字节 };

注意:

以上3个结构在Linux系统中已定义

 

2.4.3 俩种地址结构使用场合

定义源地址和目的地址结构时,用struct sockaddr_in

例:

struct sockaddr_in my_addr;

函数传入地址结构要用 struct sockaddr 进行强制转换

例:

bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));

 

2.4.4 发送数据——sendto函数

/*
功能:
向to结构体指针指IP,发送UDP数据

参数:
sockfd:套接字
buf:发送数据缓冲区
nbytes:发送数据缓冲区的大小
flags:为0
to:指向目的主机地址结构体的指针
addrlen:to指向的内容长度

注意:
通过to和addrlen确定目的地址
可发送0长度的UDP数据包

返回值:
成功:发送数据的字符数
失败: -1
*/
ssize_t sendto(int sockfd, 
               const void *buf, 
               size_t nbytes, 
               int flags, 
               const struct sockaddr *to, 
               socklen_t addrlen);

 

2.4.5 向“网络调试助手”发送消息

 

#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[]) 
{ 
    unsigned short port = 8080; 
    char *server_ip = "172.20.226.1"; 

    if(argc > 1) 
    { 
        serveer_ip = argv[1]; 
    } 
    if(argc > 2) 
    { 
        prot = atoi(argv[2]); 
    } 

    int sockfd; 
    sockfd = socket(AF_INET,SOCK_DGRAM,0); 
    if(sockfd >0) 
    { 
        perror("socket"); exit(-1); 
    } 

    struct sockaddr_in dest_addr; 
    bzero(&dest_addr,sizeof(dest_addr)); 
    dest_addr.sin_family AF_INET; 
    dest_addr.sin_port = htons(port); 
    inet_pton(AF_INET,server_ip,&dest_addr.sin_addr); 

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

    while(1) 
    { 
        char send_buf[512] = " "; 
        fgets(send_buf,sizeof(send_buf),stdin); 
        send_buf[strlen(send_buf)-1] = '\0'; 
        sendto(sockfd, 
               send_buf, 
               strlen(send_buf), 
               0, 
               (struct sockaddr *)&dest_addr,
               sizeof(dest_addr)); 
    } 
    close(sockfd); 
    return 0; 
}

 

 

 

2.4.6 绑定bind函数

         UDP网络收取数据的条件:

            确定IP地址

             确定port

              怎么完成条件:

                接收端:

                       bind函数,完成地址结构与 socket套接字的绑定,IP和port就固定

             发送端:

                   sendto函数,指定接收端的IP,port,发数据


 

/*
功能:
本地协议地址与sockfd绑定

参数:
sockfd:socket套接字
myaddr:特定协议的地址结构指针
addrlen:地址结构的长度

返回值:
成功:返回 0
失败:其他
*/
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

2.4.7 bind示例

#include <sys/socket.h> 

int err_log = 0; 
unsigned short port = 8000; 
struct sockaddr_in my_addr; 

bzero(&my_addr,sizeof(my_addr)); 
my_addr.sin_family = AF_INET; 
my_addr.sin_port = htons(port); 
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
err_log =bind(sockfd,(struct sockaddr *)&my_addr,sizeof(my_addr)); 

if(err_log != 0) 
{ 
    perror("bind"); 
    close(sockfd); 
    exit(-1); 
}

注意:

INADDR_ANY 通配地址 为 0

2.4.8 接收数据—recvfrom函数

/* 
功能:
接收UDP数据,把源地址信息保存在form指向的结构中 

注意:
通过from和addrlen参数存放数据来源信息 from和addrlen可以为NULL,表示不保存数据来源 

返回值:
成功:接收的字符数 
失败:-1 
*/ 
ssize_t recvfrom(int sockfd, //套接字 
                 void*buf, //接收数据缓冲区 
                 size_t nbytes, //接收数据缓冲区的大小 
                 int flags, //套接字标志 
                 struct sockaddr *from, //源地址结构体指针,保存数据 
                 socklen_t *addrlen); //from指向内容的长度

 

2.4.9 接收“网络调试助手”的数据

 

 

 

 

#include <stido.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[]) 
{ 
    unsigned short port = 8000; 
    if(argc > 1) //修改本程序的端口 
    { 
        port = atoi(argv[1]); 
    } 
    int sockfd; 
    sockfd =socket(AF_INET,SOCK_DGRAM,0); 
    if(sockfd > 0) //创建套接字 
    { 
        perror("socket"); 
        exit(-1); 
    } 
    struct sockaddr_in my_addr; //填充本程序的信息 
     bzero(&my_addr,sizeof(my_addr)); 
     my_addr.sin_family = AF_INET; 
      my_addr.sin_port = htons(port); 
      my_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
     printf("Binding server to port %d\n",port); 
        int err_log; 
        err_log = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
        if(err_log != 0) //绑定本程序要使用的信息 
        { 
            perror("bind"); 
            close(sockfd); 
            exit(-1); 
        } 
        printf("receive data ...\n"); 
        while(1) //收取数据 
        { 
            int recv_len; 
            char recv_buf[512] = " "; 
            struct sockaddr_in client_addr; 
        char cli_ip[INET_ADDRSTRLEN] = " "; 
        socklen_t cliadrr_len = sizeof(client_addr); 
        recv_len = recvfrom(sockfd, 
                            recv_buf, 
                            sizeof(recv_buf), 
                            0, 
                            (struct sockaddr *)&client_addr, 
                            &cliaddr_len);

        inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip,INET_ADDRSTRLEN); 

        printf("\nip:%s,port:%d\n",cli_ip ,ntohs(client_addr.sin_port));     
        printf("data(%d):%s\n",recv_len,recv_buf); 
        } 
    close(sockfd); 
    return 0; 
}

 

2.5 UDP编程-client,server

2.5.1 C/S架构回顾

 

2.5.2 UDP客户端注意点

  •    本地IP:本地端口
  •    目的IP:目的端口
  •    客户端,只设置目的IP,目的端口
bzero(&dest_addr, sizeof(dest_addr)); 
dest_addr.sin_family = AF_INET; 
dest_addr.sin_port = htons(8080); 
inet_pton(AF_INET,"172.20.223.75",&dest_addr.sin_addr);

      客户端的本地IP,本地port

 

2.5.3 UDP服务器注意点

  •     1
  • 2
  • 3

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!