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

Myplater项目

一、项目要求

.基于Mplayer的视频播放器
1.需求分析:
    1.该程序能够加载指定路径下所有的音视频文件
    (.mp3 .mp4 .avi .rm .rmvb .flv .wma)
    2.能够通过界面操作视频播放器
        支持:
            上键
            下键
            回车
            ESC
        支持:
            焦点定位
            
        1.一级页面功能如下:
        +-------------------+
        |     视频播放器    |
        |-------------------|
        |1.查看播放列表        |
        |2.开始/暂停        |
        |3.停止             |
        |4.上一个           |
        |5.下一个           |
        |6.快进             |
        |7.定位             |
        |8.播放方式            |
        |9.退出             |
        +-------------------+

        查看播放列表二级页面如下:
        +-------------------+
        |     视频播放器    |
        |-------------------|
        |1.张三的歌.mp4     |
        |2.李四的曲.avi     |
        |...                |
        +-------------------+

        定位的二级页面
        +-------------------+
        |     视频播放器    |
        +-------------------+
        定位:XX:XX:XX
    
    3.在一级页面启动播放列表功能,进入二级页面,
      焦点定位到对应歌曲按下enter播放歌曲,按下
      ESC按键能够返回一级页面
    
    4.在一级页面启动下一个功能,能够播放下一个音
      视频文件,如果到达最末尾则提示:最后一首歌曲
      
    5.在一级页面启动上一个功能,能够播放上一个音
      视频文件,如果到达最前面则提示:第一首歌曲

    6.在一级页面启动快进功能,能够按倍速播放,
      第一次选择启动2倍速播放
      第二次选择启动4倍速播放
      第三次选择返回1倍速播放(默认)
      周而复始
      
    7.在一级页面启动开始功能,
      如果音视频正在播放,则暂停播放
      如果音视频正在暂停,则继续播放
      如果音视频文件没有播放,则按照
      播放方式(1.顺序循环 2.单曲循环 3.随机播放)
      播放歌曲
    
    8.在一级页面启动停止功能:
      如果音视频文件没有播放,则提示:未播放音视频文件
      如果音视频正在播放,则停止当前播放内容
    
    9.在一级页面启动定位功能:
      如果定位在音视频文件播放时间内,则跳到对应位置播放
      如果定位位置超过文件播放时间,则提示:定位错误
    
    10.播放方式
      在一级页面启动播放方式功能:
      第一次选择启动单曲循环
      第二次选择启动随机播放
      第三次选择返回顺序循环(默认)

    11.在一级页面启动退出功能:
        则程序退出

二、思考内容:


    1.如何使用颜色打印?


        printf颜色打印
        

printf("\033[0m \033[属性代码1;属性代码2;属性代码3...m字符串,\033[0m");
printf("\033[0m \033[格式命令如:闪烁;背景颜色;字体颜色m我是中国人 \033[0m\n");
printf("\033[0m \033[5;46;31m我是中国人!  \033[0m\n");
//每一步骤的意义

  printf("\033[0m");//清空设置
  printf("\033[5;35;43m");//填写颜色设置
 
  printf("我是中国人!\n");//要打印的内容
 
  printf("\033[0m");//清空设置

