首页 > 系统相关 >基于Linux下的多人聊天室

基于Linux下的多人聊天室

时间:2024-06-06 19:29:38浏览次数:15  
标签:聊天室 socket user1 int void printf 基于 user Linux

基于Linux下的多人聊天室

1.涉及知识点

Linux、C语言、TCP通信、epoll、SQL

2.整体架构流程

服务器:
1.搭建TCP连接客户端
2.链接数据库
3.使用epoll
4.处理各种客户端消息的接收与发送
客户端:
1.搭建TCP连接服务器
2.建立菜单处理各种消息的发送与接收

3.核心功能展示

1.登录界面
用户端:
用户登录界面
服务器端(反馈):
服务器端(反馈)
进行注册:
注册
登录:
登录
私聊:
私聊
群聊
群聊
数据库:
数据库

4.详细代码

  • 服务器端
#include <stdio.h>
#include <mysql/mysql.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#define SERVER_IP "192.168.43.113"
#define SERVER_PORT 1111

int skd; // 服务器套接子
int epfd;
struct epoll_event ev; // 这个结构体仅仅创建一次,后续都可使用
int clinum;            // 用来标记在线人数
int num;               // 用来保存异动套接子的个数
char buf[1024];        // 数据库传数据的
enum MessageType
{
    // menu1
    REGISTRATION,
    LOGIN,
    // menu2
    QUN,
    SI,
    DELETE_ID,
    LOG_out,
    Ke_out
    // 此处添加其他消息类型
};

