基于C++(QT框架)设计的网络摄像头项目(支持跨平台运行)

简介: 基于C++(QT框架)设计的网络摄像头项目(支持跨平台运行)

一、运行效果展示

1.1  windows系统运行效果展示

image.png

网络摄像头项目(Windows系统运行效果)

1.2  Android系统运行效果展示

image.png

网络摄像头项目(Android系统运行效果)

1.3  Linux系统运行效果展示

image.png

网络摄像头项目(Linux系统运行效果)


二、功能简介

2.1  功能介绍

这是基于C++(QT框架)设计的网络摄像头项目,本篇文章介绍的网络摄像头项目并不是采用RTMP或者RTSP推流编码的网络摄像头产品,而是采用HTTP协议推送图片流的方式,采用浏览器访问查看摄像头画面。


项目源码下载地址: https://download.csdn.net/download/xiaolong1126626497/21232910


这是软件运行截图:

image.png

这是浏览器访问截图:

image.png

image.png

image.png

项目运行的效果:


软件打开之后,先点击刷新信息,程序会扫描当前设备的摄像头和IP地址信息;并将访问的地址打印了出来,然后点击开启摄像头,界面就实时显示选择的摄像头画面。 在局域网内,其他设备打开浏览器,输入下面提示的地址访问,输入用户名和密码,即可查看到摄像头画面。程序里处理浏览器的请求是采用多线程方式,可以支持多个浏览器同时访问。


代码思路


代码采用的是C++(QT框架)编写,代码本身主要是分为两个部分:


1.   绑定指定端口号,创建TCP服务器,用来响应客户端的请求(浏览器)


2.  摄像头画面采集部分,摄像头数据采集采用单独的线程,采集之后将图像传递给界面刷新显示,并将图像填充到全局缓冲区,方便客户端处理线程将图像再传递给浏览器。


每当有新的浏览器访问进来,就会单独开辟一个线程,去处理这个浏览器接下来的通信交互,浏览器断开连接之后,线程自动销毁;图像缓冲区是一个公共的缓冲区,摄像头图像采集线程向该缓冲区填充数据,与浏览器通信的线程就从这个缓冲区读取数据,采用的是条件变量+互斥锁同步访问。


项目里用到的知识点主要是摄像头采集,线程处理、网络编程,HTTP协议等知识点。


如果是搞QT开发,都可以当做入门学习参考;如果想要用其他语言实现,思路搞清楚也很容易。 标准C语言。


在Linux下如果不需要界面,可以直接使用C语言完成项目效果,摄像头采集采用Linux下标准V4L2框架,线程就采用pthread_create创建,互斥锁、条件变量这些Linux都有,只要把程序思路搞清楚,实现起来还是很容易。


2.2 跨平台运行

代码是采用QT框架编写,所以支持跨平台编译运行;目前代码在Android、Linux、windows系统上都编译运行通过,达到相同效果;由于身边没有苹果设备,暂时未做测试。


不同系统间代码上还是有少许区别,代码里通过宏的方式区分了,不同的系统运行不同的部分代码。


如果要编译QT的代码在Android设备上运行,得先搭建对应的开发环境;如果不会环境搭建,可以参考下面链接。


(1). Linux(ubuntu)下搭建QT Android开发环境


https://blog.csdn.net/xiaolong1126626497/article/details/117256660


(2). windows(win10 64bit)下搭建QT Android开发环境


https://blog.csdn.net/xiaolong1126626497/article/details/117254453


(3).  windows下Qt + VS2017 环境安装


https://blog.csdn.net/xiaolong1126626497/article/details/112402861


2.3  项目源码运行环境

QT版本: 5.12.6


编译器: MinGW 32


系统:  Win10  Ubuntu18.04


2.4 代码运行介绍

程序运行时,需要用到一些资源文件,这些资源文件在程序的源码目录下,名称是:  www。

image.png

如果是windows、Linux系统环境,需要把资源目录拷贝到程序运行的同级目录下。


如果是Android系统,这些资源文件需要在编译的时候打包进APK里,在工程目录下的Android目录里创建一个assets目录,将资源文件全部拷贝到assets目录下;程序编译的时候会自动将assets目录打包进apk,到时候Android程序运行时就可以直接去assets目录下获取资源文件。