在使用完属性代码进行输出后,一定要用\033[0m重置一下,否则会影响到后续的输出。

        需要注意的是用vscode进行闪烁属性的时候,无法正常闪烁,用虚拟机里面的终端就可以正常使用。


    2.如何操作焦点位置?

刚开始打印的位置就是start 到end之间的内容

然后focus进行向下移动,移动到end的位置

当走到end的之后  start ,end 和focus一起向下移动。

功能示例代码如下:


void MenuChoose(void)
{
	char ch = 0;
	int medialen = 20;

	ch = getchar();
	if ('w' == ch)
	{
		if (focus > pos_start)
		{
			focus--;
		}
		else if (focus == pos_start && pos_start == 0)
		{
			return;
		}
		else if (focus == pos_start)
		{
			pos_start--;
			pos_end--;
			focus--;
		}
	}
	else if ('s' == ch)
	{
		if (focus < pos_end)
		{
			focus++;
		}
		else if (focus == pos_end && pos_end == medialen)
		{
			return;
		}
		else if (focus == pos_end)
		{
			pos_start++;
			pos_end++;
			focus++;
		}
	}

	return;
}


 3.如何从终端接收上键?下键?回车?ESC按键?

怎么接收上 下 键?上下不是一个字符。

    

ESC是 ^[   看作是 : ESC + \n  对应的ascii码是27  和   10;

DOWN是^[ [B  看作是:ESC + [ + B  对应的ascii码是27   91   66

UP是^[ [B  看作是:ESC + [ + A  对应的ascii码是27   91   65

判断按键代码示例如下:

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char str[32] = {0};

	while (1)
	{
		memset(str, 0, sizeof(str));
		fgets(str, sizeof(str), stdin);
	
		if (27 == str[0] && 10 == str[1])
		{
			printf("ESC键!\n");
			return;
		}
		else if (10 == str[0])
		{
			printf("回车键!\n");
		}
		else if (27 == str[0] && 91 == str[1])
		{
			if (65 == str[2])
			{
				printf("UP键!\n");
			}
			else if (66 == str[2])
			{
				printf("DOWN键!\n");
			}
		}
	}

	return 0;
}


    
    4.如何加载音视频文件?


          调用mplyer通过音视频文件所在的路径来加载文件。

 
    5.如何打印界面?

#include <stdio.h>

typedef struct media
{
	int id;
	char medianame[512];
}MEDIA_CFG_S;

typedef struct menuinfo
{
	int id;
	int pos_start;
	int pos_end;
	int focus;
}MENU_CFG_S;

MEDIA_CFG_S medialist[32] = {
	{1, "张三的歌"},
	{2, "张三的歌"},
	{3, "张三的歌"},
	{4, "张三的歌"},
	{5, "张三的歌"},
	{6, "张三的歌"},
	{7, "张三的歌"},
	{8, "张三的歌"},
	{9, "张三的歌"},
	{10, "张三的歌"},
	{11, "张三的歌"},
	{12, "张三的歌"},
	{13, "张三的歌"},
	{14, "张三的歌"},
	{15, "张三的歌"},
	{16, "张三的歌"},
	{17, "张三的歌"},
	{18, "张三的歌"},
	{19, "张三的歌"},
	{20, "张三的歌"},
	{21, "张三的歌"},
};

int pos_start;		//界面起始位置
int pos_end = 10;	//界面结束位置
int focus;			//界面焦点位置
int menulevel = 2;	//一级界面

void MenuShow(void)
{
	int i = 0;
	
	if (menulevel == 2)
	{
		printf("+-----------------------------+\n");
		printf("|         音视频播放器        |\n");
		printf("|-----------------------------|\n");

		for (i = pos_start; i <= pos_end; i++)
		{
			if (i == focus)
			{
				printf("|\033[30;43m%2d.%-30s\033[0m|\n", medialist[i].id, medialist[i].medianame);
			}
			else
			{
				printf("|%2d.%-30s|\n", medialist[i].id, medialist[i].medianame);
			}
		}
		printf("+-----------------------------+\n");
	}
	else
	{
		
	}

	return;
}

void MenuChoose(void)
{
	char ch = 0;
	int medialen = 20;

	ch = getchar();
	if ('w' == ch)
	{
		if (focus > pos_start)
		{
			focus--;
		}
		else if (focus == pos_start && pos_start == 0)
		{
			return;
		}
		else if (focus == pos_start)
		{
			pos_start--;
			pos_end--;
			focus--;
		}
	}
	else if ('s' == ch)
	{
		if (focus < pos_end)
		{
			focus++;
		}
		else if (focus == pos_end && pos_end == medialen)
		{
			return;
		}
		else if (focus == pos_end)
		{
			pos_start++;
			pos_end++;
			focus++;
		}
	}

	return;
}

int main(int argc, const char *argv[])
{
	while (1)
	{
		system("clear");
		MenuShow();
		MenuChoose();
	}

	return 0;
}

这里就是打印界面,每按一次上下键都会打印一次完整的显示,所以调用system("clear");将之前打印的清除掉,然后在进行打印,这样就只保存了最后一次的打印。

    6.如何存储所有的音视频文件?

                用一个二维的数组,每一行都存储一个音视频文件的路径

    7.如何控制mplayer播放器?

        fork + exec来进行播放,也就是创建子进程来播放mplayer。

示例代码如下:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	pid_t pid;
	int fd = 0;
	char tmpbuff[1024] = {0};

	pid = fork();
	if (-1 == pid)
	{
		perror("fail to fork");
		return -1;
	}
	if (0 == pid)
	{
		execlp("mplayer", "mplayer", "-slave", "-input",  "file=/tmp/myfifo", "../1.flv", "-quiet", NULL);
	}
	else if (pid > 0)
	{
		fd = open("/tmp/myfifo", O_RDWR);
		if (-1 == fd)
		{
			perror("fail to open");
			return -1;
		}

		while (1)
		{
			fgets(tmpbuff, sizeof(tmpbuff), stdin);
			//pause\n
			write(fd, tmpbuff, strlen(tmpbuff));
		}	
		close(fd);
	}
	
	return 0;
}

那怎么控制mplayer?

在mplayer工作的时候,可以指定一个从属模式设置一个管道,打开这个管道然后写命令。


三、软件编写

1.查看播放列表:在指定目录下遍历文件,找到以媒体文件结尾的文件名,将其路径存储在变量中

2.开始/暂停: fork + exec(mplayer)

3.停止:向管道中写入命令

4.上一首:

5.下一首:

6.快进:向管道中写入命令

7.定位:向管道中写入命令

8.播放方式:SIGCHLD证明有子进程结束了

9.退出:进程任务结束,如果mplayer正在播放,将mplayer结束

1.head.h

#ifndef __HEAD_H__
#define __HEAD_H__

extern int LoadMediaFile(char *pDirName);
extern void ShowMediaList(void);
extern int StopMedia(void);
extern int PlayMedia(int MediaIndex);

extern int ExecUserChoose(int No);
extern int GetUserChoose(void);
extern void ShowMenu(void);
extern int PauseMedia(void);
extern int SetMediaSpeed(void);
extern int SetMediaPosition(void);
extern void SwitchMediaMode(void);
extern void sigchild_handler(int signo);

extern char MediaList[100][256];             //存放媒体文件列表
extern int CurMediaLen;                        //当前加载的媒体文件个数

//是否为播放上一首
extern int IsPlayPre;

//是否为播放下一首
extern int IsPlayNext;

#endif

2.main.c

/***************************************************
 * 
 * 主函数
 * 
 **************************************************/
#include <stdio.h>
#include "head.h"

int main(void)
{
    int No = 0;

    while (1)
    {
        CurMediaLen = LoadMediaFile("/home/linux/Music");
        ShowMenu();
        No = GetUserChoose();
        if (9 == No)
        {

            break;
        }
        ExecUserChoose(No);
    }
    
    return 0;
}

3.menu.c

/***************************************************
 * 
 * 界面相关代码
 * 
 **************************************************/

#include "head.h"
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

/***************************************************
 *函数名:ShowMenu
 *功 能:
 *      显示界面
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void ShowMenu(void)
{
    printf("===========================================\n");
    printf("1.查看播放列表\n");
    printf("2.开始/暂停\n");
    printf("3.停止\n");
    printf("4.上一首\n");
    printf("5.下一首\n");
    printf("6.快进\n");
    printf("7.定位\n");
    printf("8.播放方式\n");
    printf("9.退出\n");
    printf("===========================================\n");
    
    return;
}

/***************************************************
 *函数名:GetUserChoose
 *功 能:
 *      获得用户的选择
 *参数:
 *      void
 *返回值:
 *      用户选择的选项编号
 ***************************************************/ 
int GetUserChoose(void)
{
    int No = 0;

    printf("请选择:\n");
    scanf("%d", &No);

    return No;
}

/***************************************************
 *函数名:PlayNext
 *功 能:
 *      播放下一首
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void PlayNext(void)
{
    StopMedia();
    IsPlayNext = 1;

    return;
}

/***************************************************
 *函数名:PlayPre
 *功 能:
 *      播放下一首
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void PlayPre(void)
{
    StopMedia();
    IsPlayPre = 1;

    return;
}

/***************************************************
 *函数名:ExecUserChoose
 *功 能:
 *      执行用户选择
 *参数:
 *      No: 用户选择的选项编号
 *返回值:
 *      成功返回0 
 *      失败返回-1 
 ***************************************************/ 
int ExecUserChoose(int No)
{
    if (1 == No)
    {
        ShowMediaList();
    }
    else if (2 == No)
    {
        PauseMedia();
    }
    else if (3 == No)
    {
        signal(SIGCHLD, SIG_DFL);
        StopMedia();
        wait(NULL);
        signal(SIGCHLD, sigchild_handler);
    }
    else if (4 == No)
    {
        PlayPre();
    }
    else if (5 == No)
    {
        PlayNext();
    }
    else if (6 == No)
    {
        SetMediaSpeed();
    }
    else if (7 == No)
    {
        SetMediaPosition();
    }
    else if (8 == No)
    {
        SwitchMediaMode();
    }
    else if (9 == No)
    {
        signal(SIGCHLD, SIG_DFL);
        StopMedia();
    }

    return 0;
}

4.mplayer.c

/***************************************************
 * 
 * 媒体相关代码
 * 
 **************************************************/

#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include "head.h"

//存放媒体文件列表
char MediaList[100][256] = {0};  

//当前加载的媒体文件个数           
int CurMediaLen = 0;                        

//当前播放的媒体的编号
int CurMediaIndex = 0;

//Mplayer播放方式
typedef enum MediaMode
{
    MPLAYER_MODE_NEXT = 1,
    MPLAYER_MODE_SINGLE = 2,
    MPLAYER_MODE_RANDOM = 3,
}MPLAYER_MODE_EN;

//当前播放模式
MPLAYER_MODE_EN CurMediaMode = MPLAYER_MODE_NEXT;

//Mplayer播放速度
typedef enum MediaSpeed 
{
    MPLAYER_SPEED_ONE = 1,
    MPLAYER_SPEED_TWO = 2,
    MPLAYER_SPEED_FOUR = 4,
}MPLAYER_SPEED_EN;

//当前播放速度
MPLAYER_SPEED_EN CurMediaSpeed = MPLAYER_SPEED_ONE;

//Mplayer播放状态
typedef enum MediaStat
{
    MPLAYER_STAT_FREE,          //空闲状态
    MPLAYER_STAT_PLAY,          //播放状态
    MPLAYER_STAT_PAUSE,         //暂停状态
}MPLAYER_STAT_EN;

//当前播放状态
MPLAYER_STAT_EN CurMediaStat = MPLAYER_STAT_FREE;

//是否为播放上一首
int IsPlayPre = 0;

//是否为播放下一首
int IsPlayNext = 0;

/***************************************************
 *函数名:IsMediaFile
 *功 能:
 *      判断是否为媒体文件
 *参数:
 *      pfilename: 文件名
 *返回值:
 *      成功返回 1
 *      失败返回 0 
 ***************************************************/ 
int IsMediaFile(char *pfilename)
{
    char *ptmp = NULL;

    //helloworld.mp4
    ptmp = pfilename + strlen(pfilename);
    while (ptmp >= pfilename && *ptmp != '.')
    {
        ptmp--;
    }

    if (!strcmp(ptmp, ".mp3") || !strcmp(ptmp, ".mp4") || !strcmp(ptmp, ".flv"))
    {
        return 1;
    }

    return 0;
}

/***************************************************
 *函数名:LoadMediaFile
 *功 能:
 *      加载指定目录下所有的媒体(.mp3、.mp4、.avi、.flv)文件
 *参数:
 *      pDirName: 存放媒体文件的目录
 *返回值:
 *      成功返回实际加载媒体文件个数
 *      失败返回 -1 
 ***************************************************/ 
int LoadMediaFile(char *pDirName)
{
    //1.遍历目录文件下的所有文件名
    DIR *dp = NULL;
    struct dirent *pp = NULL;
    int cnt = 0;

    dp = opendir(pDirName);
    if (NULL == dp)
    {
        return -1;
    }

    while (1)
    {
        pp = readdir(dp);
        if (NULL == pp)
        {
            break;
        }

        if ('.' == *pp->d_name)
        {
            continue;
        }

        if (IsMediaFile(pp->d_name))
        {
            sprintf(MediaList[CurMediaLen], "%s/%s", pDirName, pp->d_name);
            CurMediaLen++;
            cnt++;
        }
    }

    closedir(dp);

    return cnt;
}

/***************************************************
 *函数名:sigchild_handler
 *功 能:
 *      播放完毕处理函数
 *参数:
 *      signo:信号的编号
 *返回值:
 *      void
 ***************************************************/ 
void sigchild_handler(int signo)
{
    wait(NULL);

    if (IsPlayNext)
    {
        if (CurMediaIndex == CurMediaLen)
        {
            printf("已经到最末尾了!\n");
            return;
        }
        else 
        {
            PlayMedia(CurMediaIndex+1);
        }  
        IsPlayNext = 0;

        return;
    }
    
    if (IsPlayPre)
    {
        if (CurMediaIndex == 0)
        {
            printf("已经到最开头了!\n");
            return;
        }
        else 
        {
            PlayMedia(CurMediaIndex-1);
        }  
        IsPlayPre = 0;

        return;
    }

    if (CurMediaMode == MPLAYER_MODE_NEXT)
    {
        if (CurMediaIndex == CurMediaLen)
        {
            printf("已经到最末尾了!\n");
            return;
        }
        else 
        {
            PlayMedia(CurMediaIndex+1);
        }  
    }
    else if (CurMediaMode == MPLAYER_MODE_SINGLE)
    {
        PlayMedia(CurMediaIndex);
    }
    else if (CurMediaMode == MPLAYER_MODE_RANDOM)
    {
        srand(time(NULL));
        PlayMedia(rand() % CurMediaLen);
    }
    
    return;
}

/***************************************************
 *函数名:PlayMedia
 *功 能:
 *      播放媒体文件
 *参数:
 *      MediaIndex:媒体文件的编号
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int PlayMedia(int MediaIndex)
{
    pid_t pid;

    signal(SIGCHLD, sigchild_handler);

    pid = fork();
    if (-1 == pid)
    {
        return -1;
    }
    if (0 == pid)
    {       
        mkfifo("/tmp/myfifo", 0664);
        close(1);
        close(2);
        execlp("mplayer", "mplayer", "-slave", "-input", "file=/tmp/myfifo", MediaList[MediaIndex], NULL);
    }

    CurMediaIndex = MediaIndex; 
    CurMediaStat = MPLAYER_STAT_PLAY;
    CurMediaSpeed = MPLAYER_SPEED_ONE;
    
    return 0;
}

/***************************************************
 *函数名:ShowMediaList
 *功 能:
 *      打印媒体列表
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void ShowMediaList(void)
{
    int i = 0;
    int No = -1;

    for (i = 0; i < CurMediaLen; i++)
    {
        printf("%d.%s\n", i+1, MediaList[i]);
    }

    while (1)
    {
        printf("请选择播放的歌曲编号(输入0返回上一级)\n");
        scanf("%d", &No);
        if (0 == No)
        {
            break;
        }

        PlayMedia(No-1);
    }

    return;
}

/***************************************************
 *函数名:StopMedia
 *功 能:
 *      停止播放媒体文件
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int StopMedia(void)
{   
    int fd = 0;

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    write(fd, "stop\n", 5);
    CurMediaStat = MPLAYER_STAT_FREE;

    close(fd);

    return 0;
}

/***************************************************
 *函数名:PauseMedia
 *功 能:
 *      暂停媒体文件
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int PauseMedia(void)
{   
    int fd = 0;

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    write(fd, "pause\n", 6);

    close(fd);

    if (MPLAYER_STAT_PLAY == CurMediaStat)
    {
        CurMediaStat = MPLAYER_STAT_PAUSE;
    }
    else if (MPLAYER_STAT_PAUSE == CurMediaStat)
    {
        CurMediaStat = MPLAYER_STAT_PLAY;
    }

    return 0;
}

/***************************************************
 *函数名:SetMediaSpeed
 *功 能:
 *      设置媒体播放速度
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int SetMediaSpeed(void)
{   
    int fd = 0;

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    if (MPLAYER_SPEED_ONE == CurMediaSpeed)
    {
        write(fd, "speed_set 2\n", 12);
        CurMediaSpeed = MPLAYER_SPEED_TWO;
    }
    else if (MPLAYER_SPEED_TWO == CurMediaSpeed)
    {
        write(fd, "speed_set 4\n", 12);
        CurMediaSpeed = MPLAYER_SPEED_FOUR;
    }
    else if (MPLAYER_SPEED_FOUR == CurMediaSpeed)
    {
        write(fd, "speed_set 1\n", 12);
        CurMediaSpeed = MPLAYER_SPEED_ONE;
    }

    close(fd);

    return 0;
}

/***************************************************
 *函数名:SetMediaPosition
 *功 能:
 *      设置媒体播放位置
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int SetMediaPosition(void)
{   
    int fd = 0;
    int n = 0;
    char cmdbuf[1024] = {0};

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    printf("请输入秒数:\n");
    scanf("%d", &n);
    sprintf(cmdbuf, "seek %d 2\n", n);
    write(fd, cmdbuf, strlen(cmdbuf));

    close(fd);

    return 0;
}

/***************************************************
 *函数名:SwitchMediaMode
 *功 能:
 *      切换媒体播放模式
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
void SwitchMediaMode(void)
{
    if (CurMediaMode == MPLAYER_MODE_NEXT)
    {
        printf("切换为:单曲循环\n");
        CurMediaMode = MPLAYER_MODE_SINGLE;
    }
    else if (CurMediaMode == MPLAYER_MODE_SINGLE)
    {
        printf("切换为:随机播放\n");
        CurMediaMode = MPLAYER_MODE_RANDOM;
    }
    else if (CurMediaMode == MPLAYER_MODE_RANDOM)
    {
        printf("切换为:顺序播放\n");
        CurMediaMode = MPLAYER_MODE_NEXT;
    }

    return;
}

5.makefile

a.out:main.c menu.c mplayer.c 
	gcc $^ -o $@

四、工作方式

相关文章:

  • @RestController和@RequestBody注解含义
  • 出现 [ app.json 文件内容错误] app.json: 在项目根目录未找到 app.json (env: Windows,mp 解决方法
  • VSCode本地python包“无法解析导入”
  • 千峰React:脚手架准备+JSX基础
  • Python 文件操作利器:FileUtils 工具类深度剖析
  • 赛前启航 | Azure 应用开发实战指南:开启创意的无限可能
  • MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 基础篇 part 15
  • 工业安全的智能哨兵:AI如何筑起生产线的“数字防火墙“
  • React实现自定义图表(线状+柱状)
  • Spring中Bean的四种实例化方法
  • 对称加密算法——IDEA加密算法
  • C# ConcurrentBag 使用详解
  • Spring Boot实战:拦截器
  • 高效执行自动化用例:分布式执行工具pytest-xdist实战!
  • oracle序列每天重置
  • Java 设计模式总结
  • 用 WOW.js 和 animate.css 实现动画效果
  • tomcat 使用域名访问失败
  • 项目一 - 任务1:了解Java编程语言
  • AUTO TECH China 2025 广州国际汽车技术展览会:引领汽车科技新潮流
  • 98岁动物学家、北京大学教授杨安峰逝世
  • 同为“东部重要中心城市”后交出首份季报:宁杭苏表现如何?
  • 上海国际电影节特设“走进大卫·林奇的梦境”单元
  • 美CIA发布视频“招募中国官员窃取机密”,外交部:赤裸裸的政治挑衅
  • 抗战回望19︱《中国工程师学会四川考察团报告》:“将来重工业所在,以四川为最适宜之地点”
  • 马克思主义理论研究教学名师系列访谈|王公龙:做好马克思主义研究,既要“钻进去”又要“跳出来”