typedef struct // 打包本地数据
{
    int chat_mode;
    int UID;
    char nickname[50];
    char password[20];
    int target_id;
    char send_message[1024];
    int socket;
    int log_flag;
    enum MessageType type;
} User;
User user[1024];
User user1;
User user2;
MYSQL *sql;
void *client_register_func(void *arg);
void *client_LOG_IN_func(void *arg);
void look_time();
int main(void)
{
    memset(&user1, 0, sizeof(user1));
    skd = socket(AF_INET, SOCK_STREAM, 0);       // 套接字类型为流式套接字(SOCK_STREAM)
    struct sockaddr_in info;                     // 用于存储服务器的地址信息
    info.sin_addr.s_addr = inet_addr(SERVER_IP); // 设置服务器的IP地址
    info.sin_family = AF_INET;                   // 指定地址族为IPv4(AF_INET)
    info.sin_port = htons(SERVER_PORT);          // 设置服务器的端口号,htons是将主机字节顺序统一为大端(主机字节顺序大小段模式不确定)

    // 允许快速重用端口,并支持多个套接字绑定到同一个端口
    int opt = 1;
    setsockopt(skd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(skd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    // 将套接字与指定的本地地址和端口号相关联
    int ret = bind(skd, (struct sockaddr *)&info, sizeof(info));
    if (ret < 0)
    {
        perror("bind");
        return -1;
    }
    printf("服务器IP绑定成功\n");

    ret = listen(skd, 1024);
    if (ret < 0)
    {
        perror("listen");
        return -1;
    }
    printf("服务器已启动,等待连接...\n");
    char buf[128];
    // 1.创建句柄 --- 只创建一次
    epfd = epoll_create(1);
    if (epfd <= 0)
    {
        perror("epoll_create");
        return -1;
    }
    // 2.在句柄中加入监听套接子
    ev.data.fd = skd;
    ev.events = EPOLLIN;

    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, skd, &ev);
    if (ret < 0)
    {
        perror("epoll_ctl");
        return -1;
    }
    struct epoll_event retevn[1024];
    // 链接数据库
    // 1.初始化数据库
    sql = mysql_init(NULL);
    // 2.连接数据库
    sql = mysql_real_connect(sql, "localhost", "root", "1", "mysql", 0, NULL, 0);
    if (sql == NULL)
    {
        perror("mysql_real_connect");
        return -1;
    }
    printf("数据库连接成功\n");
    // 链接需要使用的数据库
    sprintf(buf, "%s", "use Chat_data");
    // 在终端创建数据表:users_info(UID int,昵称 char(50),密码 char(20))
    ret = mysql_query(sql, buf);
    if (ret != 0)
    {
        perror("mysql_query");
        return -1;
    }
    printf("更改数据库成功\n");
    memset(buf, 0, sizeof(buf));

    int i = 0;
    while (1)
    {
        printf("开始阻塞\n");
        //-1表示单次阻塞的时间足够长
        num = epoll_wait(epfd, retevn, 1024, -1);
        if (num <= 0)
        {
            perror("epoll_wait");
            continue;
        }
        printf("解除阻塞\n");
        for (i = 0; i < num; i++) // 异动套接子可能不止一个
        {
            if (retevn[i].data.fd == skd) // 说明服务器套接字有动静
            {
                if (retevn[i].events == EPOLLIN)
                {
                    // 说明有客户端上线
                    struct sockaddr client_info;
                    socklen_t len = sizeof(client_info);
                    // 将上线的文件描述符单独保存
                    int client_socket = accept(skd, &client_info, &len);
                    if (client_socket < 0)
                    {
                        perror("accept");
                        // 处理接受错误
                        continue; // 继续处理其他事件
                    }
                    // 将上线的套接字加入到epoll的句柄
                    ev.data.fd = client_socket;
                    ev.events = EPOLLIN;
                    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, client_socket, &ev);
                    if (ret < 0)
                    {
                        perror("epoll_ctl");
                        // 处理epoll控制错误
                        close(client_socket); // 关闭套接字
                        continue;             // 继续处理其他事件
                    }
                    printf("new client connected: %d\n", client_socket);
                    user[clinum].socket = client_socket; // 将新客户端信息保存到数组中
                    printf("clinum = %d\n", clinum);
                    printf("user[clinum].socket = %d\n", user[clinum].socket);
                    clinum++;
                }
            }
            else // 说明客户端有动静
            {
                for (int j = 0; j < clinum; j++)
                {
                    if (user[j].socket == retevn[i].data.fd)
                    {
                        if (read(retevn[i].data.fd, &user1, sizeof(user1)) == 0)
                        {
                            printf("socket:%d  断开\n", user[j].UID);
                            close(user[j].socket);
                            // 移除断开连接的套接字
                            for (int k = j; k < clinum - 1; k++)
                            {
                                user[k] = user[k + 1];
                            }
                            clinum--;
                            break;
                        }
                        switch (user1.type)
                        {
                        case REGISTRATION:
                        {
                            pthread_t pd1 = 0;
                            pthread_create(&pd1, NULL, client_register_func, (void *)retevn[i].data.fd);
                            break;
                        }
                        case LOGIN:
                        {
                            user1.socket = user[j].socket; // 异动套接子 --- 发送方套接子
                            pthread_t pd2 = 0;
                            pthread_create(&pd2, NULL, client_LOG_IN_func, (void *)retevn[i].data.fd);
                            break;
                        }
                        case QUN:
                        {
                            user1.socket = user[j].socket; // 异动套接子 --- 发送方套接子
                            printf("%d %d %s\n", user1.socket, user1.UID, user1.send_message);
                            printf("%s\n", user1.send_message);
                            printf("user1.socket%d\n", user1.socket);
                            for (int k = 0; k < clinum; k++)
                            {
                                if (user[k].socket != user1.socket)
                                {
                                    printf("user1.socket%d\n", user1.socket);
                                    printf("user1.nick%s\n", user1.nickname);
                                    printf("user[k].socket %d\n", user[k].socket);
                                    sprintf(buf, "%s", "select * from users_info");
                                    mysql_query(sql, buf);
                                    MYSQL_RES *retval = mysql_store_result(sql);
                                    // 8.1.获取行数 --- 判断数据表中是否存在相关数据
                                    // 判断这个人是否存在,找到对应人的对应值
                                    unsigned long rownum = mysql_num_rows(retval);
                                    unsigned long fieldnum = mysql_num_fields(retval);
                                    // 8.2获取每一行中的内容
                                    int a = 0, b = 0;
                                    MYSQL_ROW rowval;
                                    for (a = 0; a < rownum; a++) // 打印数据表中的所有内容
                                    {
                                        rowval = mysql_fetch_row(retval);
                                        if (atoi(rowval[3]) == user1.socket)
                                        {
                                            strcpy(user1.nickname, rowval[1]);
                                        }
                                    }
                                    write(user[k].socket, &user1, sizeof(user1));
                                }
                            }
                            break;
                        }
                        case SI:
                        {
                            user2=user1;
                            printf("%d:%d\n",user1.socket,user1.UID);
                            sprintf(buf, "%s", "select * from users_info");
                            mysql_query(sql, buf);
                            MYSQL_RES *retval = mysql_store_result(sql);
                            // 8.1.获取行数 --- 判断数据表中是否存在相关数据
                            // 判断这个人是否存在,找到对应人的对应值
                            unsigned long rownum = mysql_num_rows(retval);
                            unsigned long fieldnum = mysql_num_fields(retval);
                            // 8.2获取每一行中的内容
                            int a = 0, b = 0;
                            MYSQL_ROW rowval;
                            for (a = 0; a < rownum; a++) // 打印数据表中的所有内容
                            {
                                rowval = mysql_fetch_row(retval);
                                if (atoi(rowval[3]) == user1.socket)
                                {
                                   strcpy(user2.nickname,rowval[1]);
                                }
                                if (atoi(rowval[0]) == user2.UID)
                                {
                                    user2.socket = atoi(rowval[3]);
                                }
                            }

                            printf("接收方套接子为 is %d\n", user2.socket);
                            write(user2.socket, &user2, sizeof(user2));

                            break;
                        }
                        case LOG_out:
                        {
                            printf("UID:%d  %s 退出登陆\n", user1.UID, user1.nickname);
                            sprintf(buf, "UPDATE users_info SET socket = 0 WHERE socket = %d", user[j].socket);
                            mysql_query(sql, buf);
                            // UPDATE users_info SET socket = 0 WHERE UID = '50663392'
                            break;
                        }
                        case Ke_out:
                        {
                            printf("socket:%d  客户端退出\n", user[j].socket);
                            close(user[j].socket);
                            // 移除断开连接的套接字
                            for (int k = j; k < clinum - 1; k++)
                            {
                                user[k] = user[k + 1];
                            }
                            clinum--;
                            break;
                        }
                        case DELETE_ID:
                        {
                            printf("UID:%d 账号已注销\n", user1.UID);
                            sprintf(buf, "delete from users_info where UID=%d", user1.UID);
                            mysql_query(sql, buf);
                        }
                        default:
                            break;
                        }
                    }
                }
            }
        }
    }
    return 0;
}
void *client_register_func(void *arg)
{
    srand(time(0));
    int fd = (int)arg;
    int i = 0;
    for (i = 0; i < clinum; i++)
    {
        if (user[i].socket == fd)
        {
            int number;
            do
            {
                number = rand() % 90000000 + 10000000;
            } while (number == 0);
            // 检查随机数是否已经存在
            int exists = 0;
            for (int j = 0; j < clinum; j++)
            {
                if (user[j].UID == number)
                {
                    exists = 1;
                    break;
                }
            }
            if (exists)
            {
                // 如果随机数已存在,重新生成
                i--;
                continue;
            }
            strcpy(user[i].nickname, user1.nickname);
            strcpy(user[i].password, user1.password);
            user[i].UID = number;
            // 写到数据库中
            sprintf(buf, "insert into users_info value(%d,'%s','%s',%d)", user[i].UID, user[i].nickname, user[i].password, user[i].socket);
            mysql_query(sql, buf);
            write(fd, &user[i], sizeof(user[i]));
        }
    }
    return NULL;
}