image.png

当apk生成之后,可以解压出来看看,assets目录打包成功没有。

image.png

image.png

检查了下,打包成功的,这样Android程序运行时,访问的路径就没有问题。


2.5 Linux系统运行摄像头无法读取数据问题说明

由于我测试的环境是在虚拟机里运行ubuntu系统,要在ubuntu里访问摄像头,需要先把摄像头挂载到虚拟机,才能访问。如果打开虚拟机发现右下角托盘里摄像头图标都没有,说明托盘程序可能被360、电脑管家之类的安全软件禁止了,需要开启自动启动,重启VM虚拟机软件再测试。

image.png

直接挂载进来可能遇到摄像头打开了,但是图像无法获取的情况,这个问题可能是USB协议版本引起的。

可以在这里切换USB协议测试,我的笔记本电脑是USB3.0才可以正常使用。

image.png

三、代码分析

image.png

3.1  初始化代码(构造函数)

代码开发时,主要是针对在windows平台运行的,所有程序里很多都是偏向于windows环境的设计。


构造函数里去除了系统原窗口标题栏,自定义了自己的标题;QT隐藏标题栏之后,是不能拖动拉伸的,需要自己实现,我这里采用的是GitHUB上开源的一个示例代码实现的这部分功能,效果不错,达到了想要的效果。剩下代码里初始化了托盘系统,方便程序最小化时,隐藏在windows系统的托盘图标栏里,其他的代码就是做写基本的初始化,信号槽的连接,IP地址、摄像头信息刷新等。

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //自定义标题栏
    FramelessHelper *pHelper = new FramelessHelper(this);
    setWindowFlags(Qt::FramelessWindowHint);
    pHelper->activateOn(this);  //激活当前窗体
    //pHelper->setTitleHeight(m_pTitleBar->height());  //设置窗体的标题栏高度
    pHelper->setWidgetMovable(true);  //设置窗体可移动
    pHelper->setWidgetResizable(true);  //设置窗体可缩放
   // pHelper->setRubberBandOnMove(true);  //设置橡皮筋效果-可移动
   // pHelper->setRubberBandOnResize(true);  //设置橡皮筋效果-可缩放
    /*基本设置*/
    this->setWindowIcon(QIcon(":/log.ico")); //设置图标
    this->setWindowTitle("网络摄像头");
    //打开的窗口在屏幕中间
    QDesktopWidget *widget= QApplication::desktop();
    move((widget->width()-this->width())/2,(widget->height()-this->height())/2);
    //刷新摄像头与IP地址信息
    on_pushButton_config_clicked();
    //刷新在线人数
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(timer_update()));
    timer->start(1000);
    tray= new QSystemTrayIcon(this);//初始化托盘对象tray
    connect(tray,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this,SLOT(show_Widget(QSystemTrayIcon::ActivationReason)));
    tray->setIcon(QIcon(QPixmap(":/log.png")));//设定托盘图标,引号内是自定义的png图片路径
    tray->setToolTip("网络摄像头"); //提示文字
    restoreAction = new QAction("打开", this);
    connect(restoreAction, SIGNAL(triggered()), this, SLOT(show()));
    quitAction = new QAction("退出", this);
    connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
    trayMenu = new QMenu(this);
    trayMenu->addAction(restoreAction);
    trayMenu->addSeparator();
    trayMenu->addAction(quitAction);
    tray->setContextMenu(trayMenu);
    work_class=new VideoReadThread_0;
    work_thread=new QThread;
    //启动摄像头的信号
    connect(this,SIGNAL(StartWorkThread()),work_class,SLOT(run()));
    //释放资源-释放摄像头
    connect(this,SIGNAL(Stop_VideoAudioEncode_0()),work_class,SLOT(stop()));
    //连接摄像头采集信号,在主线程实时显示视频画面
    connect(work_class,SIGNAL(VideoDataOutput(QImage)),this,SLOT(VideoDataDisplay_0(QImage)));
    //将类移动到子线程工作
    work_class->moveToThread(work_thread);
}

3.2 摄像头采集部分

摄像头采集采用的是QCamera + QVideoProbe实现。


初始化代码示例: 初始化代码里完成摄像头的一些参数,捕获模式,槽函数关联等设置。


