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

嵌入式学习day38

http天气项目总结:

http用户层协议

从网上获得了天气数据

客户端程序:
socket

        connect:组织了http请求报文并发送

        回复响应报文 + 数据

        对数据做解析

协议:
每一次的通信的规则

connection:keep-alive //长连接

特点:

  1. tcp通信建立连接

connection:close //短连接

特点:

  1. 每次通信,都需要重新建立连接

半包问题

做处理:

  1. connect server
  2. request_http_future
  3. recv http response
  4. parse_data
  5. close

细节:
解析数据时 --- 段错误

        gcc main.c -g

        gdb ./a.out

        r //运行

        如果函数返回值是指针类型,要做错误判断

        if (p == NULL)


并发服务器

同时处理多个客户端

多进程实现并发:

#include "head.h"void do_child(int signo)
{wait(NULL);
}
int main(int argc, char const *argv[])
{//step1 socket int fd = socket(AF_INET,SOCK_STREAM,0);if (fd < 0){perror("socket fail");return -1;}struct sockaddr_in seraddr;bzero(&seraddr,sizeof(seraddr));seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");int on = 1;setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int));//step2 bind if (bind(fd,(const struct sockaddr *)&seraddr,sizeof(seraddr)) < 0){perror("connect fail");return -1;}//step3 listenif (listen(fd,5) < 0){perror("listen fail");return -1;}struct sockaddr_in cliaddr;bzero(&cliaddr,0);socklen_t len = sizeof(cliaddr);//step4 acceptsignal(SIGCHLD,do_child);while (1){int connfd = accept(fd,(struct sockaddr *)&cliaddr,&len);if (connfd < 0){perror("accept fail");return -1;}printf("---client connect---\n");printf("client ip:%s\n",inet_ntoa(cliaddr.sin_addr));printf("port: %d\n",ntohs(cliaddr.sin_port));if (fork() == 0){   close(fd);char buf[1024];while(1){recv(connfd,buf,sizeof(buf),0);printf("buf = %s\n",buf );if (strncmp(buf,"quit",4) == 0){break;}}close(connfd);}close(connfd);}close(fd);return 0;
}

多线程实现并发:

#include "head.h"void do_child (int signo)
{wait(NULL); 
}void *do_client(void *arg)
{int connfd = *(int *)arg;char buf[1024];while(1){recv(connfd,buf,sizeof(buf),0);printf("buf = %s\n",buf );if (strncmp(buf,"quit",4) == 0){close(connfd);//pthread_exit(NULL);return NULL;}}}int main(int argc, char const *argv[])
{//step1 socket int fd = socket(AF_INET,SOCK_STREAM,0);if (fd < 0){perror("socket fail");return -1;}struct sockaddr_in seraddr;bzero(&seraddr,sizeof(seraddr));seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");int on = 1;setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on, sizeof(int));   //step2 bind if (bind(fd,(const struct sockaddr *)&seraddr,sizeof(seraddr)) < 0){perror("connect fail");return -1;}//step3 listenif (listen(fd,5) < 0){perror("listen fail");return -1;}struct sockaddr_in cliaddr;bzero(&cliaddr,0);socklen_t len = sizeof(cliaddr);//step4 acceptsignal(SIGCHLD,do_child);while (1){int connfd = accept(fd,(struct sockaddr *)&cliaddr,&len);if (connfd < 0){perror("accept fail");return -1;}printf("---client connect---\n");printf("client ip:%s\n",inet_ntoa(cliaddr.sin_addr));printf("port: %d\n",ntohs(cliaddr.sin_port));//创建 线程 //让子线程去通信 pthread_t tid;int ret = pthread_create(&tid,NULL,do_client,&connfd);if (ret != 0){errno = ret; perror("pthread_create fail");return -1;}pthread_detach(tid); //设置为分离 }return 0;
}

总结:

  • 单循环服务器简单,可以处理多客户端,但不能同时处理
  • 并发服务器(多进程和多线程),可以处理多个客户端,可以同时处理
    • 多进程方式的效率低于多线程

多路IO复用

I --- input

O --- output

多路IO把负责通信的那个进程或线程可以不可以用来处理多个客户端的通信

提高并发的能力

IO的处理的模型:

阻塞IO模型:

        i -- 读

                scanf/getchar/fgets/read/recv

        o -- 写

                 管道的写有阻塞

  • 当进程调用 I/O 操作(如 read(), write(), recv(), send() 等)时,如果数据尚未准备好(例如读缓冲区为空或写缓冲区已满),当前线程会被挂起(阻塞),直到数据准备好或操作完成。

  • 整个过程中,CPU 不会空转,线程会被放入等待队列,直到内核通知其继续执行。

非阻塞IO模型:

  • 当你对一个文件描述符(如 socket)设置为非阻塞模式后,所有 I/O 系统调用(如 read(), write(), recv(), send())在数据未准备好时不会阻塞线程,而是立即返回一个错误码(通常是 EAGAINEWOULDBLOCK)。

  • 用户程序需要** 主动轮询(polling)**或通过 I/O 多路复用(如 select, poll, epoll)来判断何时数据准备好。

fcntl(fd, F_SETFL, O_NONBLOCK);  // 设置为非阻塞模式
ssize_t n = read(fd, buf, sizeof(buf));
if (n == -1 && errno == EAGAIN) {// 数据还没准备好,不能读
}

总结:

信号驱动IO:

信号驱动 I/O(Signal-driven I/O)是 Linux 提供的异步通知机制
当文件描述符上出现可读写事件时,内核不阻塞线程,而是向进程发送一个信号(SIGIO),进程在信号处理函数里再去调用 read() / write() 完成实际的 I/O 操作。
它介于“同步非阻塞轮询”和“真·异步 I/O”之间,被 POSIX 归类为同步 I/O(因为真正的数据拷贝仍然由用户线程完成)。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>int g_fd;
void do_handler(int signo)
{char buf[1024] = {0};read(g_fd,buf,sizeof(buf));printf("buf = %s\n",buf);
}int main(int argc, const char *argv[])
{if (mkfifo(argv[1],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}printf("mkfifo success\n");int fd = open(argv[1],O_RDONLY);if (fd < 0){perror("open fail");return -1;}g_fd = fd;printf("-------open-----\n");char buf[1024] = {0};int flags = fcntl(fd,F_GETFL);flags = flags | O_ASYNC;fcntl(fd,F_SETFL,flags);fcntl(fd, F_SETOWN, getpid());signal(SIGIO,do_handler);int i = 0;while (1){printf("i = %d\n",i);sleep(1);++i;}close(fd);return 0;
}

总结:

模型                   数据准备阶段        数据拷贝阶段       是否阻塞线程
阻塞 I/O             阻塞                       阻塞                     全程阻塞
非阻塞 I/O          轮询(立即返回)     阻塞                     仅拷贝阻塞
信号驱动 I/O       内核发信号         阻塞                      仅拷贝阻塞
真异步 I/O(AIO)   内核完成          内核完成               全程不阻塞

select

#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

功能(What)

在一次系统调用里同时等待多个文件描述符(fd)的 I/O“就绪”事件:

  • 读就绪(可读/有新连接/对端关闭会读到 0)

  • 写就绪(发送缓冲区可写)

  • 异常(主要是 TCP 带外数据 OOB)
    适合:跨平台、小规模并发场景。

参数(Params)

  • nfds:三个集合中最大 fd 值 + 1(不是数量)。

  • readfds:关心读就绪的 fd 集合(可为 NULL)。

  • writefds:关心写就绪的 fd 集合(可为 NULL)。

  • exceptfds:关心“异常”事件(主要 OOB),一般业务很少用(可为 NULL)。

  • timeout:超时控制(可为 NULL)。

    • NULL:一直等到有事件。

    • {0,0}:非阻塞轮询,立刻返回。

    • 其他:最多等待指定时间。

注意:Linux 会修改 timeout 为“剩余时间”readfds/writefds/exceptfds 也会被改写为“就绪子集”

返回值(Return)

  • > 0:本次就绪的 fd 个数

  • = 0:超时。

  • = -1:出错,errno 常见:

    • EINTR:被信号打断(应重试这一轮或结合 pselect)。

    • EBADF:集合里有无效/已关闭的 fd。

    • EINVAL:参数非法(如 nfds 小于等于最大 fd)。

http://www.dtcms.com/a/354303.html

相关文章:

  • 怎样选择合适的报表系统?报表系统的主要功能有什么
  • PLC_博图系列☞基本指令”S_PULSE:分配脉冲定时器参数并启动“
  • PyTorch闪电入门:张量操作与自动微分实战
  • Wxml2Canvas在组件中的渲染获取不到元素问题
  • vue 海康视频插件
  • Java Spring Boot 集成淘宝 SDK:实现稳定可靠的商品信息查询服务
  • AI鱼塘,有你画的鱼吗?
  • 代码随想录刷题Day44
  • IDEA连接阿里云服务器中的Docker
  • 嵌入式学习日志————DMA直接存储器存取
  • 微信开发者工具中模拟调试现场扫描小程序二维码功能
  • Centos 7.6离线安装docker
  • 元宇宙+RWA:2025年虚拟资产与真实世界的金融融合实验
  • aiohttp模块如何使用
  • 开发避坑指南(39):Java List全null元素引发的空判断无效处理方案
  • 用LightRAG打造智能问答系统:知识图谱与RAG的融合应用实践
  • 如何在升级到S/4HANA后关闭SAP旧系统?
  • 3-4〔OSCP ◈ 研记〕❘ WEB应用攻击▸Burp Suite工具
  • MySQL中的InnoDB
  • pikachu-xss通关(1-8)
  • qt5+mingw64+opencv4.5.2搭建调试环境
  • FOC算法第二节 克拉克变换
  • mybatis-plus实现苍穹外卖项目-分类操作,不定期更新-day2
  • Hadoop(五)
  • AI、人工智能基础: 模型剪枝的概念与实践(PyTorch版)
  • uvloop深度实践:从原理到高性能异步应用实战
  • 死锁产生的条件是什么? 如何进行死锁诊断?
  • 本地部署DeepSeek大模型的基本方法
  • 自定义命令行补全机制的核心工具之compgen
  • web服务组件