void *client_LOG_IN_func(void *arg)
{
    int fd = (int)arg;
    sprintf(buf, "%s", "select * from users_info");
    mysql_query(sql, buf);
    MYSQL_RES *retval = mysql_store_result(sql);
    // 8.1.获取行数 --- 判断数据表中是否存在相关数据
    // 判断这个人是否存在,找到对应人的对应值
    unsigned long rownum = mysql_num_rows(retval);
    unsigned long fieldnum = mysql_num_fields(retval);
    // 8.2获取每一行中的内容
    int a = 0, j = 0;
    MYSQL_ROW rowval;
    for (a = 0; a < rownum; a++) // 打印数据表中的所有内容
    {
        rowval = mysql_fetch_row(retval);
        if (atoi(rowval[0]) == user1.UID)
        {
            for (j = 0; j < fieldnum; j++)
            {
                if (strcmp(rowval[j], user1.password) == 0)
                {
                    user1.log_flag = 1;
                    if (user1.log_flag == 1)
                    {
                        printf("UID:%d 成功上线\n", user1.UID);
                        sprintf(buf, "UPDATE users_info SET socket = %d WHERE UID = %d", fd, user1.UID);
                        mysql_query(sql, buf);
                        write(user1.socket, &user1, sizeof(user1));
                    }
                }
            }
        }
    }
    if (user1.log_flag == 0)
    {
        write(user1.socket, &user1, sizeof(user1));
        printf("账号或密码错误\n");
    }
    return NULL;
}
  • 客户端
