• 02_udp编程


    知识点1【字节序的特点】

    知识点2【字节序转换函数】

    1、htonl函数 发 将主机字节序的IP地址 转换成网络字节序的IP地址

     2、ntohl函数 收 将网络字节序的IP地址 转换成主机字节序的IP地址

    3、htons函数 发 将主机字节序的端口 转换成 网络字节序的端口

    4、ntohs函数 收 将网络字节序的端口 转换成 主机字节序的端口

    知识点3【地址转换】

    1、inet_pton函数 发 将点分十进制数串 转换成 32位网络字节序地址

     2、inet_ntop函数 收 将32位网络字节序IP 转换成 点分十进制数串

    知识点4【socket编程】

    1、不同协议的识别TCP UDP

    2、UDP编程C/S架构

    知识点5【socket函数】

    1、创建socket套接字

    2、IPv4套接字地址结构

     3、通用地址结构:struct sockaddr

    4、sendto函数 发送udp数据

     案例:sendto发送数据

    5、bind函数 让套接字 拥有一个固定的IP、端口信息​编辑

    6、recvfrom接受数据

    知识点6【UDP_QQ】


    知识点1【字节序的特点】

    1、网络协议指定了通讯字节序—大端

    2、只有在多字节数据作为整体处理时才需要考虑字节序

    3、运行在同一台计算机上的进程相互通信时,一般不用考虑字节序

    4、异构计算机之间通讯,需要转换自己的字节序为网络字节序

    知识点2【字节序转换函数】

    1、htonl函数 将主机字节序的IP地址 转换成网络字节序的IP地址

    1. uint32_t htonl(uint32_t hostint32);
    2. 功能:
    3. 32位主机字节序数据转换成网络字节序数据
    4. 参数:
    5. hostint32:待转换的32位主机字节序数据
    6. 返回值:
    7. 成功:返回网络字节序的值
    8. 头文件:
    9. #include <arpa/inet.h>

     2、ntohl函数 将网络字节序的IP地址 转换成主机字节序的IP地址

    1. uint32_t ntohl(uint32_t netint32);
    2. 功能:
    3. 32位网络字节序数据转换成主机字节序数据
    4. 参数:
    5. uint32_t: unsigned int
    6. netint32:待转换的32位网络字节序数据
    7. 返回值:
    8. 成功:返回主机字节序的值
    9. 头文件:
    10. #include <arpa/inet.h>

    3、htons函数 发 将主机字节序的端口 转换成 网络字节序的端口

    1. uint16_t htons(uint16_t hostint16);
    2. 功能:
    3. 16位主机字节序数据转换成网络字节序数据
    4. 参数:
    5. uint16_tunsigned short int
    6. hostint16:待转换的16位主机字节序数据
    7. 返回值:
    8. 成功:返回网络字节序的值
    9. 头文件:
    10. #include

    4、ntohs函数 收 将网络字节序的端口 转换成 主机字节序的端口

    1. uint16_t ntohs(uint16_t netint16);
    2. 功能:
    3. 16位网络字节序数据转换成主机字节序数据
    4. 参数:
    5. uint16_tunsigned short int
    6. netint16:待转换的16位网络字节序数据
    7. 返回值:
    8. 成功:返回主机字节序的值
    9. 头文件:
    10. #include

    总结:

    1、htons htonl ntohs ntohl 如果主机 和网络 字节序相同 前面4个函数不会颠倒数据

    2、htons htonl ntohs ntohl 如果主机 和网络 字节序 不相同 前面4个函数会颠倒数据

    知识点3【地址转换】

    IP地址:“192.168.0.111” 点分十进制数串(字符串)

    计算机的中的IP地址32位:4字节

    1、inet_pton函数 将点分十进制数串 转换成 32位网络字节序地址

    1. int inet_pton(int family,const char *strptr, void *addrptr);
    2. 功能:
    3. 将点分十进制数串转换成32位无符号整数
    4. 参数:
    5. family 协议族 AF_INET
    6. strptr 点分十进制数串
    7. addrptr  32位无符号整数的地址
    8. 返回值:
    9. 成功返回1 、 失败返回其它
    10. 头文件:
    11. #include

    案例:

    1. #include<stdio.h>
    2. #include<arpa/inet.h>
    3. int main()
    4. {
    5. //点分十进制数串
    6. char *str_ip="192.168.0.111";
    7. //32位无符号整形数据
    8. unsigned int num_ip=0;
    9. //AF_INETIPv4 AF_INET6 IPv6
    10. inet_pton(AF_INET,str_ip, &num_ip);
    11. printf("str_ip=%s\n", str_ip);
    12. printf("num_ip=%u\n", num_ip);//1862314176
    13. //细致分析 num_ip的4字节分布
    14. unsigned char *p = (unsigned char *)&num_ip;
    15. printf("%u %u %u %u\n", *p, *(p+1),*(p+2), *(p+3));//192 168 0 111
    16. return 0;
    17. }

    运行结果:

     

     2、inet_ntop函数 将32位网络字节序IP 转换成 点分十进制数串

    1. 整型数据转字符串格式ip地址
    2. const char *inet_ntop(int family, const void *addrptr,
    3. char *strptr, size_t len);
    4. 功能:
    5. 32位无符号整数转换成点分十进制数串
    6. 参数:
    7. family    协议族 AF_INET
    8. addrptr    32位无符号整数
    9. strptr    点分十进制数串
    10. len        strptr缓存区长度
    11. len的宏定义
    12. #define INET_ADDRSTRLEN   16  //for ipv4
    13. #define INET6_ADDRSTRLEN  46  //for ipv6
    14. 返回值:
    15. 成功:则返回字符串的首地址
    16. 失败:返回NULL
    17. 头文件:
    18. #include

    案例:

    知识点4【socket编程】

    不同主机进程间通信 需要解决的问题?

    1、不同协议的识别TCP UDP

    2、不同主机的识别(哪个IP发 哪个IP收)

    3、不同进程的识别(哪个端口发 哪个端口收)

    1、socket特点

    1、socket也称“套接字”

    2、是一种文件描述符,代表了一个通信管道的一个端点

    3、类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作

    4、得到socket套接字(描述符)的方法调用socket()

    2、UDP编程C/S架构

    知识点5【socket函数】

    1、创建socket套接字

    1. int socket(int family,int type,int protocol);
    2. 功能:
    3. 创建一个用于网络通信的socket套接字(描述符)
    4. 参数:
    5. family:协议族(AF_INETIPv4)、AF_INET6(IPv6)、PF_PACKET(链路层编程)等)
    6. type:套接字类(SOCK_STREAM(流式套接字)、SOCK_DGRAM(数据报式套接字)、
    7. SOCK_RAW(原始套接字)等)
    8. protocol:协议类别(0IPPROTO_TCPIPPROTO_UDP
    9. 返回值:
    10. 套接字
    11. 特点:
    12. 创建套接字时,系统不会分配端口
    13. 创建的套接字默认属性是主动的,即主动发起服务的请求;
    14. 当作为服务器时,往往需要修改为被动的
    15. 头文件:
    16. #include

    案例:

    1. int socket(int family,int type,int protocol);
    2. 功能:
    3. 创建一个用于网络通信的socket套接字(描述符)
    4. 参数:
    5. family:协议族(AF_INETIPv4)、AF_INET6(IPv6)、PF_PACKET(链路层编程)等)
    6. type:套接字类(SOCK_STREAM(流式套接字)、SOCK_DGRAM(数据报式套接字)、
    7. SOCK_RAW(原始套接字)等)
    8. protocol:协议类别(0IPPROTO_TCPIPPROTO_UDP
    9. 返回值:
    10. 套接字
    11. 特点:
    12. 创建套接字时,系统不会分配端口
    13. 创建的套接字默认属性是主动的,即主动发起服务的请求;
    14. 当作为服务器时,往往需要修改为被动的
    15. 头文件:
    16. #include

    案例:

    2、IPv4套接字地址结构

    #include

    1. struct in_addr
    2. {
    3. in_addr_t s_addr;//4字节
    4. };
    5. //IPv4地址结构
    6. struct sockaddr_in
    7. {
    8. sa_family_t sin_family;//2字节 协议
    9. in_port_t sin_port;//2字节 端口PORT
    10. struct in_addr sin_addr;//4字节 IP地址
    11. char sin_zero[8]//8字节 必须为0
    12. };

     3、通用地址结构:struct sockaddr

    1. struct sockaddr
    2. {
    3. sa_family_t sa_family; // 2字节
    4. char sa_data[14] //14字节
    5. };

    struct sockaddr_in IPv4地址结构 struct sockaddr通用地址结构 应用场景

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

    例:

    struct  sockaddr_in  my_addr;

    当调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换

    例:

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

    4、sendto函数 发送udp数据

    1. ssize_t sendto(int sockfd,const void *buf,
    2.                  size_t nbytes,int flags,
    3.                  const struct sockaddr *to,        
    4.                  socklen_t addrlen);
    5. 功能:
    6. to结构体指针中指定的ip,发送UDP数据
    7. 参数:
    8. sockfd:套接字
    9. buf:  发送数据缓冲区
    10. nbytes: 发送数据缓冲区的大小 
    11. flags:一般为0
    12. to: 指向目的主机地址结构体的指针
    13. addrlen:to所指向内容的长度
    14. 注意:
    15. 通过to和addrlen确定目的地址
    16. 可以发送0长度的UDP数据包
    17. 返回值:
    18. 成功:发送数据的字符数
    19. 失败: -1

     案例:sendto发送数据

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>//socket
    4. #include<sys/types.h>
    5. #include<netinet/in.h>//struct sockaddr_in
    6. #include<arpa/inet.h>//inet_pton
    7. int main()
    8. {
    9. //1、创建一个udp套接字
    10. int sockfd = socket(AF_INET, SOCK_DGRAM,0);
    11. //2、发送数据
    12. //定义一个IPv4 目的地址结构 192.168.0.110 8080
    13. struct sockaddr_in dst_addr;
    14. //清空结构体
    15. //memset(&dst_addr,0,sizeof(dst_addr));
    16. bzero(&dst_addr,sizeof(dst_addr));
    17. dst_addr.sin_family = AF_INET;//协议
    18. //将主机字节序转换成网络字节序
    19. dst_addr.sin_port = htons(8080);//端口
    20. //将字符串"192.168.0.110" 转换成32位整形数据 赋值IP地址
    21. inet_pton(AF_INET,"192.168.0.110", &dst_addr.sin_addr.s_addr);
    22. sendto(sockfd,"hehe",strlen("hehe"),0, \
    23. (struct sockaddr *)&dst_addr , sizeof(dst_addr) );
    24. //3、关闭套接字
    25. close(sockfd);
    26. return 0;
    27. }

    运行结果: 

    注意:源端口信息 是啥时候赋值呢?

    如果udp套接字 没有绑定 固定的ip、端口信息 那么在第一次调用sendto 系统分配本地主机ip以及一个临时端口(不确定的)

    5、bind函数 让套接字 拥有一个固定的IP、端口信息

    bind只能绑定自身的IP

    1. int bind(int sockfd,
    2. const struct sockaddr *myaddr,socklen_t addrlen);
    3. 功能:
    4. 将本地协议地址与sockfd绑定
    5. 参数:
    6. sockfd: socket套接字
    7. myaddr: 指向特定协议的地址结构指针
    8. addrlen:该地址结构的长度
    9. 返回值:
    10. 成功:返回0
    11. 失败:其他
    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>//socket
    4. #include<sys/types.h>
    5. #include<netinet/in.h>//struct sockaddr_in
    6. #include<arpa/inet.h>//inet_pton
    7. int main()
    8. {
    9. //1、创建一个udp套接字
    10. int sockfd = socket(AF_INET, SOCK_DGRAM,0);
    11. //2、给套接字bind固定的信息
    12. struct sockaddr_in my_addr;
    13. bzero(&my_addr,sizeof(my_addr));
    14. my_addr.sin_family = AF_INET;
    15. my_addr.sin_port = htons(8000);//自身端口
    16. //INADDR_ANY 让系统自动寻找可用的本地IP地址
    17. my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY==0
    18. bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
    19. //2、发送数据
    20. //定义一个IPv4 目的地址结构 192.168.0.110 8080
    21. struct sockaddr_in dst_addr;
    22. //清空结构体
    23. //memset(&dst_addr,0,sizeof(dst_addr));
    24. bzero(&dst_addr,sizeof(dst_addr));
    25. dst_addr.sin_family = AF_INET;//协议
    26. //将主机字节序转换成网络字节序
    27. dst_addr.sin_port = htons(8080);//端口
    28. //将字符串"192.168.0.110" 转换成32位整形数据 赋值IP地址
    29. inet_pton(AF_INET,"192.168.0.110", &dst_addr.sin_addr.s_addr);
    30. sendto(sockfd,"hehe",strlen("hehe"),0, \
    31. (struct sockaddr *)&dst_addr , sizeof(dst_addr) );
    32. sleep(1);
    33. sendto(sockfd,"haha",strlen("haha"),0, \
    34. (struct sockaddr *)&dst_addr , sizeof(dst_addr) );
    35. //3、关闭套接字
    36. close(sockfd);
    37. return 0;
    38. }

    运行结果:

    6、recvfrom接受数据

    1. ssize_t recvfrom(int sockfd, void *buf,
    2. size_t nbytes,int flags,struct sockaddr *from,
    3.             socklen_t *addrlen);
    4. 功能:
    5. 接收UDP数据,并将源地址信息保存在from指向的结构中
    6. 参数:
    7. sockfd: 套接字
    8. buf:接收数据缓冲区
    9. nbytes:接收数据缓冲区的大小
    10. flags:  套接字标志(常为0)
    11. from:  源地址结构体指针,用来保存数据的来源
    12. addrlen: from所指内容的长度
    13. 注意:
    14. 通过from和addrlen参数存放数据来源信息
    15. from和addrlen可以为NULL, 表示不保存数据来源
    16. 返回值:
    17. 成功:接收到的字符数
    18. 失败: -1

     案例:不关心发送者的信息

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>//socket
    4. #include<sys/types.h>
    5. #include<netinet/in.h>//struct sockaddr_in
    6. #include<arpa/inet.h>//inet_pton
    7. int main()
    8. {
    9. //创建一个UDP套接字
    10. int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    11. //如果收数据 尽量bind
    12. struct sockaddr_in my_addr;
    13. bzero(&my_addr,sizeof(my_addr));
    14. my_addr.sin_family = AF_INET;
    15. my_addr.sin_port = htons(8000);
    16. my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    17. bind(sockfd,(struct sockaddr *)&my_addr, sizeof(my_addr));
    18. while(1)
    19. {
    20. char buf[128]="";
    21. int len = recvfrom(sockfd, buf, sizeof(buf),0, NULL,NULL);
    22. printf("len = %d\n",len);
    23. printf("buf=%s\n",buf);
    24. }
    25. close(sockfd);
    26. return 0;
    27. }

    运行结果:

    案例:关心发送者的信息

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>//socket
    4. #include<sys/types.h>
    5. #include<netinet/in.h>//struct sockaddr_in
    6. #include<arpa/inet.h>//inet_pton
    7. int main()
    8. {
    9. //创建一个UDP套接字
    10. int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    11. //如果收数据 尽量bind
    12. struct sockaddr_in my_addr;
    13. bzero(&my_addr,sizeof(my_addr));
    14. my_addr.sin_family = AF_INET;
    15. my_addr.sin_port = htons(8000);
    16. my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    17. bind(sockfd,(struct sockaddr *)&my_addr, sizeof(my_addr));
    18. while(1)
    19. {
    20. char buf[128]="";
    21. struct sockaddr_in from;
    22. socklen_t from_len = sizeof(from);
    23. //带阻塞
    24. int len = recvfrom(sockfd, buf, sizeof(buf),0, \
    25. (struct sockaddr *)&from, &from_len);
    26. //分析发送者的信息(IP地址 port)
    27. unsigned short port = ntohs(from.sin_port);
    28. char ip[16]="";
    29. //from32位整形IP转换成 点分十进制数串
    30. inet_ntop(AF_INET,&from.sin_addr.s_addr, ip, 16);
    31. printf("消息来之%s:%hu\n",ip,port);
    32. printf("len = %d\n",len);
    33. printf("buf=%s\n",buf);
    34. }
    35. close(sockfd);
    36. return 0;
    37. }

     

    知识点6【UDP_QQ】

    同时的收发数据,多任务来完成:

    流程:

    1、创建套接字

    2、bind IP端口信息

    3、创建两个线程(收 发)

    1、接受线程

    while--->recvfrom

    2、发送线程

    while--->fgets--->sendto

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>//socket
    4. #include<sys/types.h>
    5. #include<netinet/in.h>//struct sockaddr_in
    6. #include<arpa/inet.h>//inet_pton
    7. #include<pthread.h>
    8. //./aout 9000
    9. void *my_send_fun(void *arg)//arg=&sockfd
    10. {
    11. int sockfd = *(int *)arg;
    12. struct sockaddr_in dst_addr;
    13. bzero(&dst_addr,sizeof(dst_addr));
    14. dst_addr.sin_family = AF_INET;
    15. while(1)
    16. {
    17. //获取键盘输入
    18. char buf[128]="";
    19. fgets(buf,sizeof(buf),stdin);
    20. buf[strlen(buf)-1]=0;//去掉换行符
    21. if(strncmp(buf,"sayto", 5)==0)
    22. {
    23. //sayto 192.168.0.111 8000
    24. unsigned short port = 0;
    25. char ip[16]="";
    26. sscanf(buf,"sayto %s %hd", ip, &port );
    27. dst_addr.sin_port = htons(port);
    28. inet_pton(AF_INET, ip, &dst_addr.sin_addr.s_addr);
    29. continue;
    30. }
    31. else//要发送的消息
    32. {
    33. sendto(sockfd, buf, strlen(buf),0,\
    34. (struct sockaddr *)&dst_addr, sizeof(dst_addr));
    35. }
    36. }
    37. return NULL;
    38. }
    39. int main(int argc, char *argv[])
    40. {
    41. if(argc != 2)
    42. {
    43. printf("please input:./a.out 8000\n");
    44. return 0;
    45. }
    46. //创建一个通信的套接字
    47. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    48. if(sockfd < 0)
    49. {
    50. perror("socket");
    51. return 0;
    52. }
    53. //给套接字sockfd绑定一个固定的IP以及端口信息
    54. struct sockaddr_in my_addr;
    55. bzero(&my_addr,sizeof(my_addr));
    56. my_addr.sin_family = AF_INET;
    57. my_addr.sin_port = htons(atoi(argv[1]));
    58. bind(sockfd, (struct sockaddr *)&my_addr,sizeof(my_addr));
    59. //创建一个发送线程
    60. pthread_t tid;
    61. pthread_create(&tid,NULL, my_send_fun, (void *)&sockfd);
    62. pthread_detach(tid);
    63. //接受线程
    64. while(1)
    65. {
    66. char buf[128]="";
    67. struct sockaddr_in from;
    68. socklen_t len = sizeof(from);
    69. recvfrom(sockfd,buf,sizeof(buf),0,\
    70. (struct sockaddr *)&from, &len);
    71. unsigned short port = ntohs(from.sin_port);
    72. char ip[16]="";
    73. inet_ntop(AF_INET,&from.sin_addr.s_addr, ip,16);
    74. printf("来至%s:%hu %s\n",ip,port,buf);
    75. }
    76. return 0;
    77. }

     运行结果:

     

  • 相关阅读:
    我们该如何提升测试效率?
    dd命令创建指定大小的文件
    【Redis】3、Redis主从复制、哨兵、集群
    深入理解合成复用原则(Composition /Aggregate Reuse Principle)
    实例讲解Spring boot动态切换数据源
    利用X6 制作一个简单的流程图工具
    网络连接中的三次握手和四次挥手
    【开题报告】基于uni-app的校园活动签到APP的设计与实现
    怎么开发基于springboot的配音交流系统(语言-java)
    Linux文件目录总结
  • 原文地址:https://blog.csdn.net/buhuidage/article/details/127895234