初始化代码默认设置输出的图像格式是YUYV,在windows和Linux系统上是支持的,这个可能与摄像头有关,实际需要测试调整;Android系统上只支持NV21格式,如果是Android系统上运行,要记得修改格式。

void VideoReadThread_0::Camear_Init()
{
    /*创建摄像头对象,根据选择的摄像头打开*/
    camera = new QCamera(videoaudioencode_0.camera);
    m_pProbe = new QVideoProbe;
    if(m_pProbe != nullptr)
    {
        m_pProbe->setSource(camera); // Returns true, hopefully.
        connect(m_pProbe, SIGNAL(videoFrameProbed(QVideoFrame)),this, SLOT(slotOnProbeFrame(QVideoFrame)), Qt::QueuedConnection);
    }
    else
    {
           qDebug()<<"m_pProbe == nullptr";
    }
    /*配置摄像头捕    QCamera *camera;
    QVideoProbe *m_pProbe;获模式为帧捕获模式*/
     camera->setCaptureMode(QCamera::CaptureVideo);  //如果在Linux系统下运行就这样设置
     //camera->setCaptureMode(QCamera::CaptureVideo);//如果在android系统下运行就这样设置
    /*设置摄像头的采集帧率和分辨率*/
    QCameraViewfinderSettings settings;
    settings.setPixelFormat(QVideoFrame::Format_YUYV); //设置像素格式  Android上只支持NV21格式
    settings.setResolution(QSize(VIDEO_WIDTH,VIDEO_HEIGHT)); //设置摄像头的分辨率
    camera->setViewfinderSettings(settings);
    /*启动摄像头*/
    camera->start();
}

捕获到摄像头的图像数据之后,QVideoProbe会发出videoFrameProbed信号,在关联的槽函数里完成数据处理。

处理代码如下:

void VideoReadThread_0::slotOnProbeFrame(const QVideoFrame &frame)
{
//    qDebug()<<"开始采集.";
   QVideoFrame cloneFrame(frame);
   cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
//    qDebug()<<"height:"<<cloneFrame.height();
//    qDebug()<<"width:"<<cloneFrame.width();
//    qDebug()<<"bytesPerLine:"<<cloneFrame.bytesPerLine();
//    qDebug()<<"mappedBytes:"<<cloneFrame.mappedBytes();
//    qDebug()<<"pixelFormat:"<<cloneFrame.pixelFormat();
   unsigned char rgb_buffer[VIDEO_WIDTH*VIDEO_HEIGHT*3];
   if(cloneFrame.pixelFormat()==QVideoFrame::Format_YUYV)
   {
       yuyv_to_rgb(cloneFrame.bits(),rgb_buffer,cloneFrame.width(),cloneFrame.height());
   }
   else if(cloneFrame.pixelFormat()==QVideoFrame::Format_NV21)
   {
        NV21_TO_RGB24(cloneFrame.bits(),rgb_buffer,cloneFrame.width(),cloneFrame.height());
   }
   else
   {
       qDebug()<<"当前格式编码为%1,暂时不支持转换.\n";
       return;
   }
    cloneFrame.unmap();
    //加载图片数据
    QImage image(rgb_buffer,
                       cloneFrame.width(),
                       cloneFrame.height(),
                       QImage::Format_RGB888);
    //绘制图片水印
    QDateTime dateTime(QDateTime::currentDateTime());
    //时间效果: 2020-03-05 16:25::04 周一
    QString qStr="";
    qStr+=dateTime.toString("yyyy-MM-dd hh:mm:ss ddd");
    QPainter pp(&image);
    QPen pen = QPen(Qt::white);
    pp.setPen(pen);
    pp.setFont(QFont("宋体",20));
    pp.drawText(QPointF(0,40),qStr);
    mutex.lock();
    QBuffer buff;
    image.save(&buff,"jpg",80); // 30表示压宿率,值从0 – 100, 值越小表示编码出来的图像文件就越小,当然也就越不清晰
    lcd_image_data=buff.data();
    cond_wait.wakeAll();
    mutex.unlock();
    emit VideoDataOutput(image); //发送信号
}

代码里区分了数据格式,如果是YUYV就调用对应的转换函数将数据转为RGB格式,如果是NV21也是一样,调用对应的转换函数将数据转为RGB格式后续再做其他处理。

