• UDP编程


    通信流程

    UDP编程 

    通信流程   --- 无连接(connect accept)的过程 

    UDP 无法判断客户端是否退出: 

     使用心跳包:  使用客户端, 定时给服务器发送内容

    udp流程:(类似发短信)

    server:

    创建数据报套接字(socket(,SOCK_DGRAM,))----->有手机

    绑定网络信息(bind())-----------> 绑定IP和port(发短信知道发给谁)

    接收信息(recvfrom())------------>接收信息,同时可以获取到发送者的IP和port

    关闭套接字(close())-------------->接收完毕

    client:

    创建数据报套接字(socket())----------------------->有手机

    指定服务器的网络信息------------------------------>有对方号码

    发送信息(sendto())---------------------------->发送短信,根据填充的结构体信息

    关闭套接字(close())--------------------------->发送完

    函数接口

    1、recvfrom接受数据

    1. #include 
    2. #include 
    3. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
    4. struct sockaddr *src_addr, socklen_t *addrlen);
    5. 功能:接收数据
    6. 参数:
    7. sockfd:套接字描述符
    8. buf:接收缓存区的首地址
    9. len:接收缓存区的大小
    10. flags:0
    11. src_addr:发送端的网络信息结构体的指针
    12. addrlen:发送端的网络信息结构体的大小的指针
    13. 返回值:
    14. 成功接收的字节个数
    15. 失败:-1
    16. 0:客户端退出

    2、sendto 发送数据

    1. #include 
    2. #include 
    3. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
    4.                   const struct sockaddr *dest_addr, socklen_t addrlen);
    5. 功能:发送数据
    6. 参数:
    7. sockfd:套接字描述符
    8. buf:发送缓存区的首地址
    9. len:发送缓存区的大小
    10. flags:0
    11. src_addr:接收端的网络信息结构体的指针
    12. addrlen:接收端的网络信息结构体的大小
    13. 返回值: 
    14. 成功发送的字节个数
    15. 失败:-1

    注意:

    1、对于TCP是先运行服务器,客户端才能运行。

    1. 对于UDP来说,服务器和客户端运行顺序没有先后,因为是无连接,所以服务器和客户端谁先开始,没有关系,

    1. UDP一个服务器可以同时连接多个客户端。想知道是哪个客户端登录,可以在服务器代码里面打印IP和端口号。

    以下内容面试可能会问: 感兴趣可以自己测试一下

    4、UDP,客户端当使用send的时候,上面需要加connect,,这个connect不是代表连接的作用,而是指定客户端即将要发送给谁数据。这样就不需要使用sendto而用send就可以。

    5在TCP里面,也可以使用recvfrom和sendto,使用的时候将后面的两个参数都写为NULL就OK。

    1. /*服务器创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. int main(int argc, char const *argv[])
    12. {
    13.     if (argc < 2)
    14.     {
    15.         printf("plase input \n");
    16.         return -1;
    17.     }
    18.     //1.创建套接字,用于链接
    19.     int sockfd;
    20.     sockfd = socket(AF_INET,SOCK_DGRAM, 0);
    21.     if (sockfd < 0)
    22.     {
    23.         perror("socket err");
    24.         return -1;
    25.     }
    26.     printf("sockfd:%d\n", sockfd);
    27.     //2.绑定 ip+port 填充结构体
    28.     struct sockaddr_in saddr;
    29.     saddr.sin_family = AF_INET;            
    30.     saddr.sin_port = htons(atoi(argv[1])); 
    31.     saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    32.     socklen_t len = sizeof(saddr); //结构体大小
    33.     //bind绑定ip和端口
    34.     if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    35.     {
    36.         perror("bind err");
    37.         return -1;
    38.     }
    39.     printf("bind success\n");
    40.     char buf[128= {0};
    41.     while (1)
    42.     {
    43.         //接收信息
    44.         if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
    45.         {
    46.             perror("recvfrom err");
    47.             return -1;
    48.         }
    49.         printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port),buf);
    50.         //发送信息
    51.         printf("server:");
    52.         fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中
    53.         if (strncmp(buf, "quit"4== 0//输入quit退出客户端
    54.         {
    55.             break;
    56.         }
    57.         if (buf[strlen(buf)] == '\0')
    58.         {
    59.             buf[strlen(buf) - 1= '\0';
    60.         }
    61.         sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
    62.     }
    63.     close(sockfd);
    64.     return 0;
    65. }

    1. /*客户端创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. #include <sys/types.h>
    12. #include <sys/stat.h>
    13. #include <fcntl.h>
    14. #include <unistd.h>
    15. int main(int argc, char const *argv[])
    16. {
    17.     if(argc<3)
    18.     {
    19.         printf("plase input ")
    20.     }
    21.     //1.创建套接字,用于链接
    22.     int sockfd;
    23.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    24.     if (sockfd < 0)
    25.     {
    26.         perror("socket err");
    27.         return -1;
    28.     }
    29.     printf("sockfd:%d\n", sockfd);
    30.     //2.填充结构体
    31.     struct sockaddr_in saddr;
    32.     saddr.sin_family = AF_INET;
    33.     saddr.sin_port = htons(atoi(argv[2]));
    34.     saddr.sin_addr.s_addr = inet_addr(argv[1]);
    35.     socklen_t len = sizeof(saddr); //结构体大小
    36.     char buf[128= {0};
    37.     int ret;
    38.     while (1)
    39.     {
    40.         //发送信息
    41.         printf("client:");
    42.         fgets(buf, sizeof(buf), stdin);   //从终端获取内容存放到数组中
    43.         if (strncmp(buf, "quit"4== 0//输入quit退出客户端
    44.         {
    45.             break;
    46.         }
    47.         if (buf[strlen(buf)] == '\0')
    48.         {
    49.             buf[strlen(buf) - 1= '\0';
    50.         }
    51.         sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
    52.         //接受信息
    53.         if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
    54.         {
    55.             perror("recvfrom err");
    56.             return -1;
    57.         }
    58.         printf("server buf:%s\n", buf);
    59.     }
    60.     close(sockfd);
    61.     return 0;
    62. }

    练习:实现如客户端发送"hello"给服务器端,服务器接着给客户端回,"recv:hello!!!!!"。 

    作业:熟练记忆TCP\UDP客户端服务器,标准为可以默写出来。

    项目-网络聊天室

    项目要求

    利用UDP协议,实现一套聊天室软件。服务器端记录客户端的地址,客户端发送消息后,服务器群发给各个客户端软件,服务也可以自己发送通知给所有客户端。

    问题思考

    • 客户端会不会知道其它客户端地址?

    UDP客户端不会直接互连,所以不会获知其它客户端地址,所有客户端地址存储在服务器端。

    • 有几种消息类型?
    • 登录:服务器存储新的客户端的地址。把某个客户端登录的消息发给其它客户端。
    • 聊天:服务器只需要把某个客户端的聊天消息转发给所有其它客户端。
    • 退出:服务器删除退出客户端的地址,并把退出消息发送给其它客户端。
    • 服务器如何存储客户端的地址?

    数据结构可以选择线性数据结构

    1. 链表节点结构体:
    2. struct node{
    3. struct sockaddr_in addr;//data   memcmp
    4. struct node *next;
    5. };
    6. 消息对应的结构体(同一个协议)
    7. typedef struct msg_t
    8. {
    9. int type;//'L' C  Q    enum un{login,chat,quit};
    10. char name[32];//用户名
    11. char text[128];//消息正文
    12. }MSG_t;
    13. int memcmp(void *s1,void *s2,int size)
    14. 功能:比较两个空间内的值是否完全相同

    • 客户端如何同时处理发送和接收?

    客户端不f仅需要读取服务器消息,而且需要发送消息。读取需要调用recvfrom,发送需要先调用fgets,两个都是阻塞函数。所以必须使用多任务来同时处理,可以使用多进程或者多线程来处理。

    程序流程图

    服务器端

    客户端

  • 相关阅读:
    jh7110 visionfive2 linux uboot debian
    10月26日,每日信息差
    全网最全JAVA面试八股文
    prompt问题【中间不好】
    day04_java基础
    1分钟 Serverless 极速搭建真网站 领猫超卡之《极速搭建 Zblog 博客系统》场景体验
    gdb调试方法总结
    Redis常用指令之string、list、set、zset、hash
    面试官:你说你用过Dubbo,那你说说看Dubbo的SPI
    mybatis bean属性识别丢失【NoSuchPropertyException】
  • 原文地址:https://blog.csdn.net/m0_74937538/article/details/134260632