#include <stdio.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#define SERVER_IP "192.168.43.113"
#define SERVER_PORT 1111

int ke_socket; // 套接字
int ret;
char rbuf[1024];
int si_id;
int n; // 菜单选择

enum MessageType
{
    // menu1
    REGISTRATION,
    LOGIN,

    // menu2
    QUN,
    SI,
    DELETE_ID,
    LOG_out,
    Ke_out
    // 此处添加其他消息类型
};

typedef struct // 打包本地数据
{
    int chat_mode;
    int UID;
    char nickname[50];
    char password[20];
    int target_id;
    char send_message[1024];
    int socket;
    int log_flag;
    enum MessageType type;
} User;
User user;
void si_chat(int ke_socket);
void look_time();
void qun_chat(int ke_socket);
void registers(int ke_socket);
void menu2(int ke_socket);
void LOG_in(int ke_socket);
void *my_qun_threadfunc(void *arg);
void *my_sichat_func(void *arg);

void myfunc(int signum) // 重定义2号信号
{
    if (signum == 2) // 收到 SIGINT 信号
    {
        printf("客户端已退出\n");

        close(ke_socket);
        exit(0);
    }
}

void *mypthreadfunc(void *arg) // 客户端发送
{
    int client_socket = *(int *)arg;
    while (1)
    {
        printf("client send:\n");
        scanf("%s", user.send_message);
        write(client_socket, &user, sizeof(user));
    }
    return NULL;
}

int main(void)
{
    ke_socket = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in info;
    info.sin_addr.s_addr = inet_addr(SERVER_IP);
    info.sin_family = AF_INET;
    info.sin_port = htons(SERVER_PORT);

    int opt = 1;
    setsockopt(ke_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(ke_socket, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));

    ret = connect(ke_socket, (struct sockaddr *)&info, sizeof(info));
    if (ret < 0)
    {
        perror("connect");
        return -1;
    }
    printf("成功链接智慧树\n");
    signal(SIGINT, myfunc);

    while (1)
    {
        printf("*****欢迎使用虚空终端*****\n");
        printf("1.登陆 2.注册 3.退出\n");
        printf("请选择:\n");
        int a;
        scanf("%d", &a);
        switch (a)
        {
        case 1:
            LOG_in(ke_socket);
            break;
        case 2:
            registers(ke_socket);
            break;
        case 3:
            printf("退出\n");
            user.log_flag = 0;
            user.socket = ke_socket;
            user.type = Ke_out;
            write(ke_socket, &user, sizeof(user));
            return 0;
        default:
            printf("输入有误,请重新输入!\n");
            break;
        }
    }
    close(ke_socket);
    return 0;
}

void registers(int ke_socket)
{
    while (1)
    {
        user.type = REGISTRATION;
        srand(time(0));
        printf("请输入想要注册的昵称:");
        scanf("%s", user.nickname);
        printf("请输入密码:");
        scanf("%s", user.password);
        printf("请确认密码:");
        char remm[20];
        scanf("%s", remm);
        if (strcmp(user.password, remm) == 0)
        {
            write(ke_socket, &user, sizeof(user)); // 写入用户信息
            read(ke_socket, &user, sizeof(user));  // 读取服务器返回的用户信息

            printf("注册成功,请牢记个人信息:\n");
            printf("昵称:%s\n", user.nickname);
            printf("UID:%d\n", user.UID);
            break;
        }
        else
        {
            printf("注册失败,两次密码不一致!\n");
            printf("请返回菜单,重新注册!\n");
        }
    }
}

