深入探讨 UDP 协议与多线程 HTTP 服务器

一、UDP 协议:高效但“不羁”的传输使者

UDP 协议以其独特的特性在网络传输中占据一席之地,适用于对实时性要求高、能容忍少量数据丢失的场景。
请添加图片描述

1. UDP 的特点解析

  • 无连接:无需提前建立连接,如同“不打电话直接寄快递”,减少了连接建立的时间开销,想发就发。
  • 不可靠:不保证数据一定到达、不检测错误、不排序。但这也让它省去了复杂的确认机制,适合视频通话、在线游戏等场景,偶尔卡顿或丢包可接受。
  • 数据报传输:以“块”为单位传输(数据报),发送端发多少,接收端收多少,保持原始边界。例如发送“abc”和“def”,接收端不会合并为“abcdef”。
  • 速度快、开销小:头部仅 8 字节(TCP 为 20 字节),额外负担少,传输效率高,适合直播、DNS 查询等“抢时间”场景。
  • 不适应网络拥塞控制:无“减速”机制,网络拥堵时可能丢包更多,但能保持快速发送,与 TCP 的自适应减速形成对比。
  • 支持多种通信方式:可单播(一对一)、广播/组播(一对多),如网课直播向多个设备同时发送数据。

一句话总结:UDP 如同“快递急件”,速度快但不确保签收,适合实时性要求高或简单传输的场景。

2. UDP 网络编程代码解析

