• 嵌入式学习第二十五天!(网络的概念、UDP编程)


    网络:

        可以用来:数据传输数据共享

    1. 网络协议模型:

        1. OSI协议模型:

    应用层实际收发的数据
    表示层发送的数据是否加密
    会话层是否建立会话连接
    传输层数据传输的方式(数据包,流式)
    网络层数据的路由(如何从一个局域网到达另一个局域网)
    数据链路层局域网下如何通信
    物理层物理介质的连接

          2. TCP/IP协议模型:

    应用层传输的数据
    传输层传输的方式
    网络层数据如何从一个台主机到达另一台主机
    网络接口层物理介质的连接
            1. 应用层:

                   例如有:HTTP      超文本传输协议

                                  HTTPS   

                                  FTP        文件传输协议

                                  TFTP      简单文本传输协议

                                  SMTP     邮件传输协议

                                  MQTT

                                  TELNET

                                  ...

            2. 传输层:

                  UDP:用户数据报协议

                        特点:1. 实现机制简单

                                   2. 资源开销小

                                   3. 不安全不可靠

                  TCP:传输控制协议

                          特点:1. 实现机制复杂

                                     2. 资源开销大

                                     3. 安全可靠

            3. 网络层:

                  IPv4

                  IP地址:唯一网络中一台主机的标号

                  IP地址:网络位 + 主机位

                  子网掩码:用来标识IP地址的网络位和主机位

                                    子网掩码是1的部分表示IP地址的网络位

                                    子网掩码是0的部分表示IP地址的主机位

                    网段号:网络位不变,主机位全位0,表示网段号

                    广播地址:网络位不变,主机位全为1,表示广播地址

                    IP地址类型:

                    A类:

                            1.0.0.0  -  126.255.255.255

                            子网掩码:255.0.0.0

                            管理超大规模网络

                            私有IP地址:10.0.0.0  -  10.255.255.255

                    B类:

                            128.0.0.0  -  191.255.255.255

                            子网掩码:255.255.0.0

                            管理大中规模型网络

                            私有IP地址:172.16.0.0  -  172.31.255.255

                    C类:

                            192.0.0.0  -  223.255.255.255

                            子网掩码:255.255.255.0

                            管理中小规模型网络

                            私有IP地址:192.168.0.0  -  192.168.255.255

                    D类:

                            224.0.0.0  -  239.0.0.0

                            用于组播

                    E类:

                            240.0.0.0  -  255.255.255.255

                            用于实验

            4. UDP编程:

                socket套接字(全双工)编程:

                发端:socket  ->  sendto  ->  close

                收端:socket  ->  bind  ->  recvfrom  ->  close

                1. 发端
                    1. socket:
    int socket(int domain, int type, int protocol);

                        功能:创建一个用来通信的文件描述符

                        参数:

                            domain:使用的协议族 AF_INET(IPv4协议族)

                            type:套接字类型

                                    SOCK_STREAM:流式套接字

                                    SOCK_DGRAM:数据报套接字

                                    SOCK_RAW:原始套接字

                            protocol:协议

                                    默认为0;

                        返回值:

                            成功返回文件描述符
                            失败返回-1

                    2. sendto:
    1. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
    2. const struct sockaddr *dest_addr, socklen_t addrlen);

                        功能:利用套接字向指定地址发送数据信息

                        参数:

                            sockfd:套接字文件描述符

                            buf:发送数据空间首地址

                            len:发送数据的长度

                            flags:属性默认为0

                            dest_addr:目的地址信息存放的空间首地址

                            addrlen:目的地址的长度

    1. struct sockaddr_in {
    2. sa_family_t sin_family; /* address family: AF_INET */
    3. in_port_t sin_port; /* port in network byte order */
    4. struct in_addr sin_addr; /* internet address */
    5. };
    6. /* Internet address. */
    7. struct in_addr {
    8. uint32_t s_addr; /* address in network byte order */
    9. };

                        返回值:

                            成功返回实际发送字节数
                            失败返回-1

                    3. inet_addr:
    in_addr_t inet_addr(const char *cp);

                        功能:将字符串IP地址转换为内存中的IP地址

                    4. htons:
    uint16_t htons(uint16_t hostshort);

                        功能:将本地字节序转换为网络的大端字节序

                练习:

                    1. 编写程序实现从终端接收字符串发送给windows软件调试助手,并接收软件助手的回复,显示在终端屏幕上

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int sockfd = 0;
    5. ssize_t nsize = 0;
    6. char tmpbuff[1024] = {0};
    7. struct sockaddr_in recvaddr;
    8. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    9. if(sockfd == -1)
    10. {
    11. perror("fail to socket");
    12. return -1;
    13. }
    14. gets(tmpbuff);
    15. recvaddr.sin_family = AF_INET;
    16. recvaddr.sin_port = htons(50000);
    17. recvaddr.sin_addr.s_addr = inet_addr("192.168.1.162");
    18. bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(&recvaddr));
    19. nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    20. if(nsize == -1)
    21. {
    22. perror("fail to sendto");
    23. return -1;
    24. }
    25. printf("成功发送 %ld 字节!\n", nsize);
    26. memset(tmpbuff, 0, sizeof(tmpbuff));
    27. nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, (struct sockaddr *)&recvaddr, (socklen_t *)sizeof(&recvaddr));
    28. printf("%s\n",tmpbuff);
    29. close(sockfd);
    30. return 0;
    31. }
                 2. 收端
                    1. recvfrom:
    1. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
    2. struct sockaddr *src_addr, socklen_t *addrlen);

                        功能:从套接字中接收数据

                        参数:

                            sockfd:套接字文件描述符

                            buf:存放数据空间首地址

                            flags:属性,默认为0

                            src_addr:存放IP地址信息的空间首地址

                            addlen:存放接收到IP地址大小空间的首地址

                        返回值:

                            成功返回实际接收字节数
                            失败返回-1 

                    2. 修改虚拟机到桥接模式:

                        点击“虚拟机”

                        点击“设置”

                        点击“网络适配器”

                        选择“桥接模式”

                        点击“确定”

                    3. 将网卡桥接到无线网卡:

                        点击“编辑”

                        点击“虚拟网络编辑器”

                        点击“更改设置”

                    4. 在Ubuntu中重启网络服务:
    sudo /etc/init.d/networking restart 
                    5. 通过ifconfig查看虚拟机IP地址
                    6. bind:
    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

                        功能:在套接字上绑定一个IP地址和端口号

                        参数:

                            sockfd:套接字文件描述符

                            addr:绑定IP地址空间首地址

                            addrlen:绑定IP地址的长度

                        返回值:

                            成功返回0 
                            失败返回-1 

                3. UDP需要注意的细节点:

                    1. UDP是无连接,发端退出,收端没有任何影响

                    2. UDP发送数据上限,最好不要超过1500个字节

                    3. UDP是不安全不可靠的,连续且快速的传输数据容易产生数据丢失

                4. wireshark

                    可以通过wireshark抓包工具来查看收发的数据

                    操作流程:

                        1. 打开wireshark:

    sudo wireshark

                        2. 选择抓取数据包的网卡:any

                        3. 执行通信的代码

                        4. 停止通信

                        5. 设定过滤条件

                            ip.addr == IP地址  :通过IP地址查找

                            udp                        :通过传输方式udp查找

                            tcp                         :通过传输方式tcp查找

                            udp.port == 端口号:通过端口号查找

                5. UDP包头长度:8个字节

                    源端口号(2个字节)

                    目的端口号(2个字节)

                    长度(2个字节)

                    检验和(2个字节)

                练习:

                    要求在不同主机中编写两个程序,实现全双工聊天功能

                    1. 进入软件后接收当前用户的昵称

                    2. 显示的格式为对方用户昵称 (对方IP:对方端口) > 接收到的内容

                    3. 用户输入“.quit”退出聊天

                    4. 网络通信时收发结构体

    1. struct person
    2. {
    3. char name[32];
    4. char text[512];
    5. };
    1. #include "head.h"
    2. int sockfd = 0;
    3. ssize_t nsize = 0;
    4. struct sockaddr_in tmpaddr;
    5. struct sockaddr_in sendaddr;
    6. socklen_t addrlen = sizeof(tmpaddr);
    7. struct person
    8. {
    9. char name[32];
    10. char text[512];
    11. };
    12. pthread_t tid_recv;
    13. pthread_t tid_send;
    14. void *RecvInfo(void *arg)
    15. {
    16. struct person user;
    17. while(1)
    18. {
    19. memset(&user, 0, sizeof(user));
    20. nsize = recvfrom(sockfd, &user, sizeof(user), 0, (struct sockaddr *)&tmpaddr, &addrlen);
    21. if(nsize == -1)
    22. {
    23. perror("fail to recvfrom");
    24. return NULL;
    25. }
    26. printf("%s %s : %d > %s\n", user.name, inet_ntoa(tmpaddr.sin_addr), ntohs(tmpaddr.sin_port), user.text);
    27. if(!strcmp(user.text, ".quit"))
    28. {
    29. break;
    30. }
    31. }
    32. pthread_cancel(tid_send);
    33. return NULL;
    34. }
    35. void *SendInfo(void *arg)
    36. {
    37. struct person user;
    38. while(1)
    39. {
    40. memset(&user, 0, sizeof(user));
    41. scanf("%s", user.name);
    42. scanf("%s", user.text);
    43. nsize = sendto(sockfd, &user, sizeof(user), 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
    44. if(nsize == -1)
    45. {
    46. perror("fail to sendto");
    47. return NULL;
    48. }
    49. printf("success send %ld byte\n", nsize);
    50. if(!strcmp(user.text, ".quit"))
    51. {
    52. break;
    53. }
    54. }
    55. pthread_cancel(tid_recv);
    56. return NULL;
    57. }
    58. int main(void)
    59. {
    60. struct sockaddr_in recvaddr;
    61. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    62. if(sockfd == -1)
    63. {
    64. perror("fail to socket");
    65. return -1;
    66. }
    67. recvaddr.sin_family = AF_INET;
    68. recvaddr.sin_port = htons(30000);
    69. recvaddr.sin_addr.s_addr = inet_addr("192.168.1.153");
    70. bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    71. sendaddr.sin_family = AF_INET;
    72. sendaddr.sin_port = htons(30000);
    73. sendaddr.sin_addr.s_addr = inet_addr("192.168.1.152");
    74. pthread_create(&tid_recv, NULL, RecvInfo, NULL);
    75. pthread_create(&tid_send, NULL, SendInfo, NULL);
    76. pthread_join(tid_recv, NULL);
    77. pthread_join(tid_send, NULL);
    78. close(sockfd);
    79. return 0;
    80. }
            5. UDP项目练习:

            题目:基于UDP实现直播间聊天的功能:

            需求:

                    软件划分为用户客户端和主播服务端两个软件client.c和server.c

                    用户客户端负责:

                            1.接收用户的昵称
                            2.接收用户输入的信息,能够将信息发送给服务端
                            3.接收服务端回复的数据信息,并完成显示

                    主播服务端负责:

                            1.对所有加入直播间的用户的IP地址和端口实现管理(加入、退出)
                            2.当有新的客户端加入时,能够向所有客户端提示:"欢迎 XXX 用户进入直播间"
                            3.当有客户端退出时,能够向所有客户端提示:"XXX 离开直播间"
                            4.能够实现客户端聊天内容的转发,当某个客户端发送聊天信息时,能够将该信息转给除了该用户之外聊天室内所有其余客户端用户

    client.c

    1. #include "head.h"
    2. int sockfd = 0;
    3. char name[32];
    4. struct sockaddr_in recvaddr;
    5. pthread_t tid_send;
    6. pthread_t tid_recv;
    7. void *SendMsg(void *arg)
    8. {
    9. struct msgbuf sendmsg;
    10. ssize_t nsize = 0;
    11. while(1)
    12. {
    13. memset(&sendmsg, 0, sizeof(sendmsg));
    14. sendmsg.type = USER_TYPE_CHAT;
    15. sprintf(sendmsg.name, "%s", name);
    16. gets(sendmsg.text);
    17. if(strcmp(sendmsg.text,".quit") == 0)
    18. {
    19. sendmsg.type = USER_TYPE_OUT;
    20. }
    21. nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    22. if(nsize == -1)
    23. {
    24. perror("fail to sendto");
    25. return NULL;
    26. }
    27. if(sendmsg.type == USER_TYPE_OUT)
    28. {
    29. break;
    30. }
    31. }
    32. pthread_cancel(tid_recv);
    33. return NULL;
    34. }
    35. void *RecvMsg(void *arg)
    36. {
    37. struct msgbuf recvmsg;
    38. ssize_t nsize = 0;
    39. while(1)
    40. {
    41. nsize = recvfrom(sockfd, &recvmsg, sizeof(recvmsg), 0, NULL, NULL);
    42. if(nsize == -1)
    43. {
    44. perror("fail to recvfrom");
    45. return NULL;
    46. }
    47. if(recvmsg.type == USER_TYPE_CHAT)
    48. {
    49. printf("%s>%s\n", recvmsg.name, recvmsg.text);
    50. }
    51. if(recvmsg.type == USER_TYPE_OUT)
    52. {
    53. break;
    54. }
    55. }
    56. return NULL;
    57. }
    58. int main(void)
    59. {
    60. ssize_t nsize = 0;
    61. struct msgbuf sendmsg;
    62. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    63. if(sockfd == -1)
    64. {
    65. perror("fail to socket");
    66. return -1;
    67. }
    68. printf("请输入你的名字:\n");
    69. gets(name);
    70. memset(&sendmsg, 0, sizeof(sendmsg));
    71. sendmsg.type = USER_TYPE_INT;
    72. sprintf(sendmsg.name, "%s", name);
    73. recvaddr.sin_family = AF_INET;
    74. recvaddr.sin_port = htons(SERVER_PORT);
    75. recvaddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    76. nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    77. if(nsize == -1)
    78. {
    79. perror("fail to sendto");
    80. return -1;
    81. }
    82. pthread_create(&tid_send, NULL, SendMsg, NULL);
    83. pthread_create(&tid_recv, NULL, RecvMsg, NULL);
    84. pthread_join(tid_send, NULL);
    85. pthread_join(tid_recv, NULL);
    86. close(sockfd);
    87. }

    server.c

    1. #include "head.h"
    2. int main(void)
    3. {
    4. int sockfd = 0;
    5. ssize_t nsize = 0;
    6. ssize_t size = 0;
    7. struct sockaddr_in serveraddr;
    8. struct address useraddr[100];
    9. struct sockaddr_in userinfo;
    10. socklen_t addrlen = 0;
    11. addrlen = sizeof(userinfo);
    12. struct msgbuf recvuser;
    13. int i = 0;
    14. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    15. if(sockfd == -1)
    16. {
    17. perror("fail to socket");
    18. return -1;
    19. }
    20. serveraddr.sin_family = AF_INET;
    21. serveraddr.sin_port = htons(SERVER_PORT);
    22. serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    23. bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    24. memset(useraddr, 0, sizeof(useraddr));
    25. while(1)
    26. {
    27. memset(&recvuser, 0, sizeof(recvuser));
    28. memset(&userinfo, 0, sizeof(userinfo));
    29. nsize = recvfrom(sockfd, &recvuser, sizeof(recvuser), 0, (struct sockaddr *)&userinfo, &addrlen);
    30. if(nsize == -1)
    31. {
    32. return -1;
    33. }
    34. if(recvuser.type == USER_TYPE_INT)
    35. {
    36. for(i = 0; i < 100; i++)
    37. {
    38. if(useraddr[i].mark == 1)
    39. {
    40. continue;
    41. }
    42. else if(useraddr[i].mark == 0)
    43. {
    44. useraddr[i].mark = 1;
    45. useraddr[i].cltaddr.sin_family = AF_INET;
    46. useraddr[i].cltaddr.sin_port = userinfo.sin_port;
    47. useraddr[i].cltaddr.sin_addr.s_addr = userinfo.sin_addr.s_addr;
    48. printf("欢迎用户:%s来到直播间!\n", recvuser.name);
    49. break;
    50. }
    51. }
    52. }
    53. else if(recvuser.type == USER_TYPE_OUT)
    54. {
    55. for(i = 0; i < 100; i++)
    56. {
    57. if(memcmp(&useraddr[i].cltaddr, &userinfo, sizeof(userinfo)) == 0)
    58. {
    59. useraddr[i].mark = 0;
    60. printf("用户:%s离开直播间!\n", recvuser.name);
    61. }
    62. }
    63. }
    64. else if(recvuser.type == USER_TYPE_CHAT)
    65. {
    66. printf("%s(%s:%d)>%s\n", recvuser.name, inet_ntoa(userinfo.sin_addr), ntohs(userinfo.sin_port), recvuser.text);
    67. for(i = 0; i < 100; i++)
    68. {
    69. if(useraddr[i].mark != 0)
    70. {
    71. size = sendto(sockfd, &recvuser, sizeof(recvuser), 0, (struct sockaddr *)&useraddr[i].cltaddr, sizeof(useraddr[i].cltaddr));
    72. if(size == -1)
    73. {
    74. perror("fail to sendto");
    75. return -1;
    76. }
    77. }
    78. }
    79. }
    80. }
    81. close(sockfd);
    82. return 0;
    83. }

    在这里head.h中定义了两个结构体,已经定义了客户发过来的状态

    1. #ifndef _HEAD_H_
    2. #define _HEAD_H_
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. #include
    22. #include
    23. #include
    24. #include
    25. #include
    26. struct msgbuf
    27. {
    28. int type;
    29. char name[32];
    30. char text[512];
    31. };
    32. struct address
    33. {
    34. struct sockaddr_in cltaddr;
    35. int mark;
    36. };
    37. #define USER_TYPE_INT 100
    38. #define USER_TYPE_OUT 200
    39. #define USER_TYPE_CHAT 300
    40. #define SERVER_ADDR "192.168.1.162"
    41. #define SERVER_PORT 5000
    42. #endif

  • 相关阅读:
    使用方便、易于集成、可扩展的用于物流运输行业的文档管理软件
    AR人体姿态识别,实现无边界的人机交互
    es : java 查询
    Spring之事务开发
    ChatGPT:深度学习和机器学习的知识桥梁
    工业4.0 IO-Link RFID传感器|读写器应用前景与优势分析
    【笔记】《C++性能优化指南》Ch3 测量性能
    关于解决前后端分离开发——跨域问题
    OVS-DPDK/虚拟化学习
    2.1.6.15 漏洞利用-smb-RCE远程命令执行
  • 原文地址:https://blog.csdn.net/weixin_52799506/article/details/136492573