void LOG_in(int ke_socket)
{
    int count = 0;
    while (count < 3)
    {
        user.type = LOGIN;
        printf("请输入您的UID:\n");
        scanf("%d", &user.UID);
        printf("请输入密码:\n");
        getchar();
        scanf("%s", user.password);
        write(ke_socket, &user, sizeof(user));
        usleep(1000);
        read(ke_socket, &user, sizeof(user));
        if (user.log_flag == 1)
        {
            printf("登陆成功!\n");
            menu2(ke_socket);
            return;
        }
        else
        {
            count++;
            if (count < 3)
            {
                printf("帐号或密码错误,请重新输入!\n");
            }
            else
            {
                printf("输入错误次数达到上限,返回菜单\n");
                return;
            }
        }
    }
}

void menu2(int ke_socket)
{
    while (1)
    {

        printf("****登陆成功****\n");
        printf("1.私聊 2.群聊 3.注销 4.登出\n");
        printf("请选择:");
        int n = 0;
        scanf("%d", &n);
        switch (n)
        {
        case 1:
            si_chat(ke_socket);
            break;
        case 2:
            qun_chat(ke_socket);
            break;
        case 3:
            printf("注销\n");
            user.log_flag = 0;
            user.type = DELETE_ID;
            write(ke_socket, &user, sizeof(user));
            break;
        case 4:
            printf("登出\n");
            user.log_flag = 0;
            user.type = LOG_out;
            write(ke_socket, &user, sizeof(user));
            return;
        default:
            printf("输入有误!\n");
            continue;
        }
    }
}

void qun_chat(int ke_socket)
{
    // 创建子线程接收数据
    pthread_t pd;
    pthread_create(&pd, NULL, my_qun_threadfunc, &ke_socket);
    user.type=QUN;
    while (1)
    {
        printf("发给群聊:\n");
        scanf("%s", user.send_message);
        if (strcmp(user.send_message, "quit") == 0)
        {
            printf("用户已退出群聊\n");
            pthread_cancel(pd);
            break;
        }
        write(ke_socket, &user, sizeof(user));
    }
    return;
}

void look_time()
{
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    char time_str[20];
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
    printf("[%s]\n", time_str);
}

void *my_qun_threadfunc(void *arg) // 发送给客户端
{

    int client_socket = *(int *)arg;
    user.type = QUN;
    while (1)
    {
        read(client_socket, &user, sizeof(user));
        look_time();
        printf("%s向群聊发送:%s\n", user.nickname,user.send_message);
    }
    return NULL;
}

void si_chat(int ke_socket)
{

    // 创建子线程接收私聊信息
    user.type = SI;
    pthread_t pd;
    ret = pthread_create(&pd, NULL, my_sichat_func, &ke_socket);
    printf("请输入想要私聊的UID:\n");
    scanf("%d", &user.UID);
    int account = user.UID;
    while (1)
    {
        printf("私聊发送:\n");
        scanf("%s", user.send_message);
        if (strcmp(user.send_message, "quit") == 0)
        {
            printf("已退出群聊\n");
            pthread_cancel(pd);
            break;
        }
        user.UID = account;
        write(ke_socket, &user, sizeof(user));
    }
    return;
}

void *my_sichat_func(void *arg) // 私聊接收
{
    int client_socket = *(int *)arg;
    while (1)
    {
        read(ke_socket, &user, sizeof(user));
        look_time();
        printf("%s:%s\n", user.nickname, user.send_message);
    }
    return NULL;
}

5.复盘总结

1.TCP搭建流程不够熟悉。
2.对epoll的理解不够深入。
3.功能不够丰富。

标签:聊天室,socket,user1,int,void,printf,基于,user,Linux
From: https://blog.csdn.net/Jokerblank/article/details/139473203