图像格式转换的代码如下:

/*
函数功能: 将YUV数据转为RGB格式
函数参数:
unsigned char *yuv_buffer: YUV源数据
unsigned char *rgb_buffer: 转换之后的RGB数据
int iWidth,int iHeight   : 图像的宽度和高度
*/
void yuyv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
{
    int x;
    int z=0;
    unsigned char *ptr = rgb_buffer;
    unsigned char *yuyv= yuv_buffer;
    for (x = 0; x < iWidth*iHeight; x++)
    {
        int r, g, b;
        int y, u, v;
        if (!z)
        y = yuyv[0] << 8;
        else
        y = yuyv[2] << 8;
        u = yuyv[1] - 128;
        v = yuyv[3] - 128;
        r = (y + (359 * v)) >> 8;
        g = (y - (88 * u) - (183 * v)) >> 8;
        b = (y + (454 * u)) >> 8;
        *(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
        *(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
        *(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
        if(z++)
        {
            z = 0;
            yuyv += 4;
        }
    }
}
void NV21_TO_RGB24(unsigned char *yuyv, unsigned char *rgb, int width, int height)
{
    const int nv_start = width * height ;
    int  index = 0, rgb_index = 0;
    uint8_t y, u, v;
    int r, g, b, nv_index = 0,i, j;
    for(i = 0; i < height; i++){
        for(j = 0; j < width; j ++){
            //nv_index = (rgb_index / 2 - width / 2 * ((i + 1) / 2)) * 2;
            nv_index = i / 2  * width + j - j % 2;
            y = yuyv[rgb_index];
            u = yuyv[nv_start + nv_index ];
            v = yuyv[nv_start + nv_index + 1];
            r = y + (140 * (v-128))/100;  //r
            g = y - (34 * (u-128))/100 - (71 * (v-128))/100; //g
            b = y + (177 * (u-128))/100; //b
            if(r > 255)   r = 255;
            if(g > 255)   g = 255;
            if(b > 255)   b = 255;
            if(r < 0)     r = 0;
            if(g < 0)     g = 0;
            if(b < 0)     b = 0;
            index = rgb_index % width + (height - i - 1) * width;
            //rgb[index * 3+0] = b;
            //rgb[index * 3+1] = g;
            //rgb[index * 3+2] = r;
            //颠倒图像
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 1] = b;
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 2] = g;
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 3] = r;
            //正面图像
            rgb[i * width * 3 + 3 * j + 0] = b;
            rgb[i * width * 3 + 3 * j + 1] = g;
            rgb[i * width * 3 + 3 * j + 2] = r;
            rgb_index++;
        }
    }
}

图像格式转换完成之后,下面就加载到QImage里,绘制上时间水印,将数据填充到缓冲区,然后再唤醒等待的线程,最后在传递一份数据给UI界面完成渲染显示。


如果使用过程中,摄像头支持的是其他格式,那么这里需要自己增加对应的转换函数。


3.3  浏览器交互线程

这里是TCP客户端处理数据的线程,每当连接一个新的客户端,就会开辟一个新的线程独立运行。


线程里完成浏览器请求的处理,回应,交互。


完整的响应代码如下:

void TcpServerThread::run()
{
    qDebug()<<"TcpServerThread_子线程run槽函数的ID:"<<QThread::currentThreadId();
   QTcpSocket *tcpSocket;
    tcpSocket=new QTcpSocket;
    /*使用socketDescriptor套接字初始化QAbstractSocket*/
    if(!tcpSocket->setSocketDescriptor(socketDescriptor))
    {
        return;
    }
    //读取接收的数据
    QString text;
    if(tcpSocket->waitForReadyRead())
    {
        text=tcpSocket->readAll();
    }
    //处理浏览器的请求
    if(text.contains("GET / HTTP/1.1", Qt::CaseInsensitive))
    {
        //如果是Android系统
#ifdef Q_OS_ANDROID
        text="assets:/login.html";
#else
         text=QCoreApplication::applicationDirPath();
         text+="/www/login.html";
#endif
        SendFileData(buff,text,"text/html",tcpSocket);
    }
    else if(text.contains("GET /image.jpg HTTP/1.1", Qt::CaseInsensitive))
    {
        SendImageData(buff,tcpSocket);
    }
    else if(text.contains("GET /favicon.ico HTTP/1.1", Qt::CaseInsensitive))
    {
#ifdef Q_OS_ANDROID
        text="assets:/logo.ico";
#else
        text=QCoreApplication::applicationDirPath();
        text+="/www/logo.ico";
#endif
        SendFileData(buff,text,"image/x-icon",tcpSocket);
    }
    else if(text.contains("GET /three.min.js", Qt::CaseInsensitive))
    {
#ifdef Q_OS_ANDROID
        text="assets:/three.min.js";
#else
        text=QCoreApplication::applicationDirPath();
        text+="/www/three.min.js";
#endif
        SendFileData(buff,text,"application/x-javascript",tcpSocket);
    }
    else if(text.contains("GET /style.css", Qt::CaseInsensitive))
    {
#ifdef Q_OS_ANDROID
        text="assets:/style.css";
#else
        text=QCoreApplication::applicationDirPath();
        text+="/www/style.css";
#endif
        SendFileData(buff,text,"text/css",tcpSocket);
    }
    else if(text.contains("GET /Stats.min.js", Qt::CaseInsensitive))
    {
#ifdef Q_OS_ANDROID
        text="assets:/Stats.min.js";
#else
        text=QCoreApplication::applicationDirPath();
        text+="/www/Stats.min.js";
#endif
        SendFileData(buff,text,"application/x-javascript",tcpSocket);
    }
    else if(text.contains("GET /logo.png HTTP/1.1", Qt::CaseInsensitive))
    {
#ifdef Q_OS_ANDROID
        text="assets:/logo.png";
#else
        text=QCoreApplication::applicationDirPath();
        text+="/www/logo.png";
#endif
        SendFileData(buff,text,"image/png",tcpSocket);
    }
    else if(text.contains("userName=admin&passWord=12345678", Qt::CaseInsensitive))
    {   
#ifdef Q_OS_ANDROID
        text="assets:/index.html";
#else
        text=QCoreApplication::applicationDirPath();
        text+="/www/index.html";
#endif
        SendFileData(buff,text,"text/html",tcpSocket);
    }
    //卸载连接的套接字
    fd_list.Del_fd(socketDescriptor);
    //关闭连接
    tcpSocket->close();
    delete  tcpSocket;
}
/**
 * 向浏览器响应请求数据
 */
int TcpServerThread::SendFileData(char *buff, QString file_path, const char *type, QTcpSocket *tcpSocket)
{
    qint64 size=0;
    /*1. 读取文件*/
    QFile file(file_path);
    if(file.open(QIODevice::ReadOnly)!=true)
    {
        qDebug()<<"文件不存在:"<<file_path<<endl;
        return -1;
    }
    size=file.size(); //得到文件大小
    QByteArray byte=file.readAll(); //读取所有数据
    file.close();//关闭文件
    /*2. 构建响应格式字符串*/
    sprintf(buff,"HTTP/1.1 200 OK\r\n"
                 "Content-type:%s\r\n"
                 "Content-Length:%lld\r\n"
                 "\r\n",type,size);
    /*3. 向客户端发送响应请求*/
    if(tcpSocket->write(buff,strlen(buff))<=0)return -2;
    tcpSocket->waitForBytesWritten();  //等待写
    /*4. 向客户端发送响应实体数据*/
    if(tcpSocket->write(byte)<=0)return -3;
    tcpSocket->waitForBytesWritten();  //等待写
    return 0;
}
/*
向客户端循环发送图片数据流
*/
int TcpServerThread::SendImageData(char *buff, QTcpSocket *tcpSocket)
{
    /*1. 构建响应格式字符串*/
    sprintf(buff,"HTTP/1.0 200 OK\r\n"
                "Server: wbyq\r\n"
                "Content-Type:multipart/x-mixed-replace;boundary=boundarydonotcross\r\n"
                "\r\n"
                "--boundarydonotcross\r\n");
    /*2. 向客户端发送响应请求*/
    if(tcpSocket->write(buff,strlen(buff))<=0)return -2;
    tcpSocket->waitForBytesWritten();  //等待写
    /*3. 循环发送数据*/
    while(thread_run_flag)
    {
        //从全局缓冲区取数据
        mutex.lock();
        cond_wait.wait(&mutex);
        QByteArray image_data; //保存一帧图像数据
        image_data=lcd_image_data;
        mutex.unlock();
//        QBuffer data_buff;
//        QPixmap pixmap;
//        QScreen *screen = QGuiApplication::primaryScreen();
//        pixmap=screen->grabWindow(0); //获取当前屏幕的图像
//        //缩放图像
//        pixmap = pixmap.scaled(lcd_image_w,lcd_image_h, Qt::KeepAspectRatio);
//        pixmap.save(&data_buff,"jpg",image_val);
//        QByteArray image_data1=data_buff.data();
        /*4. 向浏览器发送响应头*/
        sprintf(buff,"Content-type:image/jpeg\r\n"
                     "Content-Length:%d\r\n"
                     "\r\n",image_data.size());
        if(tcpSocket->write(buff,strlen(buff))<=0)break;
        tcpSocket->waitForBytesWritten();  //等待写
        /*5. 发送实体数据*/
         if(tcpSocket->write(image_data)<=0)break;
         tcpSocket->waitForBytesWritten();  //等待写
        /*6. 发送边界符*/
        sprintf(buff,"\r\n--boundarydonotcross\r\n");
        if(tcpSocket->write(buff,strlen(buff))<=0)break;
        tcpSocket->waitForBytesWritten();  //等待写
        //等待一段时间
        msleep(5);
    }
    return 0;
}

