Linux下C语言实现HTTP文件服务器和TCP协议实现网络数据传输

news2024/11/30 12:52:20

       在实际开发中经常用到web框架,比如Servlet,SpringBoot等,这些开发框架提高了我们的开发效率,节省了开发时间。但是这会令我们技术人员处于浮云之上,看不到其本质。说实话,Java语言那么流行,其本质是运行在JRE虚拟机上的,而JRE是用C/C++语言开发的。与其说java跨平台,不如说是因为在不同平台上各自实现JRE,从而屏蔽了java语言直接与不同平台打交道。http协议广泛应用,也是基于TCP协议之上的封装。本节博主将带领大家用C语言在Linux环境下开发HTTP服务器,支持浏览器下载和浏览文件。另外还使用TCP协议开发了服务端和客户端来实现服务端监听客户端连接,然后向其发送一首唐诗。

目录

1.HTTP服务器

1.1 源码

1.2 效果

2.TCP服务器和客户端

2.1 源码

2.1 效果


1.HTTP服务器

1.1 源码

头文件:

#pragma once
#include <pthread.h>

//线程参数结构
struct ThreadParam {
    pthread_t tid;
    int fd;
    int epfd;
};

int initListenFd(unsigned short port);

int epoolRun(int lfd);

void* acceptClient(void *);

void* recvHttpRequest(void *);

int parseRequestLine(int cfd, const char *line);

int sendFile(int cfd, const char *fileName);

int sendHeadMsg(int cfd, int status, const char *descr, const char *type, int length);

const char *getFileType(const char *name);

int sendDir(int cfd, const char*dirName);

int hex2dec (char c);

char dec2hex (short int c);

void urlDecode(char* from, char *to);

源文件

#include "HttpServer.h"
#include <arpa/inet.h>
#include <stdio.h> 
#include <stdlib.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <sys/sendfile.h>
#include <dirent.h>
#include <ctype.h>

int initListenFd(unsigned short port) {
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == lfd) {
        perror("scoket");
        return -1;
    }
    int opt = -1;
    int ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (-1 == ret) {
        perror("setsockopt");
        return -1;
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    ret = bind(lfd, (const struct sockaddr *)&addr, sizeof(addr));
    if (-1 == ret) {
        perror("bind");
        return -1;
    }
    ret = listen(lfd, 128);
    if (-1 == ret) {
        perror("listen");
        return -1;
    }
    return lfd;
}

int epoolRun(int lfd) {
    int epfd = epoll_create(1);
    if (-1 == epfd) {
        perror("epoll_create");
        return -1;
    }
    struct epoll_event ev;
    ev.data.fd = lfd;
    ev.events = EPOLLIN;
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    if (-1 == ret) {
        perror("epoll_ctl");
        return -1;
    }
    struct epoll_event evs[2048];
    int maxwait = sizeof(evs) / sizeof(struct epoll_event);
    while (1) {
        int num = epoll_wait(epfd, evs, maxwait, -1);
        for (int i = 0; i <= num; i++) {
            int fd = evs[i].data.fd;
            pthread_t tid;
            struct ThreadParam *param = 
                (struct ThreadParam *)malloc(sizeof(struct ThreadParam));
            param->fd = fd;
            param->epfd = epfd;
            param->tid = tid;
            if (fd == lfd) {
                pthread_create(&tid, NULL, acceptClient, param);
                pthread_detach(tid);
            }
            else {
                pthread_create(&tid, NULL, recvHttpRequest, param);
                pthread_detach(tid);
            }
        }
    }
    
    return 0;
}