相关文章

  • 嵌入式Linux系统编程 — 2.1 标准I/O库简介
    目录1标准I/O库简介1.1 标准I/O库简介1.2 标准I/O和文件I/O的区别2 FILE指针3标准I/O库的主要函数简介4 标准输入、标准输出和标准错误4.1标准输入、标准输出和标准错误概念4.2示例程序5 打开文件fopen()5.1 fopen()函数简介5.2 新建文件的权限5.3......
  • linux命令
    **ls[-a-l-h][路径]**(可以指定要查看的文件夹(目录)的内容,如果不给定参数,就查看当前工作目录的内容)-a选项,可以展示出隐藏的内容,以.开头的文件或文件夹默认被隐藏,需要-a才能显示出来-l选项,以列表的形式展示内容,并展示更多细节-h选项,需要和-l选项搭配使用,以更加人性化的......
  • 基于rtl8188eu的imx6ull开发板与Windows11实现无线网络ping通
    文章目录版本信息1.开发板网卡移植1.1.配置rtl8188eu驱动2.内核修改编译2.1.rtl8188eu固件配置2.2.USB、WIFI、IEEE802.11配置3.wifi工具移植3.1.wirelesstool移植3.2.wpa_supplicant移植4.根文件系统配置5.ping测试版本信息arm开发板:正点原子imx6ullalphav2......
  • 利用智普AI大模型进行基于 RAG 的表格数据问答
    前言最近一直在探索RAG相关的技术,刚好尝试了一些国产的大模型,发现智普的大模型用着还挺不错的,因此就尝试用它对表格数据进行问答。遇到的问题智普的SDK更新到了2.0的版本,这也就导致原来Langchain的版本无法适配了,需要重新自己写一些代码才可以。另外,Langchain提供的cr......
  • 查看Linux端口占用和开启端口命令
    查看端口的使用的情况lsof命令比如查看80端口的使用的情况lsof-itcp:80列出所有的端口netstat-ntlp查看端口的状态/etc/init.d/iptablesstatus开启端口以开启端口80为例。1用命令开启端口iptables-IINPUT-ptcp--dport80-jaccpet --写入要开放的端......
  • 基于AnolisOS 8.6的OpenVPN和GmSSLv2国密算法SSL VPN测试
    测试环境AnolisOS-8.6-x86_64-minimal.isoVirtualBox,2vCPU,4GRAM,40vDisk安装依赖yuminstall-ymakegcc编译安装GmSSLunzipGmSSL-master.zip**注:**由于许多系统有自带的ssl库,为避免潜在的动态库冲突,此处仅生成静态库./config--prefix=/usr/local/gmssl......
  • 基于修改iOS内核绕过iOS 基于svc 0x80的ptrace反调试
    yuzhouheike62天 看到一个帖子:[原创]绕过iOS基于svc0x80的ptrace反调试24.跟着操作了下.这篇文章的核心思想来源于[原创]iOS内核修改之过某音等PT_DENY_ATTACH反动态ptrace调试我的设备是:iphone7iOS14.1,DarwinKernelVersion20.0.0:WedSep3003:24:41......
  • Centos Stream 10 测试版下载:未来的RHEL10&Rocky Linux 10
    简介最近发现Centos最放出了Stream10测试版本,应该是基于Fedora40构建的。未来红帽会基于此版本构建RHEL10。内核版本:6.9.0Python版本:3.12.2RHEL系发行版对应关系Fedora(根发行版-软件实时更新-只支持一年)>>某一版本作为基准版本⏬CentosStream(长期稳定发行版-软......
  • 【忻州师范学院毕业论文】基于Java的家政公司网站的设计与实现
    注:仅展示部分文档内容和系统截图,需要完整的视频、代码、文章和安装调试环境请私信up主。1.1 开发背景及研究意义随着我国人口的增长、人们生活水平的提高,居民社会需求也随之增多,市场经济的快速发展和信息化水平的不断提高,人们的工作节奏也不断加快,许多人们没有闲暇的时间和......
  • Linux常用命令
    一、目录操作1、cd命令cda//进入a目录cdabc+tab键//如果有多个abc开头的目录,会显示相应的文件cd..//返回上一级目录cd../..//返回上上一级目录,以此类推cd/use/local//进入根目录下面的use/local目录cd—//返回上一次访问目录cd~//回到用户目录2、ls&l......