服务器端(ser.c
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  int main() {  // 创建 UDP 套接字,AF_INET 表示 IPv4,SOCK_DGRAM 表示 UDP 类型,0 表示默认协议  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);  if (sockfd == -1) {  perror("socket err");  exit(1);  }  struct sockaddr_in saddr, caddr;  memset(&saddr, 0, sizeof(saddr));  saddr.sin_family = AF_INET;  saddr.sin_port = htons(6000); // 端口号转网络字节序  saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 绑定本地回环地址  // 绑定套接字到指定地址和端口  int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));  if (res == -1) {  perror("bind err\n");  exit(1);  }  while (1) {  char buff[128] = {0};  int len = sizeof(caddr);  // 接收数据,recvfrom 用于 UDP,可获取发送方地址  int n = recvfrom(sockfd, buff, 128, 0, (struct sockaddr*)&caddr, &len);  printf("recvfrom(%s) buff:%s\n", inet_ntoa(caddr.sin_addr), buff);  // 向发送方回复“ok”  sendto(sockfd, "ok", 2, 0, (struct sockaddr*)&caddr, sizeof(caddr));  }  close(sockfd);  
}  
客户端(cli.c
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  int main() {  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);  if (sockfd == -1) {  perror("socket err");  exit(1);  }  struct sockaddr_in saddr;  saddr.sin_family = AF_INET;  saddr.sin_port = htons(6000);  saddr.sin_addr.s_addr = inet_addr("127.0.0.1");  while (1) {  printf("input:\n");  char buff[128] = {0};  fgets(buff, 128, stdin);  if (strncmp(buff, "end", 3) == 0) {  break;  }  // 向服务器发送数据  sendto(sockfd, buff, strlen(buff) - 1, 0, (struct sockaddr*)&saddr, sizeof(saddr));  memset(buff, 0, 128);  int len = sizeof(saddr);  // 接收服务器响应  recvfrom(sockfd, buff, 128, 0, (struct sockaddr*)&saddr, &len);  printf("buff(%s):%s\n", inet_ntoa(saddr.sin_addr), buff);  }  close(sockfd);  
}  
  • 服务器端:创建 UDP 套接字,绑定到 127.0.0.1:6000,循环接收客户端数据,打印发送方 IP 和数据,回复“ok”。
  • 客户端:创建 UDP 套接字,循环读取用户输入,发送给服务器,接收并打印服务器响应,输入“end”时退出。

二、多线程 HTTP 服务器:构建 Web 通信枢纽

HTTP 协议(端口 80,HTTPS 为 443)是应用层的核心协议,基于 TCP 协议,具有无状态、简单灵活等特点,广泛用于 Web 通信。多线程设计使其能并发处理多个客户端请求,提升性能。
请添加图片描述

1. HTTP 协议深度解析

  • 请求方法:常见的有 GET(获取资源)、POST(提交数据)、PUT(更新资源)、DELETE(删除资源)等。例如,浏览器访问网页使用 GET 请求获取页面内容。
    在这里插入图片描述

  • 状态码

    • 2xx(如 200 OK):表示请求成功。
    • 4xx(如 404 NOT FOUND):客户端错误,资源不存在。
    • 5xx(如 500 Internal Server Error):服务器内部错误。
      在这里插入图片描述
  • 长连接与短连接

    • 短连接:每次请求都新建 TCP 连接,完成后关闭。适用于请求不频繁的场景,如普通网页浏览。
    • 长连接(Keep-Alive):多个请求复用一个 TCP 连接,减少连接建立开销,提升效率,适用于频繁交互的场景,如单页应用(SPA)。
  • 无状态特性:HTTP 协议本身不记录客户端状态,通过 CookieSession 等机制实现会话跟踪。例如,用户登录后,服务器通过 Cookie 识别用户后续请求。

  • http请求报文如下图
    在这里插入图片描述

  • http应答报文如下图
    在这里插入图片描述

2. 多线程 HTTP 服务器代码详解

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <pthread.h>  
#include <fcntl.h>  #define PATH "/home/stu/0101/http"  
int socket_init();  
char *strtok_fun(char buff[]) {  char *saveptr;  // 解析请求头第一行,获取请求的文件名。例如,对于“GET /index.html HTTP/1.1”,提取“/index.html”  char *p = strtok_r(buff, " ", &saveptr);  if (p != NULL) {  p = strtok_r(NULL, " ", &saveptr);  return p;  }  return NULL;  
}  void *thread_fun(void *arg) {  int c = *((int *)arg);  free(arg);  while (1) {  char buff[1024] = {0};  int n = recv(c, buff, 1024, 0);  if (n == 0) { // 客户端关闭连接,recv 返回 0  break;  }  if (n == -1) {  perror("recv err");  break;  }  char *filename = strtok_fun(buff);  if (filename == NULL) {  break;  }  if (strcmp(filename, "/") == 0) {  filename = "/index.html"; // 若请求根路径,默认返回 index.html  }  char path[256];  strcpy(path, PATH);  strcat(path, filename);  printf("path:%s\n", path);  int file_id = open(path, O_RDONLY);  if (file_id == -1) { // 文件不存在,构造 404 响应头  char head[256] = {"HTTP/1.1 404 NOT FOUND\r\n"};  strcat(head, "Server: myhttp\r\n");  sprintf(head + strlen(head), "Content-Length: 0\r\n");  strcat(head, "\r\n");  send(c, head, strlen(head), 0);  break;  }  int filesize = lseek(file_id, 0, SEEK_END);  lseek(file_id, 0, SEEK_SET);  char head[256] = {"HTTP/1.1 200 OK\r\n"};  strcat(head, "Server: myhttp\r\n");  sprintf(head + strlen(head), "Content-Length: %d\r\n", filesize);  strcat(head, "\r\n");  send(c, head, strlen(head), 0); // 发送 200 响应头,包含服务器信息、内容长度等  char data[1024] = {0};  int num = 0;  while ((num = read(file_id, data, 1024)) > 0) {  send(c, data, num, 0); // 分块读取文件内容并发送给客户端  }  close(file_id);  }  printf("cli close");  close(c);  pthread_exit(NULL);  
}  int main() {  int sockfd = socket_init();  if (sockfd == -1) {  exit(1);  }  while (1) {  int c = accept(sockfd, NULL, NULL);  if (c == -1) {  perror("accept err");  continue;  }  pthread_t id;  int *p = (int *)malloc(sizeof(int));  *p = c;  if (pthread_create(&id, NULL, thread_fun, (void *)p) != 0) {  perror("pthread_create err");  free(p);  close(c);  } else {  pthread_detach(id); // 分离线程,使其结束后自动释放资源,避免内存泄漏  }  }  
}  int socket_init() {  int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建 TCP 套接字,SOCK_STREAM 表示面向连接的 TCP  if (sockfd == -1) {  perror("socket err");  return -1;  }  struct sockaddr_in saddr;  saddr.sin_family = AF_INET;  saddr.sin_port = htons(80); // 绑定 80 端口,HTTP 协议默认端口  saddr.sin_addr.s_addr = inet_addr("127.0.0.1");  int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));  if (res == -1) {  perror("bind err");  return -1;  }  if (listen(sockfd, 5) == -1) {  perror("listen err");  return -1;  }  return sockfd;  
}  
  • socket_init 函数:初始化 TCP 套接字,绑定到 127.0.0.1:80,通过 listen 开始监听,使服务器处于等待客户端连接状态。
  • main 函数:循环调用 accept 接收客户端连接。每收到一个连接,创建新线程处理(thread_fun),通过 pthread_detach 分离线程,确保线程结束后资源自动释放,避免服务器资源泄漏。
  • thread_fun 函数
    • 读取客户端请求数据,解析请求头获取文件名。
    • 根据文件名构造文件路径,尝试打开文件。若文件不存在,发送 404 响应头;若存在,发送 200 响应头(包含 HTTP 协议版本、状态码、服务器名称、内容长度等),然后分块读取文件内容并发送给客户端。
    • 处理完一个客户端请求后,关闭连接套接字,线程退出。

3. 多线程 HTTP 服务器的优势

  • 并发处理:每个客户端连接独立分配线程,多个客户端请求可同时处理,提升服务器吞吐量,避免单个请求阻塞影响整体服务。
  • 资源利用:充分利用多核 CPU 资源,线程间相互独立,提高系统资源利用率。
  • 响应速度:及时处理客户端请求,减少等待时间,提升用户体验,尤其适合高并发场景,如电商网站、新闻门户等。

UDP 协议以其高效简洁适用于特定场景,而多线程 HTTP 服务器通过并发处理与 HTTP 协议特性结合,成为 Web 通信的重要支柱。深入理解这些技术,能更好地应对网络编程挑战,构建强大稳定的网络应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/80009.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/80009.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/80009.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

引用第三方自定义组件——微信小程序学习笔记

1. 使用 npm 安装第三方包 1.1 下载安装Node.js 工具 下载地址&#xff1a;Node.js — Download Node.js 1.2 安装 npm 包 在项目空白处右键弹出菜单&#xff0c;选择“在外部终端窗口打开”&#xff0c;打开命令行工具&#xff0c;输入以下指令&#xff1a; 1> 初始化:…

数字化转型是往哪转?怎么转?

写在前面 当下数字化转型的风还在吹&#xff0c;企业数字化转型过程中以数字化项目满足业务化需求&#xff0c;已有相关数字化平台的话&#xff0c;就搞大平台、大系统&#xff0c;解决数据孤岛。政府数字化转型亦是如此&#xff0c;某些省市发了系统优化整合的文&#xff0c;旨…

嵌入式学习--江协51单片机day2

今天学的不多&#xff0c;内容为&#xff1a;静态、动态数码管的控制&#xff0c;模块化编程和lcd1602调试工具 数码管的控制 由于内部电路的设计&#xff0c;数码管每次只能显示一个位置的一个数字&#xff0c;动态的实现是基于不同位置的闪烁频率高。 P2_4,P2_3,P2_2控制位…

《数据结构:二叉搜索树(Binary Search Tree)》

文章目录 :red_circle:一、二叉搜索树的概念:red_circle:二、二叉搜索树的性能分析:red_circle:三、二叉搜索树的操作&#xff08;一&#xff09;插入&#xff08;二&#xff09;查找&#xff08;三&#xff09;删除 :red_circle:四、二叉搜索树的实现代码&#xff08;一&#…

【Linux相关】实时查看Nvidia-smi使用情况

【Linux相关】 实时查看Nvidia-smi使用情况 文章目录 实时查看Nvidia-smi使用情况 实时查看Nvidia-smi使用情况 在本地终端执行下述语句 watch -n 1 nvidia-smi每一秒都会更新&#xff0c;将 1 改为其他数字可以满足不同需求

Kotlin密封类优化Android状态管理

Kotlin 的密封类&#xff08;Sealed Class&#xff09;确实是 Android 开发中管理复杂 UI 状态的利器。它通过类型安全的层次结构&#xff0c;让状态管理代码更加清晰简洁。让我们从实际开发场景出发&#xff0c;深入探讨其应用&#xff1a; 一、密封类核心优势 受限的类继承…

JavaWeb:SpringBootWeb快速入门

介绍 Spring SpringBoot 入门程序 需求 步骤 修改端口 1.新建application.yml #设置端口 server:port: 8081入门程序-分析 为什么main方法能启动web应用-内嵌tomcat 为什么tomcat能定位HelloController程序 请求先到DisPatcherServlet&#xff0c;根据路径转发 小结 1.…

Unity学习笔记二

文章目录 3D数学公共计算结构体Mathf常用成员三角函数 向量Vector3基本成员点乘叉乘插值运算 四元数引出基本概念Quaternion结构体成员四元数运算 更多的Mono延迟函数协同程序多线程相关协程概念辨析协程本体协程调度器 Resources资源动态加载特殊文件夹Resources同步加载Resou…

为什么Transformer推理需要做KV缓存

一、我们先来回忆一下在transformer中KV在哪里出现过&#xff0c;都有什么作用&#xff1f; α的计算过程&#xff1a; 这里引入三个向量&#xff1a; 图中的q为Query&#xff0c;用来匹配key值 图中的k为key,用来被Query匹配 图中的Value&#xff0c;是用来被进行加权平均的 由…

【大模型面试】大模型(LLMs)高频面题全面整理(★2025年5月最新版★)

【大模型面试】大模型&#xff08;LLMs&#xff09;高频面题全面整理&#xff08;★2025年5月最新版★&#xff09; &#x1f31f; 嗨&#xff0c;你好&#xff0c;我是 青松 &#xff01; &#x1f308; 自小刺头深草里&#xff0c;而今渐觉出蓬蒿。 本笔记适合大模型初学者和…

JAVA:使用 iTextPDF 处理 PDF 的技术详解

1、简述 iTextPDF 是一个功能强大的 Java PDF 库,可以用来创建、修改和处理 PDF 文档。通过它,我们可以完成如生成 PDF、读取 PDF 内容、添加水印、合并 PDF 等多种操作。本篇博客将详细介绍 iTextPDF 的使用方法,并提供一些实践样例,帮助开发者快速上手。 样例代码: htt…

模态与非模态窗口及使用时的数据交互

模态窗口使用exec()方法显示&#xff0c;会阻塞父窗口&#xff0c;直到对话框关闭&#xff1b; 非模态对话框允许同时操作主窗口和设置窗口&#xff0c;使用show()。 模态和非模态的主要区别在于用户能否与父窗口交互&#xff0c;非模态更适合需要频繁切换的场景。非模态窗口需…

Docker进入MySQL之后如何用sql文件初始化数据

关闭Docker-compose.yml里面所有容器 docker compose -f docker_compose.yml down后台形式开启Docker-compose.yml所有容器 docker compose -f docker_compose.yml up -d罗列出所有启动过的&#xff08;包括退出过的&#xff09;容器 docker ps -a进入指定容器ID内部 docke…

MAC 地址

MAC地址&#xff08;Media Access Control Address&#xff09;是指网络设备在数据链路层使用的唯一标识符&#xff0c;也称为硬件地址或物理地址。它用于标识设备之间的网络通信&#xff0c;是网络适配器&#xff08;如网卡、Wi-Fi适配器等&#xff09;的唯一标识。每个网络设…

Redis 7.0中5种新特性及实战应用

Redis 7.0引入了多项革命性的新特性&#xff0c;不仅在性能和可靠性方面有所提升&#xff0c;更在功能和使用体验上有了质的飞跃。本文将介绍Redis 7.0的五大关键新特性&#xff0c;可以根据实际情况利用Redis 7.0的强大功能&#xff0c;构建更高效、更可靠的应用系统。 特性一…

PHP实现PDF自动签名

技术要点&#xff1a;在PDF中找到一个固定锚点&#xff0c;在需要放置图片的地方找到测试出锚点对应的XY位 // 使用了poppler方法&#xff0c;其他PDF库在获取坐标方面有各种问题&#xff0c;他的安装是在Linux底层&#xff0c;比在PHP项目中用Composer安装的库看上去更稳定&a…

中达瑞和便携式高光谱相机:珠宝鉴定领域的“光谱之眼”

在珠宝行业中&#xff0c;真伪鉴定始终是核心需求。随着合成技术与优化处理手段的日益精进&#xff0c;传统鉴定方法逐渐面临挑战。中达瑞和推出的便携式高光谱相机&#xff0c;凭借其独特的“图谱合一”技术&#xff0c;为珠宝真假鉴定提供了科学、高效且无损的解决方案&#…

2025年渗透测试面试题总结-某战队红队实习面经(附回答)(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 某战队红队实习面经 个人经历与技术能力 2. HVV/攻防演练成绩 3. 上一个工作主要内容 4. 有意思的逻…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】5.1 描述性统计分析(均值/方差/分位数计算)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 5.1 描述性统计分析&#xff1a;均值、方差与分位数计算实战5.1.1 数据准备与分析目标数据集介绍分析目标 5.1.2 均值计算&#xff1a;从整体到分组分析总体均值计算加权均值…

npm下载插件无法更新package.json和package-lock.json文件的解决办法

经过多番查证&#xff0c;使用npm config ls查看相关配置等方式&#xff0c;最后发现全局的.npmrc文件的配置多写了globaltrue&#xff0c;去掉就好了 如果参数很多&#xff0c;不知道是哪个参数引起的&#xff0c;先只保留registryhttp://xxx/&#xff0c;试试下载&#xff0…