void* acceptClient(void *arg) {
    struct ThreadParam *param = (struct ThreadParam *)arg;
    if (!param) {
        return NULL;
    }
    int lfd = param->fd;
    int epfd = param->epfd;
    int cfd = accept(lfd, NULL, NULL);
    if (-1 == cfd) {
        perror("accept");
        return NULL;
    }
    int flag = fcntl(cfd, F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(cfd, F_SETFL, flag);
    struct epoll_event ev;
    ev.data.fd = cfd;
    ev.events = EPOLLIN | EPOLLET; //cfd边缘非阻塞模式,效率最高
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
    if (-1 == cfd) {
        perror("epoll_ctl");
    }
    free(param);
    param = NULL;
    printf("accept thread %ld\n", pthread_self());
    return NULL;
}

void* recvHttpRequest(void *arg) {
    struct ThreadParam *param = (struct ThreadParam *)arg;
    char buf[8192] = {0};
    int len = 0;
    int total = 0;
    char tmpBuf[1024] = {0};
    if (!param) {
        return NULL;
    }
    int cfd = param->fd;
    int epfd = param->epfd;
    while ((len = recv(cfd, tmpBuf, sizeof(tmpBuf), 0)) > 0) {
        if (total + len < sizeof(buf)) {
            memcpy(buf + total, tmpBuf, len);
        }
        total += len;
    }
    if (-1 == len && errno == EAGAIN && total > 0) { //接收数据完毕
        //解析http协议
        char *pt = strstr(buf, "\r\n");
        int reqLen = pt - buf;
        buf[reqLen] = '\0';
        parseRequestLine(cfd, buf);
    }
    else if (0 == len) { //客户端断开了连接
        epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
        close(cfd);
    }
    else {
        perror("recv");
    }
    free(param);
    param = NULL;
    printf("client thread %ld\n", pthread_self());
    return NULL;
}

int parseRequestLine(int cfd, const char *line) {
    char method[32] = {0};
    char path[2048] = {0};
    char decodePath[1024] = {0};
    char protocol[128] = {0};
    //sscanf解析格式化字符串
    sscanf(line, "%[^ ] %[^ ] %s", method, path, protocol);
    printf("method: %s, path: %s protocol: %s\n", method, path, protocol);
    if (0 != strcasecmp(method, "get")) {
        return -1;
    }
    urlDecode(path, decodePath);
    //http中/代表服务端工作的资源根目录
    char *file = NULL;
    if (0 == strcmp(decodePath, "/")) {
        file = ".";
    }
    else {
        file = decodePath + 1;
    }
    struct stat st;
    int ret = stat(file, &st);
    if (-1 == ret) {
        //回复404页面
        sendHeadMsg(cfd, 404, "Not Found", getFileType(".html"), -1); //-1表示不知道长度,让浏览器自己解析去
        sendFile(cfd, "404.html");
        return 0;
    }
    if (S_ISDIR(st.st_mode)) {
        sendHeadMsg(cfd, 200, "OK", getFileType(".html"), -1);
        sendDir(cfd, file);
    }
    else {
        sendHeadMsg(cfd, 200, "OK", getFileType(file), st.st_size);
        sendFile(cfd, file);
    }

    return 0;
}

int sendFile(int cfd, const char *fileName) {
    //读一部分数据,发送一部分数据,因为tcp是面向连接的流式的
    int fd = open(fileName, O_RDONLY);
    assert(fd > 0);
#if 0
    while (1) {
        char buf[1024];
        int len = read(fd, buf, sizeof(buf));
        if (len > 0) {
            send(cfd, buf, len, 0);
            usleep(20); //减轻接收端压力
        }
        else if (0 == len) {
            break;
        }
        else {
            perror("read");
        }
    }
#endif
#if 1
    off_t len = 0;
    int size = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);
    while (len < size) {
        int ret = sendfile(cfd, fd, &len, size - len);
        printf("ret value %d \n", ret);
        if (-1 == ret) {
            if (EAGAIN == errno) {
                printf("no data\n");
                perror("sendfile");
            }
            else {
                printf("client quit \n");
                break;
            }
        }
        
    }
#endif
    return 0;
}

int sendHeadMsg(int cfd, int status, const char *descr, const char *type, int length) {
    char buf[8192] = {0};
    int offset = 0;
    int ret = sprintf(buf + offset, "http/1.1 %d %s\r\n", status, descr);
    offset += ret;
    ret = sprintf(buf + offset, "content-type: %s\r\n", type);
    offset += ret;
    ret = sprintf(buf + offset, "content-length: %d\r\n\r\n", length);
    offset += ret;
    send(cfd, buf, offset, 0);
    return 0;
}

const char *getFileType(const char *name) {
    const char* dot = strrchr(name, '.');
    if (NULL == dot) {
        return "text/palin; charset=utf-8";
    }
    if (0 == strcasecmp(dot, ".html")) {
        return "text/html; charset=utf-8";
    }
    if (0 == strcasecmp(dot, ".png")) {
        return "image/png; charset=utf-8";
    }
    if (0 == strcasecmp(dot, ".txt")) {
        return "text/palin; charset=utf-8";
    }
    // ...
    return "application/octet-stream; charset=utf-8";
}