当密码、账号验证通过之后,服务器就与浏览器建立长连接,循环发送图片流数据,浏览器完成连续刷新显示。 里面实现的思路,就是基本的TCP网络编程代码。


四、  HTTP协议简单介绍

这个网络摄像头项目主要是与浏览器交互的,要完成浏览器的交互,首先得知道HTTP协议的报文格式,如何响应,下面只是介绍当前项目里用到的部分知识,方便理解第三章的浏览器交互代码。


4.1  HTTP协议介绍

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,服务器传输超文本到本地浏览器的传送协议。

HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。


HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。


HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。

image.png

4.2 请求方法与报文格式

客户端请求消息

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。

image.png

服务器响应消息

HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

image.png

HTTP协议常用的2种请求方法: GET, POST 方法。

image.png

4.3 HTTP响应头信息

HTTP请求头提供了关于请求,响应或者其他的发送实体的信息。

image.png

4.4 HTTP状态码

当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。


当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。


HTTP状态码的英文为HTTP Status Code。


下面是常见的HTTP状态码:

200 - 请求成功

301 - 资源(网页等)被永久转移到其它URL

404 - 请求的资源(网页等)不存在

500 - 内部服务器错误


4.5 HTTP content-type

Content-Type,内容类型,一般是指网页中存在的Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些Asp网页点击的结果却是下载到的一个文件或一张图片的原因。


HTTP content-type 对照表:

image.png

image.png

image.png

image.png

image.png

image.png



