当前位置: 首页 > news >正文

《Linux 网络编程二:UDP 与 TCP 的差异、应用及问题应对》

一、UDP 与 TCP 对比表

对比项UDPTCP
连接方式无需建立连接有连接(三次握手建立,四次挥手断开)
传输可靠性尽最大努力交付,可能丢包安全可靠的数据传输机制
面向对象面向数据包面向数据流
传输模式一对一、一对多传输本质一对一,并发实现一对多
协议机制机制简单,资源开销小,实时性高机制复杂,网络资源开销大
报文头部8 字节(源端口、目标端口、长度、校验和)更复杂,包含更多控制信息
应用场景视频流、音频流等实时性要求高的场景文件传输、HTTP 等准确性要求高的场景
编程复杂度相对简单相对复杂,需处理连接建立与断开

二、UDP 相关内容

1.UDP 丢包原因

        UDP 存在接收缓冲区,当发送数据速度过快,致使接收缓冲区满时,后续数据包会丢失。UDP 无需建立连接,进一步增加了丢包可能性。

2.UDP 特点

  • 面向数据包:以数据包为基本传输单位。
  • 无需建立连接:通信前无需预先建立连接。
  • 尽力交付:是不安全可靠的数据协议,存在数据丢包情况。
  • 传输模式多样:能够实现一对一、一对多的传输。
  • 机制简单高效:机制简单,资源开销小,数据实时性高。

3.避免 UDP 丢包方法

  • 发送延时:在发送时使用 usleep() 函数延时,让接收方有足够时间处理数据。
  • 模仿 TCP 应答:发送一个数据后,等待接收端回应,收到回应后再发送下一包数据。

4.UDP 报文头部字段

字段字节数含义
源端口号2 字节发送方进程端口号
目标端口号2 字节接收方进程端口号
长度2 字节UDP 报文长度(头部加正文)
校验和2 字节用于数据差错校验

UDP 报文头部共 8 字节。


三、TCP 相关内容

1.TCP 协议概述

        TCP 即传输控制协议,位于传输层,采用流式套接字。

2.TCP 特点

  • 面向数据流:将数据作为连续的字节流处理。
  • 有连接:通信前需通过三次握手建立连接。
  • 安全可靠:具备安全可靠的数据传输机制。
  • 机制复杂:机制复杂,网络资源开销大。
  • 通信模式:本质只能实现一对一通信,可通过 TCP 并发方式实现一对多通信。