int sendDir(int cfd, const char*dirName) {
    char buf[2048] = {0};
    int len = 0;
    int ret = sprintf(buf + len, "<html><head><title>%s</title><body><table>", dirName);
    len += ret;
    struct dirent** namelist = NULL;
    int num = scandir(dirName, &namelist, NULL, alphasort);
    for (int i = 0; i < num; i++) {
        char * name = namelist[i]->d_name;
        struct stat st;
        char path[1024] = {0};
        sprintf(path, "%s/%s", dirName, name);
        stat(path, &st);
        if (S_ISDIR(st.st_mode)) {
            if (!strcmp(".", name) || !strcmp("..", name)) {
                continue;
            }
            ret = sprintf(buf + len, "<tr><td><a href=\"%s/\" style=\"font-size:20px\">%s</a>    </td><td>%ld</td></tr>", \
                        name, name, st.st_size);
        }
        else {
            ret = sprintf(buf + len, "<tr><td><a href=\"%s\" style=\"font-size:20px\">%s</a>    </td><td>%ld</td></tr>",
                        name, name, st.st_size);
        }
        len += ret;
        send(cfd, buf, len, 0);
        len = 0;
        memset(buf, 0x00, sizeof(buf));
        free(namelist[i]);
    }
    len = sprintf(buf, "</table></head></body></html>");
    send(cfd, buf, len, 0);
    free(namelist);
    return 0;
}

int hex2dec (char c) {
    if ('0' <= c && c <= '9') return c - '0';
    else if ('a' <= c && c <= 'f') return c - 'a' + 10;
    else if ('A' <= c && c <= 'F') return c - 'A' + 10;
    return 0;
}

char dec2hex (short int c) {
    if (0 <= c && c <= 9) return c + '0';
    else if (10 <= c && c <= 15) return c + 'A' - 10;
    return 0;
}

void urlDecode(char* org, char *obj) {
    if (!org || !obj) {
        return;
    }
    //isxdigit:判断字符是否是十六进制字符
    for (; *org != '\0'; ++org, ++obj) {
        if ('%' == org[0] && isxdigit(org[1]) && isxdigit(org[2])) {
            *obj = hex2dec(org[1]) * 16 + hex2dec(org[2]);
            org += 2;
        }
        else {
            *obj = *org;
        }
    }
}

主程序:

#include <stdio.h>
#include <unistd.h>
#include "HttpServer.h"

int main(int argc, char** argv) {
    unsigned short int port;
    if (argc < 3) {
        printf("program {port} {path}\n");
        return -1;
    }
    sscanf(argv[1], "%hu", &port);
    printf("begin start http server, listen %d ...\n", port);
    //修改进程的工作目录
    chdir(argv[2]);
    int lfd = initListenFd(port);
    epoolRun(lfd);

    return 0;
}

Makefile 编译脚本:

app: httpServer tcpServer tcpClient

#说明:$^代表依赖项
httpServer: CommonUtil.c HttpServer.c main.c
	gcc -g $^ -o httpServer -lpthread 

tcpServer: CommonUtil.c TcpServer.c
	gcc -g $^ -o tcpServer

tcpClient: CommonUtil.c TcpClient.c
	gcc -g $^ -o tcpClient

clean:
	-rm httpServer tcpServer tcpClient -f

1.2 效果

 

 

 

 

2.TCP服务器和客户端

2.1 源码

头文件:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int isLittleEndian();

int readn(int fd, char* buf, int len);

int writen(int fd, char *buf, int len);

源文件

 #include <strings.h>
#include <arpa/inet.h>
#include <stdio.h> 
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include "CommonUtil.h"

static const char* _s_showMsg [] = {
    "次北固山下",
    "【作者】王湾 【朝代】唐",
    "客路青山外,行舟绿水前。",
    "潮平两岸阔,风正一帆悬。",
    "海日生残夜,江春入旧年。",
    "乡书何处达?归雁洛阳边。"
};

static void sendMsg(int fd, const char *data, int len) {
    char *buf = (char *)malloc(sizeof(int) + len);
    int nlen = len;
    if (isLittleEndian()) {
        nlen = htonl(len);
    }
    memcpy(buf, &nlen, sizeof(int));
    memcpy(buf + sizeof(int), data, len);
    printf("发送数据长度:: [%d] 内容:: [%s]\n", len, data);
    writen(fd, buf, sizeof(int) + len);
    if (buf) {
        free(buf);
    }
}

