C/S Linux _chat room

本文深入剖析了当前信息技术领域的最新趋势,包括大数据、人工智能、云计算等关键领域的发展现状及未来展望。文章详细介绍了各领域的主要技术和应用实例,旨在帮助读者把握行业脉搏,推动技术创新与实践。


//client.h

#ifndef _CLIENT_H__
#define _CLIENT_H__


#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <arpa/inet.h>


#define MAXLEN    1024


struct message              //信息结构
{
char   flag[8];         //标识:"all","view","trans"...还可能是客户的名字
char   name[12];        
int    size;            //信息 msg 字段的字节长度
char   msg[MAXLEN];     //信息的内容
};


struct msg                  //系统中用于信息交互的 消息队列
{
long   type;            //信息类型,要大于 0,小于 0 的 内核有特殊用途。一般用 进程号 表示,
    //因为 消息队列 多用于不同进程间的通信,用 进程号 标识先应的接口,用于发收信息。


char   text[4];         //本程序中,并不把数据放到 消息队列,而是放到 chitchat(客户聊天记录) 文件中
//用 消息队列,只是为了通过 其 发收 机制及时显示所接受到的信息,因为本程序的 输入
//与 显示 是分开的,即是用不同的进程,所以需要 消息队列。如果在 同一个 窗口下 则不需要
};


void   HandleQuit(int signal);                  //客户退出
void * HandleRecvmsg(int *sockfd);              //客户接受信息线程的处理操作
void * HandleSendfile(char *filename);          //客户发送文件线程的处理操作
void * HandleRecvfile(struct message *msg);     //客户接受文件线程的处理操作


int    qid;                 //整数描述符,用其来指代将要的 消息队列,并初始化
int    fd;                  // chitchat 文件的 文件描述符
int    sockfd;              //链接到服务器的套接口
int    savefilefd;          // 发送方:-1 是初始化,还没打开文件,大于 0,打开了的文件的 文件描述符
    // 接收方:0  是接受方收到询问是否接受时,所设定的
char   filetoname[12];      //用于文件传输,接收方的名字
char   filename[12];        //发送方将要发送的 文件名


// chitchat 文件:客户端运行时会自动生成已保存客户本次的通话记录
#endif






// client_main

#include "client.h"

#define SERVER_PORT      9877
/*
extern int    qid;            //
整数描述符,用其来指代将要的 消息队列,并初始化
extern int    fd;             // chitchat
文件的 文件描述符
extern int    savefilefd;     //
发送方:-1 是初始化,还没打开文件,大于 0,打开了的文件的 文件描述符
*/