目录
相关文章
|
1月前
|
安全 虚拟化
在数字化时代,网络项目的重要性日益凸显。本文从前期准备、方案内容和注意事项三个方面,详细解析了如何撰写一个优质高效的网络项目实施方案,帮助企业和用户实现更好的体验和竞争力
在数字化时代,网络项目的重要性日益凸显。本文从前期准备、方案内容和注意事项三个方面,详细解析了如何撰写一个优质高效的网络项目实施方案,帮助企业和用户实现更好的体验和竞争力。通过具体案例,展示了方案的制定和实施过程,强调了目标明确、技术先进、计划周密、风险可控和预算合理的重要性。
41 5
|
2月前
|
监控 安全
从 Racket 语言出发,创新员工网络监控软件的框架
在数字化企业环境中,员工网络监控软件对于保障信息安全和提升效率至关重要。Racket 语言凭借其独特特性和强大功能,为开发创新的监控软件提供了新可能。通过捕获和分析网络数据包、记录员工网络活动日志,甚至构建复杂的监控框架,Racket 能够满足企业的定制化需求,为企业信息安全和管理提供强有力支持。未来,基于 Racket 的创新解决方案将不断涌现。
45 6
|
1月前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
97 6
|
2月前
|
机器学习/深度学习 人工智能
类人神经网络再进一步!DeepMind最新50页论文提出AligNet框架:用层次化视觉概念对齐人类
【10月更文挑战第18天】这篇论文提出了一种名为AligNet的框架,旨在通过将人类知识注入神经网络来解决其与人类认知的不匹配问题。AligNet通过训练教师模型模仿人类判断,并将人类化的结构和知识转移至预训练的视觉模型中,从而提高模型在多种任务上的泛化能力和稳健性。实验结果表明,人类对齐的模型在相似性任务和出分布情况下表现更佳。
68 3
|
2月前
|
安全 网络安全 区块链
网络安全与信息安全:构建数字世界的防线在当今数字化时代,网络安全已成为维护个人隐私、企业机密和国家安全的重要屏障。随着网络攻击手段的不断升级,从社交工程到先进的持续性威胁(APT),我们必须采取更加严密的防护措施。本文将深入探讨网络安全漏洞的形成原因、加密技术的应用以及提高公众安全意识的重要性,旨在为读者提供一个全面的网络安全知识框架。
在这个数字信息日益膨胀的时代,网络安全问题成为了每一个网民不可忽视的重大议题。从个人信息泄露到企业数据被盗,再到国家安全受到威胁,网络安全漏洞如同隐藏在暗处的“黑洞”,时刻准备吞噬掉我们的信息安全。而加密技术作为守护网络安全的重要工具之一,其重要性不言而喻。同时,提高公众的安全意识,也是防范网络风险的关键所在。本文将从网络安全漏洞的定义及成因出发,解析当前主流的加密技术,并强调提升安全意识的必要性,为读者提供一份详尽的网络安全指南。
|
2天前
|
机器学习/深度学习 算法 PyTorch
基于图神经网络的大语言模型检索增强生成框架研究:面向知识图谱推理的优化与扩展
本文探讨了图神经网络(GNN)与大型语言模型(LLM)结合在知识图谱问答中的应用。研究首先基于G-Retriever构建了探索性模型,然后深入分析了GNN-RAG架构,通过敏感性研究和架构改进,显著提升了模型的推理能力和答案质量。实验结果表明,改进后的模型在多个评估指标上取得了显著提升,特别是在精确率和召回率方面。最后,文章提出了反思机制和教师网络的概念,进一步增强了模型的推理能力。
16 4
基于图神经网络的大语言模型检索增强生成框架研究:面向知识图谱推理的优化与扩展
|
20天前
|
人工智能 自然语言处理
WebDreamer:基于大语言模型模拟网页交互增强网络规划能力的框架
WebDreamer是一个基于大型语言模型(LLMs)的网络智能体框架,通过模拟网页交互来增强网络规划能力。它利用GPT-4o作为世界模型,预测用户行为及其结果,优化决策过程,提高性能和安全性。WebDreamer的核心在于“做梦”概念,即在实际采取行动前,用LLM预测每个可能步骤的结果,并选择最有可能实现目标的行动。
49 1
WebDreamer:基于大语言模型模拟网页交互增强网络规划能力的框架
|
26天前
|
JSON 数据处理 Swift
Swift 中的网络编程,主要介绍了 URLSession 和 Alamofire 两大框架的特点、用法及实际应用
本文深入探讨了 Swift 中的网络编程,主要介绍了 URLSession 和 Alamofire 两大框架的特点、用法及实际应用。URLSession 由苹果提供,支持底层网络控制;Alamofire 则是在 URLSession 基础上增加了更简洁的接口和功能扩展。文章通过具体案例对比了两者的使用方法,帮助开发者根据需求选择合适的网络编程工具。
27 3
WK
|
1月前
|
开发框架 开发工具 C++
C++跨平台框架Qt
Qt是一个功能强大的C++跨平台应用程序开发框架,支持Windows、macOS、Linux、Android和iOS等操作系统。它提供了250多个C++类,涵盖GUI设计、数据库操作、网络编程等功能。Qt的核心特点是跨平台性、丰富的类库、信号与槽机制,以及良好的文档和社区支持。Qt Creator是其官方IDE,提供了一整套开发工具,方便创建、编译、调试和运行应用程序。Qt适用于桌面、嵌入式和移动应用开发。
WK
66 5
|
1月前
|
存储 安全 网络安全
网络安全法律框架:全球视角下的合规性分析
网络安全法律框架:全球视角下的合规性分析
44 1