static int startTcpServer(unsigned short port)
{
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == lfd) {
        perror("scoket");
        return -1;
    }
    int opt = -1;
    int ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (-1 == ret) {
        perror("setsockopt");
        return -1;
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    ret = bind(lfd, (const struct sockaddr *)&addr, sizeof(addr));
    if (-1 == ret) {
        perror("bind");
        return -1;
    }
    ret = listen(lfd, 128);
    if (-1 == ret) {
        perror("listen");
        return -1;
    }
    struct sockaddr_in client;
    socklen_t client_addrlen = sizeof(client);
     printf("port %d, wait client accept ...\n", port);
    int connfd = accept(lfd, (struct sockaddr*)(&client), &client_addrlen);
    if (connfd < 0) {
        perror("accept");
        return -1;
    }
    printf("connect client info: addr = %s, port = %d\n",
            inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    int lineNum = sizeof(_s_showMsg) / sizeof(_s_showMsg[0]);
    for (int i = 0; i < lineNum; i++) {
        sendMsg(connfd, _s_showMsg[i], strlen(_s_showMsg[i]));
        usleep(1000 * 100); //此处为了减轻客户端压力
    }
    printf("我活干完了,数据已经全部发送到客户端!\n");
    getchar();
    close(connfd);
    close(lfd);

    return 1;
}

int main(int argc, char**argv) {
    if (argc < 2) {
        printf("a.out {port}\n");
        return -1;
    }
    unsigned short port;
    sscanf(argv[1], "%hu", &port);
    startTcpServer(port);
    return 0;
}
#include <arpa/inet.h>
#include <stdio.h> 
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include "CommonUtil.h"

static void recvMsg(int fd, char **data, int *len) {
   int nlen = 0;
   int ret = readn(fd, (char*)&nlen, sizeof(int));
   *len = nlen;
   if (isLittleEndian()) {
        *len = ntohl(nlen);
   }
   char *tmp = (char *)malloc(*len + 1);
   readn(fd, tmp, *len);
   tmp[*len] = '\0';
   *data = tmp;
}

static int startTcpClient(unsigned short port)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    
	assert(-1 != sockfd);
	//指定服务器的ip和端口
	struct sockaddr_in saddr;  
    memset(&saddr, 0, sizeof(saddr)); 
    saddr.sin_family = AF_INET;   
    saddr.sin_port = htons(port);    
    //saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    inet_pton(sockfd, "127.0.0.1", &saddr.sin_addr.s_addr);
    //作为客户端不需要指定端口,系统自动给客户端设置端口,连接上后直接收发数据
    printf("begin connect port %d\n", port);
    int ret = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));   
    if (-1 == ret) {
        perror("connect");
        return 0;
    }
    while(1)    
    {               
    	char *buff = NULL;          
        int ret = 0;
        recvMsg(sockfd, &buff, &ret);
        if (-1 == ret) {
            perror("read");
            break;
        }
        else if (0 == ret) {
            perror("server quit ");
            break;
        }
        else {
            if (buff) {
                printf("接收数据长度:: [%d] 内容:: [%s]\n", ret, buff);
                free(buff);
            }
        }
        printf("\r\n------------------------------\n\r");
        sleep(rand() % 10);
     }
    close(sockfd);    
    exit(0); 
}