int main(int argc,char *argv[])
{
    struct sockaddr_in   serveraddr;       //
服务器地址结构
    struct message       sendmsg;          //
将要发送的信息
    struct message       recvmsg;          //
服务器的回应信息
    int                  choose;           //
操作选择
    char                 buf[MAXLEN];
    pthread_t            pid;              //
接受线程id
    char                 flag[8];          //
发送信息的标识,allview...
    char                 name[12];         //
发送者名字

    qid                  = -1;
    fd                   = -1;
    savefilefd           = -1;
//  memset(&recvmsg,0,sizeof(structmessage));
//  memset(&sendmsg,0,sizeof(structmessage));
    if(argc < 2)
    {
        printf("
请输入服务器的地址 (./out [ip地址])");
        exit(1);
    }

    if((sockfd =socket(AF_INET,SOCK_STREAM,0)) < 0)
    {
        perror("socket error");
        exit(1);
    }
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family      = AF_INET;
    serveraddr.sin_addr.s_addr =inet_addr(argv[1]);         //
把 服务器地址赋值给 服务器地址结构
    serveraddr.sin_port        = htons(SERVER_PORT);

    if(connect(sockfd,(structsockaddr*)&serveraddr,sizeof(struct sockaddr)) < 0)
    {//
与服务器链接
        perror("connect error");
        exit(1);
    }

    signal(SIGINT,HandleQuit);                               //
退出是,启动 HandleQuit函数
//
问题:该 信号 函数应放在那个位置
    printf("----------------------------------\n");
    printf("|                                |\n");
    printf("|   input a number to work       |\n");
    printf("|\t1.login\t\t\t|\n");
    printf("|\t2.register\t\t|\n");
    printf("|\t3.exit\t\t\t|\n");
    printf("|                                |\n");
    printf("----------------------------------\n");
   
    printf("
请输入你的选择:");
    scanf("%d",&choose);
    while(choose != 1 && choose !=2 && choose != 3)
    {//
输入不正确
        printf("
你输入的不是上面的选项,请重新输入:\n");
        scanf("%d",&choose);
    }

    int  n = 3;                  //
3 次登录机会
    char     password[20];      
    char     password_sure[20];      //
密码确认
    int      done = -1;            //
输入密码正确

    switch(choose)
    {
        case 1://
登录操作
                                 
            while(n)
            {
                printf("
请输入你的帐号名字: ");
                scanf("%s",name);
                strcpy(sendmsg.name,name);
                printf("
请输入你的密码:");
                scanf("%s",sendmsg.msg);
                strcpy(sendmsg.flag,"login");
                send(sockfd,(structmessage *)&sendmsg,sizeof(struct message),0);
                printf("
等待服务器链接...\n");
                recv(sockfd,(structmessage *)&recvmsg,sizeof(struct message),0);
                if(strcmp(recvmsg.msg,"
登录成功") != 0)
                {//
登录不成功
                    printf("
登录不成功\n");
                    --n;
                    printf("
你还有 %d 次登录机会\n",n);
                    continue;                              //
结束本次循环,等待下一次登录
                }
               
                //
登录成功后的操作
                printf("
登录成功");
                n = 0;                                     //
客户已登录,退出后要重新打开程序方可登录
                pthread_create(&pid,NULL,(void*)HandleRecvmsg,(void*)&sockfd);    //
创建接受信息线程

                while(1)
                {
                    printf("
开始发送信息...\n");
                    printf("
请输入标识(flag):");            //私聊时,flag为 接受端的名字
                    scanf("%s",flag);
                    strcpy(sendmsg.flag,flag);
                    strcpy(sendmsg.name,name);

                    if(strcmp(sendmsg.flag,"all")== 0)
                    {//
群发信息
                        printf("
请输入你的内容:\n");
                        scanf("%s",buf);
                        strcpy(sendmsg.msg,buf);
                        send(sockfd,(structmessage *)&sendmsg,sizeof(struct message),0);
                        continue;                          //
接受本次循环,可重新发送信息
                    }
                    elseif(strcmp(sendmsg.flag,"view") == 0)
                    {//
查看在线客户
                        send(sockfd,(structmessage *)&sendmsg,sizeof(struct message),0);
                        continue;                          //
接受本次循环,可重新发送信息                      
                    }
                    elseif(strcmp(sendmsg.flag,"trans") == 0)
                    {
                        if(savefilefd <0)
                        {//
发送方
                            printf("
请输入所要发送的文件:");
                            scanf("%s",filename);
                            strcpy(sendmsg.msg,filename);
               
                            printf("
请输入文件的接收方: ");
                            scanf("%s",name);
                            strcpy(sendmsg.name,name);
   
                            send(sockfd,(structmessage*)&sendmsg,sizeof(struct message),0);  //
发送询问文件接收信息,agree |disagree
                        }
                        else
                        {//
接收方 ,与 接受线程的 trans 的 (接受端)相呼应
                            scanf("%s",buf);                //
输入是否同意接受文件,agree|disagree
                            send(sockfd,(structmessage*)&sendmsg,sizeof(struct message),0);   //
发送回复信息给 发送端    
                        }
//
问题:如果几个客户同时传送文件给同一个客户,如何处理
                        continue;
                    }//end trans
                    else
                    {//
私聊,因为 flag 与上面的都不符合,私聊时,flag为 接受端的名字
                        printf("
请输入你的内容:\n");
                        scanf("%s",buf);
                        strcpy(sendmsg.msg,buf);
                        send(sockfd,(structmessage*)&sendmsg,sizeof(struct message),0);
                    }
                }//end while(1)            
            }//end while(n)
            break;
        case 2://
注册操作

            printf("
请输入你的 用户名:");
            scanf("%s",name);
            do
            {//
密码输入
                printf("
请输入你的密码:");
                scanf("%s",password);
                printf("
请重新输入一次密码:");
                scanf("%s",password_sure); 
//
问题:如何隐藏密码
                if(strcmp(password,password_sure)== 0)
                {
                    printf("
密码输入正确");
                    done = 1;
                }      
                else
                {
                    printf("
密码输入不正确,请重新输入");
                    done = -1;
                }
            }while(done < 0);

            strcpy(sendmsg.flag,"reg");
            strcpy(sendmsg.name,name);
            strcpy(sendmsg.msg,password);

            printf("
向服务器发送注册请求...\n");
            send(sockfd,(struct message*)&sendmsg,sizeof(struct message),0);
            printf("
等待服务器应答...\n");
            recv(sockfd,(struct message*)&recvmsg,sizeof(struct message),0);
            printf("%s\n",recvmsg.msg);
            close(sockfd);

            break;
        case 3://
退出
            close(sockfd);
            printf("
程序已退出\n");
            exit(0);                                        //
正确退出
            break;
        default:
            printf("
你的输入有误\n");
    }

    return 0;
}

/*
测试时遇到问题
问题 1fread()的错误处理,即当 if((fread(...)==0))是,可能是 错误 或 EOF,用 if(feof(fd))则是EOF,否则不是
问题 2struct message msg字段 内容中如何处理有空格的语句???
   
预想解决方法:发送端还好,简单;但是 接收端麻烦: msg 内容都要扫描
*/

//client.c 功能函数的定义 


#include "client.h"


/**************************
    HandleQuit
函数:客户端退出处理
   
其参数:intsignal:所接收到的信号
   
返回值:void
**************************/
void  HandleQuit(int signal)
{
    if(fd > 0)
    {// chitchat
文件已打开
        close(fd);
    }

    if(qid > 0)
    {//
创建了 消息队列
        if(msgctl(qid,IPC_RMID,NULL) <0)
        {
            perror("
删除消息队列失败");
           
            close(savefilefd);           //
即使 消息队列 删除失败,也是要关闭 文件间的映射的,既要关闭文件描述符
            exit(1);
        }
    }

    close(savefilefd);                   //
关闭 消息队列 成功,关闭 文件描述符
    close(sockfd);
    printf("
程序正常退出...\n");

}




/**************************
    HandleSendfile
函数:客户传送文件的线程函数
   
其参数:charfilename[12]:发送方将要发送的 文件名
   
返回值:void*:(void*)0,(void*)1...0 表示正常退出,1表示出错,强制退出...
**************************/
void * HandleSendfile(char *filename)
{
    char           file[12];           //
文件名
    strcpy(file,filename);
   
    struct message  filedata;          //
该文件的相关信息
    strcpy(filedata.flag,"transf");    //
表示正在传送文件
    strcpy(filedata.name,filetoname);  //
文件的接收方
   
    ssize_t         nread;             //
从被发送的文件中读到的数据字节数
    int             fd;                //
与被发送的文件相映射的 文件描述符      
    char            buf[MAXLEN];
         
    if((fd = open(file,O_RDONLY)) < 0)
    {
        perror("
文件无法打开");
        strcpy(filedata.msg,"end$");   
        send(sockfd,&filedata,sizeof(structmessage),0);
        exit(1);
    }
//
问题:文件无法打开时,怎么办。直接发送 end$,结束该线程

    do
    {
        nread = read(fd,buf,sizeof(buf));
        if(nread < 0)
        {//
文件读取数据失败
            perror("
文件数据读取失败");
            close(fd);
            exit(1);
        }
        else if(nread == 0)
        {
            printf("
文件读取完成\n");
            strcpy(filedata.msg,"end$");
            send(sockfd,&filedata,sizeof(structmessage),0);
        }
        else
        {//
有数据要传
            filedata.size = nread;        //
数据的字节数
            strcpy(filedata.msg,buf);
            send(sockfd,&filedata,sizeof(structmessage),0);
        }
    }while(nread > 0);                    //
有数据要传

    close(fd);                            //
关闭被传送的文件
    savefilefd = -1;                      //
有可以传文件了

    return NULL;
}




/**************************
    HandleRecvfile
函数:接受端接收发送端传送文件的线程函数
   
其参数:structmessage *recvmsg:发送方发送过来的文件内容
   
返回值:void*:(void*)0,(void*)1...0 表示正常退出,1表示出错,强制退出...
**************************/
void * HandleRecvfile(struct message *msg)
{
    struct message  recvmsg = *msg;    //
把 接收线程 接收到的数据赋值到 接收文件线程 中
    int             nread;             //
将要写入文件的字节书数

    if(strcmp(recvmsg.msg,"end$")== 0)
    {//
接受完发送端发送过来的文件
        printf("
接收文件完成\n");
        close(savefilefd);             //
关闭刚接收完的文件
        savefilefd = -1;               //
有可以接受或发送文件了
    }

    do
    {//
把数据写入文件中
        nread =write(savefilefd,&recvmsg.msg,MAXLEN);
    }while(nread > 0);
   
    return NULL;
}





/**************************
    HandleRecvmsg
函数:客户端接受到服务器发来的消息
   
其参数:int*sockfd:链接套接口
   
返回值:void*:(void*)0,(void*)1...0 表示正常退出,1表示出错,强制退出...
**************************/
void * HandleRecvmsg(int *sockfd)
{
    int          connfd = *sockfd;       //
把 主线程 中的链接套接口 赋值到 接受信息线程的 套接口 中
    int          nread;                  //
接受到的信息的字节数
    char        str[MAXLEN];  

    struct message  recvmsg;             //
记录 接受到的信息
    struct msg      msg;                 //
消息队列
    time_t          curtime;             //
表示当下时间
   
    if((fd =open("chitchat",O_RDWR | O_CREAT | O_APPEND)) < 0)
    {
        perror("
打开 聊天记录文件 失败");
        exit(1);
    }
    if((qid = msgget(2222,IPC_CREAT |0666)) < 0)
    {//
显示的创建一个键值是为方便通讯
        perror("
创建 消息队列 不成功");
        exit(1);
    }
/*
在创建 消息队列 时,对于用户的权限不是很清楚,解决(下列图表)

数字值               符号值                       说明  
(八进制)  消息队列     信号量      共享内存区
 0400    MSG_R       SEM_R       SHM_R       
由用户(属主)读
 0200    MSG_W       SEM_A       SHM_W       
由用户(属主)写
 0040    MSG_R >> 3  SEM_R >>3  SHM_R >> 3  
由(属)组成员读
 0020    MSG_W >> 3  SEM_A >>3  SHM_W >> 3  
由(属)组成员写
 0004    MSG_R >> 6  SEM_R >>6  SHM_R >> 6  
由其他用户读
 0002    MSG_W >> 6  SEM_A >>6  SHM_W >> 6  
由其他用户写

记号 >> 3 的意思是将 值 右移 3
该图表上的 权限 都是用 二进制的 AND|)运算的
*/
    msg.type = getpid();                  //
得到当前 进程号,以使 消息队列 可分辨相关数据的处理对象
    strcpy(msg.text,"ok");                //
该数据作用不大,只是为了起到填充数据的作用

   
    while(1)
    {
        nread = recv(connfd,(structmessage *)&recvmsg,sizeof(struct message),0);
       
        if(nread < 0)
        {//
接受信息时出错
            perror("
接受信息失败");
            close(fd);
            exit(1);
        }
        else if(nread == 0)
        {// IP
数据包内容为空
            printf("
与服务器断开链接...\n");
            close(fd);
            exit(1);
        }
        elseif(strcmp(recvmsg.flag,"all") == 0)
        {//
群聊信息
            time(&curtime);                    //
获取当前时间
//          sprintf(str," %s %s
发给所有客户:%s\n\n",ctime(&curtime),recvmsg.name,recvmsg.msg);      
            printf(" %s %s
发给所有客户:%s\n\n",ctime(&curtime),recvmsg.name,recvmsg.msg);                
        }
        elseif(strcmp(recvmsg.flag,"view") == 0)
        {//
查看在线客户
            time(&curtime);
            printf(" %s
当前在线客户:%s\n\n",ctime(&curtime),recvmsg.msg);
            continue;                           //
结束 本次 循环,继续等待新的信息
        }
        else if(strcmp(recvmsg.flag,"trans")== 0)
        {//
传送 文件 询问信息
            pthread_t  pid;                     //
用于传送文件的线程id

            if(strcmp(recvmsg.msg,"agree")== 0)
            {//
(发送端)发送方收到接收方的肯定接受文件的回复信息
                strcpy(filetoname,recvmsg.name);
                pthread_create(&pid,NULL,(void*)HandleSendfile,(void*)filename);
                continue;
            }
            elseif(strcmp(recvmsg.msg,"disagree") == 0)
            {//
(发送端)接收方拒绝接收文件
                printf("
客户 %s 拒绝接收 %s文件\n",recvmsg.name,filename);
                savefilefd = -1;                //
客户又可以重新选择在线用户传送文件了
                continue;
            }
            else if(strcmp(recvmsg.msg,"noexist")== 0)
            {//
(发送端)客户不存在
                printf("
不存在该用户\n");          //可从服务器查看在线的客户,用 flag = view即可
                savefilefd = -1;                 //
客户又可以重新选择在线用户传送文件了
                continue;
            }
            else
            {//
(接受端)如果发送端发送文件结束询问,~.msg 为 发送端要发送的文件名,故和以上的都不匹配
                printf("
客户 %s 传送 %s文件给你,是否接收?[agree(同意)] | [disagree(不同意)]\n",\
                       recvmsg.name,recvmsg.msg);//
作为接受端,其接受到的的是发送端的传送文件询问信息,
                                                 //
此时,recvmsg.name为发送端的名字,
                                                //recvmsg.msg
为将要发送的问文件名 
                strcpy(filename,recvmsg.msg);    //
接收方接受到的文件名

                if((savefilefd =open(recvmsg.msg,O_CREAT | O_RDWR | O_APPEND,0666)) <0)
                {//
savefiledf 作为与接收到的文件的 文件描述符,但此时打开该文件不成功
                    printf("
无法创建 %s 文件",recvmsg.msg);
                }          
            }
/*
在传送文件的设计中,需要考虑到接收方,发送方都是客户端,都要接受收据
故在这数据的接受函数中,这个文件传送的询问信息关系到这两方
*/
            continue;                           //
继续等待信息
        }//end trans
        elseif(strcmp(recvmsg.flag,"transf") == 0)
        {//
(接收方)接受发送端发送过来的文件
/*
            pthread_t        pid;                 //
接收方接收文件的线程处理函数
            pthread_create(&pid,NULL,(void*)HandleRecvfile,(void*)&recvmsg);
问题:如此每次接受到一条文件信息,就要创建一个接收文件线程,不好。
   
如需真用接收文件线程,就需要与接收线程分开(HandleRecvmsg
*/
            if(strcmp(recvmsg.msg,"end$")== 0)
            {//
接受完发送端发送过来的文件
                printf("
接收文件完成\n");
                close(savefilefd);             //
关闭刚接收完的文件
                savefilefd= -1;               //
有可以接受或发送文件了
            }

            do
            {//
把数据写入文件中
                nread =write(savefilefd,&recvmsg.msg,MAXLEN);
            }while(nread > 0);

            continue;
        }
        else
        {//
私聊,因为私聊是,flag 为 接收端的 名字,与以上不符
            time(&curtime);                    //
获取当前时间
//          sprintf(str,"
私聊: %s %s 发给客户:%s\n\n",ctime(&curtime),recvmsg.name,recvmsg.msg); 
            printf("
私聊: %s %s 发给客户:%s\n\n",ctime(&curtime),recvmsg.name,recvmsg.msg);
        }//
群聊,私聊都没有用 continue 是因为,这些内容要写入 聊天记录 文件中,一起处理,方便而已,
         //
也可用continue,只要分别在 群聊,私聊 中直接写入 聊天记录 文件即可

        write(fd,str,sizeof(str));              //
把消息写入 聊天记录 文件中
        msgsnd(qid,&msg,sizeof(structmsg),0);  //
发送消息给 消息队列,通知其 display进程 到 聊天记录 文件中读取数据

    }//end while

    return NULL;
}
/*
问题:可用 switch,可用 枚举 把 flag 打包起来,用相关数字表示
struct message
{
    enum flag {all,view...}
    ...
}
*/






 




 
# snmp_exporter ## Getting started To make it easy for you to get started with GitLab, here's a list of recommended next steps. Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! ## Add your files - [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files - [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: ``` cd existing_repo git remote add origin https://10.6.80.1/ops/snmp_exporter.git git branch -M main git push -uf origin main ``` ## Integrate with your tools - [ ] [Set up project integrations](https://10.6.80.1/ops/snmp_exporter/-/settings/integrations) ## Collaborate with your team - [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) - [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) - [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) - [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) - [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) ## Test and Deploy Use the built-in continuous integration in GitLab. - [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) - [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) - [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) - [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) - [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) *** # Editing this README When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. ## Suggestions for a good README Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. ## Name Choose a self-explaining name for your project. ## Description Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. ## Badges On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. ## Visuals Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. ## Installation Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. ## Usage Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. ## Support Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. ## Roadmap If you have ideas for releases in the future, it is a good idea to list them in the README. ## Contributing State if you are open to contributions and what your requirements are for accepting them. For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. ## Authors and acknowledgment Show your appreciation to those who have contributed to the project. ## License For open source projects, say how it is licensed. ## Project status If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. 翻译以上内容 并总结使用方法
09-04
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值