3.TCP 机制

        三次握手:TCP 建立连接时需进行三次握手,确保双方通信前都已准备就绪。SYN 为请求建立连接标志位,ACK 为响应报文标志位。

        四次挥手:确保断开连接时需进行四次挥手,保证断开连接前双方都已通信结束。FIN 为请求断开连接标志位,ACK 为响应报文标志位。ACK 和 FIN 不能一起,防止客户端断开后服务端还想发送信息。

    4.TCP 编程流程

            1)客户端流程
            

            socket() 创建套接字
            connect() 请求建立连接
            send() 发送
            recv() 接收
            close() 关闭

            2)服务端流程

            socket() 创建监听套接字
            bind() 绑定
            listen() 监听要建立连接的客户端
            accept() 接受完成三次握手的客户端并产生通信套接字
            recv()
            send()
            close()

    5.相关函数接口

    • connect():请求与服务端建立连接。

       int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
      • 参数:sockfd(套接字)、addr(服务端地址信息)、addrlen(服务端地址大小)。
      • 返回值:成功返回 0,失败返回 -1。
    • send():发送网络数据。

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
      • 参数:sockfd(网络套接字)、buf(发送数据首地址)、len(发送字节数)、flags(默认 0)。
      • 返回值:成功返回实际发送字节数,失败返回 -1。
    • listen():监听建立三次握手的客户端。

      int listen(int sockfd, int backlog);
      • 参数:sockfd(监听套接字)、backlog(最大监听客户端数)。
      • 返回值:成功返回 0,失败返回 -1。
    • accept():接收建立三次握手的客户端并产生通讯套接字。

      int accept(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);
      • 参数:socket(监听套接字)、address(客户端地址信息)、address_len(客户端地址长指针)。
      • 返回值:成功返回通讯套接字,失败返回 -1。
    • recv():从网络套接字接收数据。

      ssize_t recv(int sockfd, void *buf, size_t len, int flags);
      • 参数:sockfd(通讯套接字)、buf(接收数据首地址)、len(期望接收字节数)、flag(默认 0)。
      • 返回值:成功返回实际接收字节数,失败返回 -1,对方断开连接返回 0。

    6.TCP 粘包问题

    问题描述
    发送方应用层发送的多包数据,在接收方可能一次读到,多包数据产生粘连。

    原因

    • 发送方速度快,TCP 底层可能对多包数据重新组帧。
    • 接收方数据处理速度慢,多包数据在接收缓冲区缓存,应用层读时一次读出。

    7.解决方法

    • 调整发送速率:控制发送速度,避免粘包。
    • 发送指定大小数据:发送方发送指定大小数据,接收方也接收指定大小数据。注意跨平台数据传输时结构体对齐问题。
    • 增加分隔符:在应用层为发送的数据增加分隔符,接收方利用分隔符解析数据。
    • 自定义数据帧格式:封装自定义数据帧格式(协议)进行发送,并严格按协议解析。

    四、代码部分

            1.客户端不断从终端接收数据,使用TCP发送给服务端,服务端输出。

            head.h

    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>

            client.c

    #include "head.h"int main(int argc, char const *argv[])
    {int sockfd = socket(AF_INET, SOCK_STREAM,0);if(sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;//种类seraddr.sin_port = htons(50000);//端口号seraddr.sin_addr.s_addr = inet_addr("192.168.0.192");//ip地址int ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("connect error");return -1;}char buff[1024] = {0};while (1){fgets(buff, sizeof buff, stdin);ssize_t cnt = send(sockfd, buff, strlen(buff), 0);if(cnt < 0){perror("send error");return -1;}printf("cnt = %ld\n", cnt);memset(buff, 0, sizeof(buff));}close(sockfd);return 0;
    }
    

            recv.c

    #include "head.h"int main(int argc, char const *argv[])
    {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.192");int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("bind error");return -1;}ret = listen(sockfd, 10);if(ret < 0){perror("listen error");return -1;}int connfd = accept(sockfd, NULL, NULL);if(connfd < 0){perror("accept error");return -1;}char buff[1024] = {0};while(1){   ssize_t cnt = recv(connfd, buff, sizeof(buff), 0);if(cnt <= 0){printf("recv error");return -1;}printf("cnt = %ld, buff = %s\n", cnt, buff);memset(buff, 0, sizeof(buff));}close(connfd);close(sockfd);return 0;
    }
    
    http://www.dtcms.com/a/348783.html

    相关文章:

  • Grafana k6 性能测试
  • golang5字符串
  • Linux驱动之DMA(三)
  • 强光干扰下漏检率↓78%!陌讯动态决策算法在智慧交通违停检测的实战优化
  • 自动化运维之k8s——Kubernetes集群部署、pod、service微服务、kubernetes网络通信
  • SSRF的学习笔记
  • MATLAB 入门:从变量定义到基础绘图的完整上手指南
  • 学习Java25天
  • 杭电oj第2061题:Treasure the new start, freshmen!
  • 今天学习计算机网格技术的TCP,UDP以及OSPF
  • 南科大C++ 第四章(数组,结构体,联合体,枚举)
  • odoo 工作台
  • Microsoft .NET Packages AIO:全面的.NET开发框架
  • 强光干扰下检出率↑93%!陌讯多模态融合算法在充电桩车位占用检测的实战解析
  • DDR3入门系列(一)——初识DDR3
  • FastAPI中定时任务的使用详解
  • Kernel Pwn 入门(五) 条件竞争 userfaultfd利用
  • PMP项目管理知识点-②项⽬环境
  • LeetCode 第464场周赛 第三天
  • 抽奖池项目测试
  • 【信息安全】英飞凌TC3xx安全调试口功能实现(调试口保护)
  • 解决Ubuntu22.04 安装vmware tools之后,不能实现文件复制粘贴和拖拽问题
  • AIStarter安装与调试:一键启动与收益中心教程
  • 为什么hive在处理数据时,有的累加是半累加数据
  • Codejock Suite ProActiveX COM Crack
  • C++如何将多个静态库编译成一个动态库
  • 【C++】 9. vector
  • golang3变量常量
  • 【golang长途旅行第30站】channel管道------解决线程竞争的好手
  • 在WSL2-Ubuntu中安装Anaconda、CUDA13.0、cuDNN9.12及PyTorch(含完整环境验证)