int main(int argc, char**argv) {
    if (argc < 2) {
        printf("a.out {port}\n");
        return -1;
    }
    unsigned short port;
    sscanf(argv[1], "%hu", &port);
    startTcpClient(port);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "CommonUtil.h"

int isLittleEndian() {
    static unsigned short data = 0x1234;
    if (*((char*)&data) == 0x34) {
        return 1;
    }
    else {
        return 0;
    }
}


int readn(int fd, char* buf, int len) {
    int nleft = len;
    int nread = 0;
    char *pbuf = buf;
    while (nleft > 0) {
        nread = read(fd, pbuf, nleft);
        if (-1 == nread) {
            perror("read");
            return -1;
        }
        else if (0 == nread) {
            return len - nleft;
        }
        pbuf += nread;
        nleft -= nread;
    }
    return len;
}

int writen(int fd, char *buf, int len) {
    int nleft = len;
    int nwrite = 0;
    char *pbuf = buf;
    while (nleft > 0) {
        nwrite = write(fd, pbuf, nleft);
        if (-1 == nwrite) {
            perror("write");
            return -1;
        }
        else if (0 == nwrite) {
            continue;
        }
        pbuf += nwrite;
        nleft -= nwrite;
    }
    return len;
}

编译脚本:

app: httpServer tcpServer tcpClient

#说明:$^代表依赖项
httpServer: CommonUtil.c HttpServer.c main.c
	gcc -g $^ -o httpServer -lpthread 

tcpServer: CommonUtil.c TcpServer.c
	gcc -g $^ -o tcpServer

tcpClient: CommonUtil.c TcpClient.c
	gcc -g $^ -o tcpClient

clean:
	-rm httpServer tcpServer tcpClient -f

2.1 效果

源码下载路径如下:
https://download.csdn.net/download/hsy12342611/87183336 

只有平时多接触底层编程才能体会到一些技术的本质,做技术不能被表面的虚幻所迷惑,要从本质上去理解一些东西,好的,今天就到这里了,该去休息了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot-自动配置

一、简介 在搭建springboot应用的时候&#xff0c;无需像之前spring的时候&#xff0c;要一堆繁琐的配置文件之类的。一个main的方法&#xff0c;就能把springboot的项目run起来。和其他框架整合也是非常的简单&#xff0c;只需要使用到Enablexxxxx注解就可以搞起来。 二、原理…

外卖项目09---Redis了解

目录 Redis了解 141 一、Redis入门 143 1.1Redis简介 143 1.2Redis下载与安装 143 1.3Redis入门---Redis服务启动 144 1.3.1Redis服务启动 1.3.2设置密码远程连接 二、Redis数据类型 145 三、Redis常用命令 146 3.1Redis常用命令---字符串string操纵命令 3.2Redis…

现代密码学导论-21-分组密码

目录 3.6.3分组密码及其操作模式 ECB(Electronic Code Book) 电码本模式 CBC(Cipher Block Mode) 密文分组链接方式 THEOREM 3.32 CBC模式的CPA安全 连锁CBC模式 OFB(Output Feedback Mode) 输出反馈模式 CTR(Counter) 计数器模式 THEOREM 3.33 CTR多明文PCA安全 THE…

C语言学习之路(基础篇)—— 复合类型(自定义类型)

说明&#xff1a;该篇博客是博主一字一码编写的&#xff0c;实属不易&#xff0c;请尊重原创&#xff0c;谢谢大家&#xff01; 结构体 1) 概述 数组&#xff1a;描述一组具有相同类型数据的有序集合&#xff0c;用于处理大量相同类型的数据运算。 结构体&#xff1a;将多个…

工具分享:Springboot+Netty+Xterm搭建一个网页版的SSH终端

一. 简述 搭建一个web ssh&#xff0c;主要是借助websocket和xterm&#xff0c;可以实现一个类似于xshell的效果&#xff0c;如图&#xff1a; 二. 技术栈 这里使用了springboot、netty、jsch、react、Ts,xterm。 这里我用了springboot和netty实现了websocket&#xff0c;js…

稳了,我用 Python 可视化分析和预测了 2022 年 FIFA 世界杯

许多人称足球为 “不可预测的游戏”&#xff0c;因为一场足球比赛有太多不同的因素可以改变最后的比分。 预测一场比赛的最终比分或赢家确实是很难的&#xff0c;但在预测一项比赛的赢家时就不是这样了。在过去的5年中&#xff0c;Bayern Munich 赢得了所有的德甲联赛&#xf…

如何发布一个属于自己的 npm 包

如何发布一个属于自己的 npm 包 start 在日常的工作中&#xff0c;我们会接触很多 npm 包。 例如&#xff1a; npm install jquerynpm install vue/clinpm install axios# ... 等等有时候会想到&#xff0c;构建一个属于自己的 npm 包&#xff0c;应该超级酷吧&#xff1f; …

zabbix基础环境部署

目录 一、环境准备 二、部署LNMP 1、安装Nginx及其依赖包 2、修改nginx配置 3、测试页面 三、部署zabbix服务端 1、下载zabbix 2、安装源码zabbix 3、为zabbix创建数据库与数据库账户 4、搭建Zabbix页面 4.1、第1步 Check of pre-requisites 4.2、第2步 Configure D…

Python代码的编写运行方式

Python代码的编写运行方式介绍 python官方运行环境可到网站https://www.python.org/downloads/找到合适版本下载安装。 安装比较容易&#xff0c;特别强调&#xff0c;安装过程建议勾选“Add Python to PATH”&#xff08;将Python添加到PATH环境变量&#xff09;【注1】&…

Docker安装Minio

寻找Minio镜像 Docker Hub 查找官方镜像 下载Minio镜像 下载最新版Minio镜像 docker pull minio/minio等同于 docker pull minio/minio:latest下载指定版本的Minio镜像 docker pull minio/minio:RELEASE.2022-11-26T22-43-32Z.fips检查当前所有Docker下载的镜像 docker …

第六章TCP/IP——网络传输硬件设备

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。个人爱好: 编程&#xff0c;打篮球&#xff0c;计算机知识个人名言&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石…

Linux进程通信之消息队列

目录 1.消息队列的原理&#xff1a; 2.消息队列的接口&#xff1a; &#xff08;1&#xff09;创建消息队列 &#xff08;2&#xff09;向消息队列发送消息 &#xff08;3&#xff09;接收消息 &#xff08;4&#xff09;操作消息队列的接口 1.消息队列的原理&#xff1a; 消…

docker 安装 Heimdall 导航页

前言 随着群晖中使用的功能越来越多&#xff0c;各种端口太多容易忘&#xff0c;所以就有了使用导航页的想法&#xff08;使用收藏夹的朋友除外&#xff09;。群晖也有自带的WEB套件管理&#xff0c;不过个人感觉相对麻烦&#xff0c;使用 Heimdall 导航页可以设置密码登录&am…

【数字信号调制】16QAM信号调制解调【含Matlab源码 2050期】

⛄一、QAM调制与解调&#xff08;附实验题目说明&#xff09; 1 引 言 数字振幅调制、数字频率调制和数字相位调制是数字调制的基础,然而,这3种数字调制方式都存在不足之处。如频谱利用率低、抗多径衰落能力差、功率谱衰减慢、带外辐射严重等。为了改善这些不足,几十年来人们不…

yocto machine class解析之st-partitions-image

yocto machine class解析之st-partitions-image stm32mp157 yocto的meta-st-stm32mp layer中提供了几个class,后续几篇文章重点分析这些class文件&#xff1a; 第一篇就从st-partitions-image.bbclass 开始&#xff0c;st所有创建image的bb文件都会引用st-partitions-image&am…

「UWB」精准定位黑科技,开启座舱雷达新蓝海

基于厘米级定位、超低功率、强抗干扰、超大容量等技术特点&#xff0c;UWB&#xff08;超宽带&#xff09;技术在消费电子、智能汽车等领域的应用前景被赋予厚望。 值得一提的是&#xff0c;利用UWB雷达还可实现舱内活体检测、脚踢尾箱等&#xff0c;这意味着新一轮座舱感知革…

【Vue】ref引用,插槽

一、ref 什么是ref&#xff1f; ref用来辅助开发者在不依赖于jQuery 的情况下&#xff0c;获取DOM元素或组件的引用。 每个vue的组件实例上&#xff0c;都包含一个$refs对象&#xff0c;里面存储着对应的DOM元素或组件的引用。默认情况下&#xff0c;组件的$refs 指向一个空对…

Ubuntu20.04下安装nvidia驱动

ubuntu-drivers devices会显示你的电脑上可用的nvidia驱动。只需要安装推荐的版本即可&#xff08;后面有recommend字样&#xff09; 打开电脑里的软件和更新app&#xff08;这里建议换提前换源&#xff0c;阿里源或者清华源&#xff09; 来到附加驱动这个页面&#xff0c;选…

HTML初识-概念和基本知识

1 . HTML初识-基础认知 HTML标签 1.1 目录 ◆ 基础认知 ◆ HTML标签学习 ◆ 综合案例 1.2 学习目标 ◆ 能够理解HTML的 基本语法 和标签的关系 ◆ 能够使用 排版标签 实现网页中标题、段落等效果 ◆ 能够使用 相对路径 选择不同目录下的文件 ◆ 能够使用 媒体标签 在网页中显示…

ES进阶教程

1.分片Shards 一个索引可以存储超出单个结点硬件限制的大量数据,es提供了将索引划分为多份的能力,每一份都称之为分片.当创建索引时,可以指定想要的分片数量.每个分片本身也是一个功能完善并且相对独立的索引.这个索引可以被放在集群中的任何结点上. 分片的重要性 1.允许水平切…