• Linux网络编程9——UDP 和 本地/网络套接字


    学习视频链接

    19-本地套接字和网络套接字比较_bilibili_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1iJ411S7UA?p=105&vd_source=0471cde1c644648fafd07b54e303c905

    目录

    一、UDP 和 TCP 的优缺点

    二、UDP 实现的 C/S 模型

    2.1 通信过程

    2.2 函数讲解

    2.3 代码

    三、本地套接字和网络套接字

    3.1 进程间通信方式 (IPC)

    3.2 本地套接字对比网络 TCP 


    一、UDP 和 TCP 的优缺点

    1、TCP 

    面向连接的,可靠数据包传输。对于不稳定的网络层,采取完全弥补的通信方式。丢包重传。

    优点:稳定 (数据流量稳定、速度稳定、顺序)

    缺点:传输速度慢。相率低。开销大

    使用场景:数据的完整型要求较高,不追求效率。大数据传输、文件传输

    2、UDP

    无连接的,不可靠的数据报传递。对于不稳定的网络层,采取完全不弥补的通信方式。默认还原网络状况

    优点:传输速度块。相率高。开销小

    缺点:不稳定 (数据流量。速度。顺序)

    使用场景:对时效性要求较高场合。稳定性其次。游戏、视频会议、视频电话。腾讯、华为、阿里--- 应用层数据校验协议, 弥补 udp 的不足

    二、UDP 实现的 C/S 模型

    2.1 通信过程

    recv() / send() 只能用于 TCP 通信。替代 read、write

    accpet():--- Connect();  ---被舍弃

    server:

            lfd = socket(AF_INET, STREAM, 0);

            SOCK_DGRAM  ---报式协议

            bind();

            listen(); --- 可有可无

            while(1) {

                    read(cfd, buf, sizeof)  ---被替换  ---recvfrom()  ---涵盖accept传出地址结构体

                    小 -- 大

                    write():---被替换--- sendto()

            }

            close();

    client:

            connfd = socket(AF_INET, SOCK_DGRAI, 0);

            sendto(‘服务器的地址结构’, 地址结构大小)

            recvfrom()

            写到屏幕

            close();

    2.2 函数讲解

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

            sockfd: lfd

            buf: 缓冲区地址

            len: 缓神区大小

            flags: 0

            src_addr: (struct sockaddr *) &addr 传出。对端地址结构

            addrlen: 传入传出

    返回值: 成功接收数据字节数。失败: -1 errn。 0: 对端关闭

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

            sockfd: 套接字

            buf: 存储数据的缓冲区

            len: 数据长度

            flags: 0

            src_addr: (struct sockaddr *) &addr 传入。目标地址结构

            addrlen: 地址结构长度

    返回值:成功接收数据字节数。失败 -1 errno

    2.3 代码

    server.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define SERV_PORT 8000
    7. int main(void)
    8. {
    9. struct sockaddr_in serv_addr, clie_addr;
    10. socklen_t clie_addr_len;
    11. int sockfd;
    12. char buf[BUFSIZ];
    13. char str[INET_ADDRSTRLEN];
    14. int i, n;
    15. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    16. bzero(&serv_addr, sizeof(serv_addr));
    17. serv_addr.sin_family = AF_INET;
    18. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    19. serv_addr.sin_port = htons(SERV_PORT);
    20. bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    21. printf("Accepting connections ...\n");
    22. while (1) {
    23. clie_addr_len = sizeof(clie_addr);
    24. n = recvfrom(sockfd, buf, BUFSIZ, 0, (struct sockaddr *)&clie_addr, &clie_addr_len);
    25. if (n == -1) {
    26. perror("recvfrom error");
    27. }
    28. printf("received from %S at PORT %d\n",
    29. inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
    30. ntohs(clie_addr.sin_port));
    31. for(i = 0; i < n; i++) {
    32. buf[i] = toupper(buf[i]);
    33. }
    34. n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
    35. if (n == -1) {
    36. perror("sendto error");
    37. }
    38. }
    39. close(sockfd);
    40. return 0;
    41. }

     client.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define SERV_PORT 8000
    7. int main(int argc, char *argv[])
    8. {
    9. struct sockaddr_in servaddr;
    10. int sockfd, n;
    11. char buf[BUFSIZ];
    12. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    13. bzero(&servaddr, sizeof(servaddr));
    14. servaddr.sin_family = AF_INET;
    15. inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
    16. servaddr.sin_port = htons(SERV_PORT);
    17. bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    18. while (fgets(buf, BUFSIZ, stdin) != NULL) {
    19. n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
    20. if (n == -1)
    21. perror("sendto error");
    22. n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0); //NULL:不关心对端信息
    23. if (n == -1)
    24. perror("recvfrom error");
    25. write(STDOUT_FILENO, buf, n);
    26. }
    27. close(sockfd);
    28. return 0;
    29. }

    三、本地套接字和网络套接字

    3.1 进程间通信方式 (IPC)

    pipe:易用性是最强的

    fifo:没有血缘关系进程之间的通信

    mmap:没有血缘关系进程之间的通信,可以反复读取

    信号:开销最小

    本地套接字 (domain):稳定性最好的(CS 模型)

    3.2 本地套接字对比网络 TCP 

    1、int socket(int domain, int type, int protocol);

    参数:

    domin: AF_INET 变为 AF_UNIX/AF_LOCAL

    type: SOCK_STREAM/SOCK_DGRAM 都可以

    2、地址结构 socke_in -> sockaddr_un

    struct sockaddr_in srv_addr; -> struct sockaddr_un srv_addr;

    srv_addr.sin_family = AF_INET; -> srv_adrr.sun_family = AF_UNIX;

    srv_addr.sin_port = htons(8888); -> strcpy(srv_addr.sun_path, "srv.socket")

    srv_addr.sin_addr.s_addr = hton(INADDR_ANY);

    bind(fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); -> bind(fd, (struct sockaddr *)&srv_addr, offsetof(struct sockaddr_un, sun_path) + strlen("srv.socket"));

    sockaddr_un

    sockaddr 数据结构

    bind 函数创建出来的这个文件,是一个伪文件

    3.3 代码

    服务端

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include "wrap.h"
    11. #define SERV_ADDR "serv.socket"
    12. int main(void)
    13. {
    14. int lfd, cfd, len, size, i;
    15. struct sockaddr_un servaddr, cliaddr;
    16. char buf[4096];
    17. lfd = Socket(AF_UNIX, SOCK_STREAM, 0);
    18. bzero(&servaddr, sizeof(servaddr));
    19. servaddr.sun_family = AF_UNIX;
    20. strcpy(servaddr.sun_path, SERV_ADDR);
    21. len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); /* servaddr total len */
    22. unlink(SERV_ADDR); /* 确保bind之前serv.sock文件不存在,bind会创建该文件 */
    23. Bind(lfd, (struct sockaddr *)&servaddr, len); /* 参3不能是sizeof(servaddr) */
    24. Listen(lfd, 20);
    25. printf("Accept ...\n");
    26. while (1) {
    27. len = sizeof(cliaddr); //AF_UNIX大小+108B
    28. cfd = Accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t *)&len);
    29. len -= offsetof(struct sockaddr_un, sun_path); /* 得到文件名的长度 */
    30. cliaddr.sun_path[len] = '\0'; /* 确保打印时,没有乱码出现 */
    31. printf("client bind filename %s\n", cliaddr.sun_path);
    32. while ((size = read(cfd, buf, sizeof(buf))) > 0) {
    33. for (i = 0; i < size; i++)
    34. buf[i] = toupper(buf[i]);
    35. write(cfd, buf, size);
    36. }
    37. close(cfd);
    38. }
    39. close(lfd);
    40. return 0;
    41. }

    客户端

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include "wrap.h"
    12. #define SERV_ADDR "serv.socket"
    13. #define CLIE_ADDR "clie.socket"
    14. int main(void)
    15. {
    16. int cfd, len;
    17. struct sockaddr_un servaddr, cliaddr;
    18. char buf[4096];
    19. cfd = Socket(AF_UNIX, SOCK_STREAM, 0);
    20. bzero(&cliaddr, sizeof(cliaddr));
    21. cliaddr.sun_family = AF_UNIX;
    22. strcpy(cliaddr.sun_path,CLIE_ADDR);
    23. len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path); /* 计算客户端地址结构有效长度 */
    24. unlink(CLIE_ADDR);
    25. Bind(cfd, (struct sockaddr *)&cliaddr, len); /* 客户端也需要bind, 不能依赖自动绑定*/
    26. bzero(&servaddr, sizeof(servaddr)); /* 构造server 地址 */
    27. servaddr.sun_family = AF_UNIX;
    28. strcpy(servaddr.sun_path, SERV_ADDR);
    29. len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); /* 计算服务器端地址结构有效长度 */
    30. Connect(cfd, (struct sockaddr *)&servaddr, len);
    31. while (fgets(buf, sizeof(buf), stdin) != NULL) {
    32. write(cfd, buf, strlen(buf));
    33. len = read(cfd, buf, sizeof(buf));
    34. write(STDOUT_FILENO, buf, len);
    35. }
    36. close(cfd);
    37. return 0;
    38. }

     

    3.4 总结

    对比 本地套 和 网络套

     

  • 相关阅读:
    什么是子网掩码?
    go语言 leetcod1 两数之和
    如何利用InVest模型估算区域产水量
    nginx禁用3DES和DES弱加密算法,保证SSL证书安全
    基于Unity引擎的RPG3D项目开发笔录
    Idea中flume的Interceptor的编写教程
    golang面试题:字符串转成byte数组,会发生内存拷贝吗?
    AC修炼计划(AtCoder Regular Contest 162)
    vue3源码分析——解密nextTick的实现
    Spring 源码阅读 10:自定义的 EntityResolver 是如何帮助 Spring 校验 XML 配置的
  • 原文地址:https://blog.csdn.net/HuanBianCheng27/article/details/127741483