网络编程项目(聊天室项目)

一、实现目标

一个在Linux下可以使用的聊天软件,要求至少实现如下功能:
1. 采用Client/Server架构
2. Client A 登陆聊天服务器前,需要注册自己的ID和密码
3. 注册成功后,Client A 就可以通过自己的ID和密码登陆聊天服务器
4. 多个Client X 可以同时登陆聊天服务器之后,与其他用户进行通讯聊天
5. Client A 成功登陆后可以查看当前聊天室内其他子啊先用户Client x
6. Client A 可以选择发消息给某个特定的 Client x,即“悄悄话”功能
7. Client A 可以选择发消息给全部的在线用户,即“群发消息”功能
8. Client A 在退出时需要保存聊天记录
9. Server 端维护一个所有登陆用户的聊天会的记录文件,以便查看

可以选择实现的附加功能:
1. Server 可以内建一个特殊权限的账号admin,用于管理聊天室
2. Admin 可以将某个Client X“踢出聊天室”
3. Admin 可以将某个Client X“设为只能旁听,不能发言”
4. Client 端发言增加表情符号,可以设置某些自定义的特殊组和来表达感情,如输入:),则会自动发送“XXX向大家做了个笑脸”
5. Client 段增加某些常用话语,可以对其中某些部分进行“姓名替换”,例如,输入/ClientA/welcome,则会自动发送“ClientA大侠,欢迎你来到咱们的聊天室”
Client.c源文件

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 

#define PORT  9999

char myName[20];        // 保存用户名
char msg1[1024];        // 保存聊天信息

sqlite3 * database;

int flag1 = 0;          // 线程退出的判断条件(不退出)
int flag2 = 0;          // 文件传输确认信号(无接收)
int flag3 = 0;          // 存在文件接收请求的判断(不存在)
int flag4 = 0;          // 本地存储是否被禁言(未被禁言)

// 协议
struct Msg
{
    char msg[1024];         // 消息内容
    int  cmd;               // 消息类型
    char filename[50];      // 保存文件名
    char toname[20];        // 接收者姓名
    char fromname[20];      // 发送者姓名
    int  sig;               // 用户状态(0:管理员、1:普通用户、2:被禁言)
};

struct Msg msg;     // 全局变量两个线程共享

// 注册/登录界面
void interface1()
{
    system("clear");
    printf ("\t*************************** 网络聊天室 *****************************\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                    1、 注册                                      *\n");                               
    printf ("\t*                    2、 登录                                      *\n");
    printf ("\t*                    q、 退出                                      *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                 BY szw           *\n");
    printf ("\t********************************************************************\n\n");
    printf ("\t***** 请输入命令: ");

}

// 普通用户界面
void interface2()
{
    system("clear");
    printf ("\t*************************** 网络聊天室 *****************************\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                    1、 查看当前在线人数                          *\n");                               
    printf ("\t*                    2、 进入群聊界面                              *\n");
    printf ("\t*                    3、 进入私聊界面                              *\n");
    printf ("\t*                    4、 查看聊天记录                              *\n");
    printf ("\t*                    5、 文件传输                                  *\n");
    printf ("\t*                    6、 更改密码                                  *\n"); 
    printf ("\t*                    7、 在线注销                                  *\n"); 
    printf ("\t*                    Q、 退出聊天室 返回登录界面                   *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                    BY szw        *\n");
    printf ("\t********************************************************************\n\n");
    printf ("\t***** 请输入命令: ");
}

// 管理员界面
void interface3()
{
    system("clear");
    printf ("\t*************************** 网络聊天室 *****************************\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                    1、 查看当前在线人数                          *\n");                               
    printf ("\t*                    2、 进入群聊界面                              *\n");
    printf ("\t*                    3、 进入私聊界面                              *\n");
    printf ("\t*                    4、 查看聊天记录                              *\n");
    printf ("\t*                    5、 文件传输                                  *\n");
    printf ("\t*                    6、 更改密码                                  *\n"); 
    printf ("\t*                    7、 在线注销                                  *\n"); 
    printf ("\t*                    8、 管理员界面                                *\n");  
    printf ("\t*                    Q、 退出聊天室 返回登录界面                   *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                                  *\n");
    printf ("\t*                                                    BY szw        *\n");
    printf ("\t********************************************************************\n\n");
    printf ("\t***** 请输入命令: ");
}

// 用来保存收到的聊天信息
void keep_msg(char * msg1)
{
    // 打开数据库
    int ret = sqlite3_open("Histroy.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("\t打开数据库失败\n");
        return;
    }

    // 往histroy表中添加信息
    char buf[100];
    char *errmsg = NULL;
    sprintf (buf, "insert into histroy values('%s','%s','%s')",msg.fromname,msg.toname,msg1);
    ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("\t数据库操作失败:%s\n", errmsg);
        return;
    }
}

// 接收文件
void receive()
{
    printf ("\n\t正在接收文件.....\n");
    int fd2 = open(msg.filename, O_WRONLY|O_CREAT, 0777);
    if (fd2 == -1)
    {
        perror ("open fd2");
        return;
    }
    write (fd2, msg.msg, 1023);
    close (fd2);
}

// 用来监听收到的信息
void *readMsg(void *v)
{

    int socketfd = (int)v;
    while(1)
    {
        if (flag1 == 1)         // 判断线程的退出条件
        {
            flag1 = 0;          // 重置线程退出条件
            pthread_exit(NULL);
        } 
        read (socketfd, &msg, sizeof(msg));

        switch(msg.cmd)
        {
            case 9001:       // 群聊
                sprintf (msg1, "收到一条来自%s的群消息:\n\t%s", msg.fromname,msg.msg);
                printf ("\n\n\t%s\n", msg1);
                printf ("\n\t回车键返回  \n");
                keep_msg (msg1);
                break;
            case 9002:       // 私聊
                printf ("\n\t%s 发来一条消息:\n\t%s\n", msg.fromname, msg.msg);
                sprintf (msg1,"%s 向 %s 发送一条信息:\n\t%s",msg.fromname, msg.toname, msg.msg);
                printf ("\n\t回车键返回  \n");
                keep_msg (msg1);
                break;
            case 9003:      // 处理发送失败
                sleep(3);
                printf ("\n\t用户不在线或不存在,发送失败\n");
                printf ("\n\t回车键返回  \n");
                break;
            case 9004:      // 是否存在文件接收确认信号
                printf ("\n\n\t收到一条信息,输入任一字符进行回复:");
                fflush(stdout);
                flag3 = 1;
                break;  
            case 9005:      // 文件传输请求被拒绝
                printf ("\n\t您发送的文件传输请求已被拒绝\n");
                printf ("\n\t回车键返回  \n");
                break;
            case 9006:      // 文件传输请求已通过
                printf ("\n\t您发送的文件传输请求已通过,请打开文件传输界面进行文件传输\n");
                printf ("\n\t回车键返回  \n");
                break;
            case 9007:      // 接收文件
                if (flag2 != 1)
                    printf ("\n\t已成功拦截 %s 给您发送的文件\n", msg.fromname);
                else
                    receive();
                break;
            case 9008:      // 文件传输完成   
                printf ("\n\t%s 给您发送的文件已全部接收完毕,请及时查看\n", msg.fromname);
                printf ("\n\t回车键返回  \n");
                flag2 = 0;  // 重置文件传输确认信号
                break;
            case 9009:      // 密码修改成功
                printf ("\n\t密码修改成功!\n");
                sleep(1);
                break;
            case 9010:      // 密码输入有误
                printf ("\n\t密码输入有误,修改失败\n");
                usleep(1500000);
                break;  
            case 9011:      // 收到禁言信号
                printf ("\n\t您已被管理员禁言,将无法发送群聊信息\n");
                printf ("\n\t回车键返回  \n");
                flag4 = 1;
                break;
            case 9012:      // 收到结除禁言信号
                printf ("\n\t您已被管理员解除禁言\n");
                printf ("\n\t回车键返回  \n");
                flag4 = 0;
                break;
            case 9013:      // 收到被踢出信号
                printf ("\n\t您已被管理员踢出,即将退出聊天室....\n");
                //sleep (2);
                flag1 = 1; 
                break;
        }
        usleep(400000);
    }
}

// 查看当前在线人数
void display (int socketfd)
{
    msg.cmd = 1;

    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求

    usleep(100000);
    printf ("\n\t当前在线人数为:%d\n", msg.cmd);
    printf ("\n\t回车键返回  \n");
    getchar();
}

// 退出聊天室,返回登录界面
void quit_chatroom (int socketfd)
{
    msg.cmd = 10;
    strcpy (msg.fromname, myName);
    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送退出信号
}

// 进入群聊界面
void chat1(int socketfd)
{
    if (flag4 == 1)
    {
        printf ("\n\t您已被管理员禁言,无法发送群聊信息...\n");
        return;
    }
    msg.cmd = 2;
    strcpy (msg.fromname, myName);
    strcpy(msg.toname, "all");

    printf ("\n\t请输入您要发送的内容:\n\t");
    scanf ("%s",msg.msg);
    getchar();

    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求

    printf ("\n\t发送完成,等待处理结果.....\n");
//  usleep (500000);
}

// 进入私聊界面
void chat2(int socketfd)
{   
    msg.cmd = 3;
    strcpy (msg.fromname, myName);

    printf ("\n\t请输入您要发送的对象:\n\t");
    scanf ("%s",msg.toname);
    getchar();

    printf ("\t请输入您要发送的内容:\n\t");
    scanf ("%s",msg.msg);
    getchar();

    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求

    printf ("\n\t发送完成,请稍候.....\n");
    usleep (500000);
}

// 打印群聊历史记录
void chat1_hst()
{
    system("clear");
    printf ("\t*************************** 网络聊天室 *****************************\n\n\n");
    printf ("\t群聊历史记录:                                                      \n");

    // 打开数据库
    int ret = sqlite3_open("Histroy.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("\t打开数据库失败\n");
        return;
    }

    // 获取histroy表中的信息
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from histroy";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    int i;
    for (i = 1+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], "all") == 0)
        {
            printf ("\n\t%s\n", resultp[i+1]);
        }
    }
    sqlite3_free_table(resultp);

    // 关闭数据库
    sqlite3_close(database);
}

// 打印私聊历史记录
void chat2_hst()
{
    system("clear");
    printf ("\t*************************** 网络聊天室 *****************************\n\n\n");
    printf ("\t私聊历史记录:\n");

    // 打开数据库
    int ret = sqlite3_open("Histroy.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("\t打开数据库失败\n");
        return;
    }

    // 获取histroy表中的信息
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from histroy";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    int i;
    for (i = 1+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], "all") != 0)
        {
            printf ("\n\t%s\n", resultp[i+1]);
        }
    }
    sqlite3_free_table(resultp);

    // 关闭数据库
    sqlite3_close(database);
}

// 查看聊天记录
void dis_histroy(int socketfd)
{
    printf ("\n\t a、查看群聊记录\n\t b、查看个人聊天记录\n\t");
    printf ("\n\t***** 请选择: ");
    switch (getchar())
    {
        case 'a':
            chat1_hst();
            break;
        case 'b':
            chat2_hst();
            break;
    }
    printf ("\n\t回车键返回  ");
    getchar();
}

// 确认传输对象
void convey_confirm(int socketfd)
{
    msg.cmd = 5;
    strcpy (msg.fromname, myName);

    printf ("\n\t请输入文件的传输对象:\n\t");
    scanf ("%s",msg.toname);
    getchar();

    printf ("\n\t传输请求发送完成,请稍等.....\n");
    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求
}

// 文件传输过程
void convey_chose(int socketfd)
{
    msg.cmd = 9007;
    strcpy (msg.toname, msg.fromname);
    strcpy (msg.fromname, myName);
    printf ("\n\t当前目录下的文件有: \n\n\t");
    fflush(stdout);
    system ("ls");
    printf ("\n\t请输入您要传送的文件名:  ");
    printf ("\n\t");
    scanf ("%s",msg.filename);
    getchar();

    // 打开要读的文件
    int fd1 = open(msg.filename, O_RDONLY);
    if (fd1 == -1)
    {
        perror ("open fd1");
        return;
    }
    int ret = 0;
    flag1 = 1;          // 关闭线程
    while (ret = read (fd1, msg.msg, 1023))
    {
        if (ret == -1)
        {
            perror("read");
            break;
        }
        printf ("\n\t正在传输文件,请稍候......\n");
        write (socketfd, &msg, sizeof(struct Msg));
        sleep(1);
    }
    msg.cmd = 9008;
    write (socketfd, &msg, sizeof(struct Msg));
    printf ("\n\t文件传输完成\n");
    close (fd1);

    // 重新开启一个线程
    pthread_t id;
    pthread_create(&id, NULL, readMsg, (void*)socketfd);
    pthread_detach(id); 

//  getchar();
}

// 文件传输界面
void convey(int socketfd)
{
    system("clear");
    printf ("\t*************************** 网络聊天室 *****************************\n\n\n");
    printf ("\t                     1、传输对象确认\n");
    printf ("\t                     2、选择文件\n");
    printf ("\n\t***** 请选择: ");
    char ch[2];
    fgets(ch, 2, stdin);
    while (getchar()!= '\n');
    switch (ch[0])
    {
        case '1':       // 确认传输对象
            convey_confirm(socketfd);
            break;
        case '2':       // 文件选择
            convey_chose(socketfd);
            break;

    }
    printf ("\n\t回车键返回  ");
    getchar();
}

// 更改密码
void change_pass(int socketfd)
{
    char ch[2];
    msg.cmd = 6;
    printf ("\n\t您是否确定需要修改密码?(y/n): ");
    fgets(ch, 2, stdin);
    while (getchar()!= '\n');
    if (ch[0] != 'y')
    {
        printf ("\n\t请稍候.....\n");
        usleep(700000);
        return;
    }
    printf ("\t请输入旧密码: ");
    scanf ("%s", msg.msg);
    getchar();
    printf ("\t请输入新密码: ");
    scanf ("%s", msg.filename);
    getchar();
    strcpy (msg.fromname, myName);

    write (socketfd, &msg, sizeof(struct Msg)); // 向服务器发送注册信息

    printf ("\n\t正在校验数据,请稍候......\n");
    sleep(1);
}

// 在线注销
void delete_user(int socketfd)
{
    msg.cmd = 8;

    printf ("\n\t正在处理注销操作......\n");
    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求

    sleep(1);
    printf ("\t注销完成!\n");
}

// 普通用户操作
void user_do (int socketfd)
{   
    char ch[2];
    while(1)
    {
        interface2();
        if (flag3 == 1)
        {
            printf ("\n\n\t%s 请求传输文件,是否接收?(y/n):", msg.fromname);
            fflush(stdout);
            fgets(ch, 2, stdin);
            while (getchar()!= '\n');
            if (ch[0] != 'y')
            {
                printf ("\n\t您已拒绝接受文件传输\n");
                printf ("\n\t回车键返回  \n");
            }
            else 
            {
                printf ("\n\t您已接受文件传输请求\n");
                printf ("\n\t回车键返回  \n");
                flag2 = 1;
            }
            if (flag2 == 0)
            {
                msg.cmd = 9005;                     // 不接受文件
                strcpy (msg.toname,msg.fromname);   // 修改信号发送对象
                strcpy (msg.fromname, myName);
                write (socketfd, &msg, sizeof(struct Msg)); 
            }
            else if (flag2 == 1)
            {
                msg.cmd = 9006;                     // 接受文件
                strcpy (msg.toname,msg.fromname);   // 修改信号发送对象
                strcpy (msg.fromname, myName);
                write (socketfd, &msg, sizeof(struct Msg)); 
            }
            flag3 = 0;      // 重置是否存在文件接收请求的判断
            flag2 = 0;
        }
        fgets(ch, 2, stdin);
        while (getchar()!= '\n');  
        switch(ch[0])
        {
            case '1':     // 查看当前在线人数
                display(socketfd);
                break;
            case '2':     // 进入群聊界面 
                chat1(socketfd);
                printf ("\n\t回车键返回  \n");
                getchar();
                break;
            case '3':     // 进入私聊界面 
                chat2(socketfd);
                printf ("\n\t回车键返回  \n");
                getchar();
                break;
            case '4':     // 查看聊天记录
                dis_histroy(socketfd);
                getchar();
                break;
            case '5':     // 文件传输
                convey(socketfd);
                break;
            case '6':     // 更改密码
                change_pass(socketfd);
                break;
            case '7':     // 在线注销
                printf ("\n\t是否确认注销?(y/n):   ");
                fgets(ch, 2, stdin);
                while (getchar()!= '\n');
                if (ch[0] != 'y')
                {
                    printf ("\n\t请稍候.....\n");
                    usleep(700000);
                    break;
                }
                delete_user(socketfd);
            case 'q':     // 退出聊天室 返回登录界面
                flag1 = 1;
                quit_chatroom(socketfd);
                printf ("\n\t正在退出,请稍候......\n");
                break;
        }
        if (flag1 == 1)   // 判断退出条件
        {
            break;
        }
        system("clear");

    }
}

// 将成员禁言
void silent (int socketfd)
{
    msg.cmd = 9011;
    strcpy (msg.fromname, myName);

    printf ("\n\t请输入您要禁言的用户:\n\t");
    scanf ("%s",msg.toname);
    getchar();

    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求

    printf ("\n\t操作完成,请稍候.....\n");
    usleep (500000);    
}

// 将成员解除禁言
void silent_del (int socketfd)
{
    msg.cmd = 9012;
    strcpy (msg.fromname, myName);

    printf ("\n\t请输入您要解除禁言的用户:\n\t");
    scanf ("%s",msg.toname);
    getchar();

    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求

    printf ("\n\t操作完成,请稍候.....\n");
    usleep (500000);        
}

// 将成员踢出聊天室
void kickout (int socketfd)
{
    msg.cmd = 9013;
    strcpy (msg.fromname, myName);

    printf ("\n\t请输入您要踢出的用户:\n\t");
    scanf ("%s",msg.toname);
    getchar();

    write (socketfd, &msg, sizeof(struct Msg));  //向服务器发送请求

    printf ("\n\t操作完成,请稍候.....\n");
    usleep (500000);        
}

// 管理员权限
void supuser (int socketfd)
{
    system("clear");
    printf ("\t*************************** 网络聊天室 *****************************\n\n\n");
    printf ("\t                     1、将成员禁言\n");
    printf ("\t                     2、将成员解除禁言\n");
    printf ("\t                     3、将成员踢出聊天室\n");
    printf ("\n\t***** 请选择: ");
    char ch[2];
    fgets(ch, 2, stdin);
    while (getchar()!= '\n');
    switch (ch[0])
    {
        case '1':       // 将成员禁言
            silent(socketfd);
            break;
        case '2':       // 将成员解除禁言
            silent_del(socketfd);
            break;          
        case '3':       // 将成员踢出聊天室
            kickout(socketfd);
            break;
    }
    printf ("\n\t回车键返回  ");
    getchar();  
}

// 管理员操作
void supuser_do (int socketfd)
{   
    char ch[2];
    while(1)
    {
        interface3();
        if (flag3 == 1)
        {
            printf ("\n\n\t%s 请求传输文件,是否接收?(y/n):", msg.fromname);
            fflush(stdout);
            fgets(ch, 2, stdin);
            while (getchar()!= '\n');
            if (ch[0] != 'y')
            {
                printf ("\n\t您已拒绝接受文件传输\n");
                printf ("\n\t回车键返回  \n");
            }
            else 
            {
                printf ("\n\t您已接受文件传输请求\n");
                printf ("\n\t回车键返回  \n");
                flag2 = 1;
            }
            if (flag2 == 0)
            {
                msg.cmd = 9005;                     // 不接受文件
                strcpy (msg.toname,msg.fromname);   // 修改信号发送对象
                strcpy (msg.fromname, myName);
                write (socketfd, &msg, sizeof(struct Msg)); 
            }
            else if (flag2 == 1)
            {
                msg.cmd = 9006;                     // 接受文件
                strcpy (msg.toname,msg.fromname);   // 修改信号发送对象
                strcpy (msg.fromname, myName);
                write (socketfd, &msg, sizeof(struct Msg)); 
            }
            flag3 = 0;      // 重置是否存在文件接收请求的判断
            flag2 = 0;
        }
        fgets(ch, 2, stdin);
        while (getchar()!= '\n');  
        switch(ch[0])
        {
            case '1':     // 查看当前在线人数
                display(socketfd);
                break;
            case '2':     // 进入群聊界面 
                chat1(socketfd);
                printf ("\n\t回车键返回  \n");
                getchar();
                break;
            case '3':     // 进入私聊界面 
                chat2(socketfd);
                printf ("\n\t回车键返回  \n");
                getchar();
                break;
            case '4':     // 查看聊天记录
                dis_histroy(socketfd);
                getchar();
                break;
            case '5':     // 文件传输
                convey(socketfd);
                break;
            case '6':     // 更改密码
                change_pass(socketfd);
                break;
            case '8':     // 管理员权限操作
                supuser (socketfd);
                break;
            case '7':     // 在线注销
                printf ("\n\t是否确认注销?(y/n):   ");
                fgets(ch, 2, stdin);
                while (getchar()!= '\n');
                if (ch[0] != 'y')
                {
                    printf ("\n\t请稍候.....\n");
                    usleep(700000);
                    break;
                }
                delete_user(socketfd);
            case 'q':     // 退出聊天室 返回登录界面
                flag1 = 1;
                quit_chatroom(socketfd);
                printf ("\n\t正在退出,请稍候......\n");
                break;
        }
        if (flag1 == 1)   // 判断退出条件
        {
            break;
        }
        system("clear");

    }
}

// 登录
void log_in(int socketfd)
{
    char password[20];
    msg.cmd = 2;
    printf ("\n\t用户登录:\n");
    printf ("\t请输入用户名: ");
    scanf ("%s", myName);
    getchar();
    printf ("\t请输入密码: ");
    scanf ("%s", password);
    getchar();
    strcpy (msg.fromname, myName);
    strcpy (msg.msg, password);

    write (socketfd, &msg, sizeof(struct Msg)); // 向服务器发送登录请求

    read (socketfd, &msg, sizeof(struct Msg));  // 读取服务器的登录回应

    printf ("\n\t正在校验数据......\n");
    sleep(1);
    if (msg.cmd == 1002)
    {
        printf ("\n\t验证通过,正在登录......\n");

        usleep(1500000);
        flag4 = msg.sig;        // 更新禁言状态

        // 线程分离,用来监听服务器返回信息
        pthread_t id;
        pthread_create(&id, NULL, readMsg, (void*)socketfd);
        pthread_detach(id);  

        user_do (socketfd);
    }
    else if (msg.cmd == 1003)
    {
        printf ("\n\t验证通过,正在登录......\n");

        usleep(1500000);

        // 线程分离,用来监听服务器返回信息
        pthread_t id;
        pthread_create(&id, NULL, readMsg, (void*)socketfd);
        pthread_detach(id);  

        supuser_do (socketfd);
    }   
    else if (msg.cmd == -4)
    {
        printf ("\n\t此账号已在别处登录\n");
    }
    else if (msg.cmd == -3)
    {
        printf ("\n\t验证失败,请您确认信息后重新登录\n");
    }
    else if (msg.cmd == -2)
    {
        printf ("\t验证失败,数据库打开失败\n");
    }
    else if (msg.cmd == -1)
    {
        printf ("\t数据库操作失败\n");
    }
    usleep(1500000);
}

// 注册(可注册管理员)
void reg(int socketfd)
{
    msg.cmd = 1;
    printf ("\t用户注册:\n");
    printf ("\t请输入用户名: ");
    scanf ("%s", myName);
    getchar();
    printf ("\t请输入密码: ");
    scanf ("%s", msg.msg);
    getchar();
    printf ("\t管理员: ");
    scanf ("%d", &msg.sig);
    getchar();  
    strcpy (msg.fromname, myName);

    write (socketfd, &msg, sizeof(struct Msg)); // 向服务器发送注册信息

    read (socketfd, &msg, sizeof(struct Msg));  // 读取服务器的注册回应

    printf ("\n\t正在校验数据......\n");
    sleep(1);
    if (msg.cmd == 1001)
    {
        printf ("\n\t注册成功!\n\t请稍候......\n");
    }
    else if (msg.cmd == -1)
    {
        printf ("\t注册失败,该用户名已被注册\n");
    }
    else if (msg.cmd == -2)
    {
        printf ("\t注册失败,数据库打开失败\n");
    }
    usleep(1500000);
}

// 向服务器发送请求
void ask_server(int socketfd)
{
    char ch[2];
    while (1)
    {
        interface1();
        fgets(ch, 2, stdin);
        while (getchar()!= '\n');
        switch(ch[0])
        {
            case '1':     // 注册
                reg(socketfd);
                break;
            case '2':     // 登录
                log_in(socketfd);
                break;
            case 'q':     // 退出
                exit(1);
        }

        system("clear");
    }
}

int main(int argc, char **argv)
{
    // 打开数据库Histroy.db
    int ret = sqlite3_open("Histroy.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return -1;
    }
    // 创建 histroy 表
    char *errmsg = NULL;
    char *sql = "create table if not exists histroy(fromname TEXT,toname TEXT,msg TEXT)";
    ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return -1;
    }
    // 关闭数据库
    sqlite3_close(database);

    // 创建与服务器通信的套接字
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd == -1)
    {
        perror ("socket");
        return -1;
    }

    // 连接服务器
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family  = AF_INET;     // 设置地址族
    addr.sin_port    = htons(PORT); // 设置本地端口
    inet_aton(argv[1],&(addr.sin_addr));


    // 连接服务器,如果成功,返回0,如果失败,返回-1
    // 成功的情况下,可以通过socketfd与服务器进行通信
    ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror ("connect");
        return -1;
    }
    printf ("成功连上服务器\n");

    ask_server(socketfd);

    // 关闭套接字
    close(socketfd);

    return 0;
}

Server.c源文件

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  

#define PORT  9999

sqlite3 * database;

// 协议
struct Msg
{
    char msg[1024];         // 消息内容
    int  cmd;               // 消息类型
    char filename[50];      // 保存文件名
    char toname[20];        // 接收者姓名
    char fromname[20];      // 发送者姓名
    int  sig;               // 用户状态(0:管理员、1:普通用户、2:被禁言)
};

// 初始化套接字,返回监听套接字
int init_socket()
{
    //1、创建socket
    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_socket == -1)
    {
        perror ("socket");
        return -1;
    }

    // 2、命名套接字,绑定本地的ip地址和端口
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family  = AF_INET;     // 设置地址族
    addr.sin_port    = htons(PORT); // 设置本地端口
    addr.sin_addr.s_addr = htonl(INADDR_ANY);   // 使用本地的任意IP地址

    int  ret = bind(listen_socket,  (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror ("bind");
        return -1;
    }

    // 3、监听本地套接字
    ret = listen(listen_socket, 5);
    if (ret == -1)
    {
        perror ("listen");
        return -1;
    }

    printf ("服务器已就绪,等待客户端连接.......\n");
    return listen_socket;
}

// 处理客户端连接,返回与连接上的客户端通信的套接字
int MyAccept(int listen_socket)
{
    // 4、接收连接
    struct sockaddr_in client_addr; // 用来保存客户端的ip和端口信息
    int len = sizeof(client_addr);
    int client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &len);
    if (client_socket == -1)
    {
        perror ("accept");
    }
    printf ("成功接收一个客户端: %s\n", inet_ntoa(client_addr.sin_addr));

    return client_socket;
}

// 查看当前在线人数
void display (int client_socket, struct Msg *msg)
{
    printf ("查看当前在线人数\n");

    // 确认flag参数
    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 与User表中信息进行比对
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    int count = 0;
    int i;
    for (i = 3+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], "1") == 0)
        {
            count++;
        }
    }
    // 返回在线人数
    msg->cmd = count;
    printf ("当前在线人数为:%d\n", msg->cmd);
    write (client_socket, msg, sizeof(struct Msg));
    sqlite3_free_table(resultp);
    // 关闭数据库
    sqlite3_close(database);
    printf ("操作完成,已关闭数据库\n");
}

// 退出聊天室,返回登录界面
void quit_chatroom (int client_socket, struct Msg *msg)
{
    printf ("%s 退出聊天室\n", msg->fromname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return;
    }
    char buf[100];
    char *errmsg = NULL;
    errmsg = NULL;
    sprintf (buf, "update user set flag = 0 where name = '%s'",msg->fromname);
    ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    sqlite3_close(database);
    printf ("登录状态修改完毕,已关闭数据库\n");
    write (client_socket, msg, sizeof(struct Msg));
}

// 客户端发送群聊消息
void chat1 (int client_socket, struct Msg *msg)
{
    printf ("%s 发了一条群消息\n",msg->fromname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 获取数据库中的flag参数信息
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    int i;
    for (i = 3+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        // 查询所有在线的用户
        if(strcmp(resultp[i], "1") == 0)
        {
            msg->cmd = 9001;    
            write (atoi(resultp[i-1]), msg, sizeof(struct Msg));    
        }
    }
    printf ("群消息已全部发送完成\n");
}

// 客户端发送私聊消息
void chat2 (int client_socket, struct Msg *msg)
{
    printf ("%s 向 %s 发了一条消息\n",msg->fromname,msg->toname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 获取数据库中的flag参数信息,判断是否在线
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            msg->cmd = 9002;
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));
            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
}

// 处理确认文件传输对象
void convey_confirm (int client_socket, struct Msg *msg)
{
    printf ("%s 向 %s 发送文件传输请求\n",msg->fromname,msg->toname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return;
    }

    // 获取数据库中的flag参数信息
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            msg->cmd = 9004;
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));
            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
}

// 用户不接受文件
void refuse (int client_socket, struct Msg *msg)
{
    printf ("%s 拒绝了 %s 的文件传输请求\n",msg->fromname,msg->toname);


    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return;
    }

    // 获取数据库中 toname 的套接字
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));
            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
}

// 用户接受文件
void accept_ (int client_socket, struct Msg *msg)
{
    printf ("%s 通过了 %s 的文件传输请求\n",msg->fromname,msg->toname);
    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return;
    }

    // 获取数据库中 toname 的套接字
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));
            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
}

// 处理文件传输
void convey_chose (int client_socket, struct Msg *msg)
{
    printf ("%s正在向%s传输文件......\n",msg->fromname,msg->toname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 获取数据库中的 flag 参数信息,判断是否在线
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    // 获取toname的套接字
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));
            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
}

// 文件传输完成
void convey_complete (int client_socket, struct Msg *msg)
{
    printf ("文件传输结束\n");

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 获取数据库中的 flag 参数信息,判断是否在线
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    // 获取toname的套接字
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));
            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
}

// 更改密码
void change_pass (int client_socket, struct Msg *msg)
{
    printf ("%s请求修改密码\n", msg->fromname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 与User表中信息进行比对
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from user";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->fromname)==0 && strcmp(resultp[i+1], msg->msg)==0)
        {
            // 返回确认信息
            msg->cmd = 9009;
            printf ("%s 验证通过\n", msg->fromname);
            write (client_socket, msg, sizeof(struct Msg));

            // 修改密码
            char buf[100];
            errmsg = NULL;
            sprintf (buf, "update user set password = '%s' where name = '%s'",msg->filename,msg->fromname);
            ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
            if (ret != SQLITE_OK)
            {
                printf ("数据库操作失败:%s\n", errmsg);
                return;
            }
            sqlite3_free_table(resultp);

            // 关闭数据库
            sqlite3_close(database);
            printf ("密码修改完成,已关闭数据库\n");
            return;
        }
    }   
    printf ("%s 验证不通过,密码输入有误\n", msg->fromname);
    msg->cmd = 9010;
    write (client_socket, msg, sizeof(struct Msg));
    sqlite3_free_table(resultp);
    // 关闭数据库
    sqlite3_close(database);
    printf ("操作完成,已关闭数据库\n");           

}

// 客户端请求在线注销
void delete_user (int client_socket, struct Msg *msg)
{
    printf ("即将处理用户注销\n");

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return;
    }

    // 删除 user 表中的信息
    char buf[100];
    char *errmsg = NULL;
    sprintf (buf, "delete from user where name = '%s'", msg->fromname);
    ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }

    // 关闭数据库
    sqlite3_close(database);
    printf ("删除成功,已关闭数据库\n");
}

// 处理禁言请求
void silent (int client_socket, struct Msg *msg)
{
    printf ("正在处理管理员 %s 对成员 %s 的禁言请求\n",msg->fromname,msg->toname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 获取数据库中的 flag 参数信息,判断是否在线
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    // 获取toname的套接字
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            msg->cmd =  9011;
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));

            char buf[100];
            errmsg = NULL;
            sprintf (buf, "update user set sig = 2 where name = '%s'",msg->toname);
            ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
            if (ret != SQLITE_OK)
            {
                printf ("数据库操作失败:%s\n", errmsg);
                return;
            }
            sqlite3_close(database);
            printf ("禁言状态修改完毕,已关闭数据库\n");

            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
    sqlite3_close(database);
    printf ("用户不在线,修改失败,已关闭数据库\n");
}

// 处理解除禁言请求
void silent_del (int client_socket, struct Msg *msg)
{
    printf ("正在处理管理员 %s 对成员 %s 的解除禁言请求\n",msg->fromname,msg->toname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 获取数据库中的 flag 参数信息,判断是否在线
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    // 获取toname的套接字
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            msg->cmd =  9012;
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));

            char buf[100];
            errmsg = NULL;
            sprintf (buf, "update user set sig = 1 where name = '%s'",msg->toname);
            ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
            if (ret != SQLITE_OK)
            {
                printf ("数据库操作失败:%s\n", errmsg);
                return;
            }
            sqlite3_close(database);
            printf ("禁言状态修改完毕,已关闭数据库\n");

            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
    sqlite3_close(database);
    printf ("用户不在线,修改失败,已关闭数据库\n"); 

}

// 处理踢出成员
void kickout (int client_socket, struct Msg *msg)
{
    printf ("正在处理管理员 %s 对成员 %s 的踢出请求\n",msg->fromname,msg->toname);

    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 获取数据库中的 flag 参数信息,判断是否在线
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from User";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return;
    }
    // 获取toname的套接字
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0)
        {
            msg->cmd =  9013;
            write (atoi(resultp[i+2]), msg, sizeof(struct Msg));

            char buf[100];
            errmsg = NULL;
            sprintf (buf, "update user set flag = 0 where name = '%s'",msg->toname);
            ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
            if (ret != SQLITE_OK)
            {
                printf ("数据库操作失败:%s\n", errmsg);
                return;
            }
            sqlite3_close(database);
            printf ("踢出完毕,已关闭数据库\n");

            return;
        }
    }
    msg->cmd = 9003;
    write (client_socket, msg, sizeof(struct Msg));
    sqlite3_close(database);
    printf ("用户不在线,修改失败,已关闭数据库\n"); 
}

// 处理用户操作请求函数
void user_do (int client_socket)
{
    struct Msg msg;
    int sig = 0;
    while(1)
    {
        // 从客户端读一个结构体数据
        int ret = read(client_socket, &msg, sizeof(msg));
        if (ret == -1)
        {
            perror ("read");
            break;
        }

        // 代表客户端退出
        if (ret == 0)
        {
            printf ("客户端返回登录界面\n");
            break;
        }

        switch (msg.cmd)
        {
            case 10:    // 退出聊天室,返回登录界面
                quit_chatroom(client_socket, &msg);
                sig = 1;
                break;
            case 1 :    // 查看当前在线人数
                display (client_socket, &msg);
                break;
            case 2 :    // 处理群聊消息 
                chat1 (client_socket, &msg);
                break;
            case 3 :    // 处理私聊消息 
                chat2 (client_socket, &msg);
                break;  
            case 5 :    // 处理确认文件传输对象
                convey_confirm (client_socket, &msg);
                break;
            case 6 :    // 更改密码
                change_pass (client_socket, &msg);
                break;
            case 8 :    // 处理在线注销
                delete_user (client_socket, &msg);
                break;
            case 9005 : // 用户不接受文件
                refuse (client_socket, &msg);
                break;
            case 9006 : // 用户接受文件
                accept_ (client_socket, &msg);
                break;
            case 9007 : // 处理文件传输
                convey_chose (client_socket, &msg);
                break;              
            case 9008 : // 文件传输完成
                convey_complete (client_socket, &msg);
                break;
            case 9011:  // 处理禁言请求
                silent (client_socket, &msg);
                break;
            case 9012:  // 处理解除禁言请求
                silent_del (client_socket, &msg);
                break;
            case 9013:  // 处理踢出成员
                kickout (client_socket, &msg);
                break;
        }
        if (sig == 1)
        {
            printf("即将退出普通用户操作请求函数\n");
            break;
        }
    }
}

// 处理客户端的登录请求
void log_in(int client_socket, struct Msg *msg)
{
    printf ("%s 请求登录\n", msg->fromname);

    // 将用户信息进行比对
    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 与User表中信息进行比对
    char *errmsg = NULL;
    char **resultp = NULL;
    int nrow, ncolumn;
    char *sql = "select * from user";
    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    int i;
    for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn)
    {
        if(strcmp(resultp[i], msg->fromname)==0 && strcmp(resultp[i+1], msg->msg)==0)
        {
            if (strcmp(resultp[i+3], "1") == 0)
            {
                msg->cmd = -4;
                printf ("%s 已经在别处登录\n", msg->fromname);
                write (client_socket, msg, sizeof(struct Msg));
                sqlite3_free_table(resultp);
                // 关闭数据库
                sqlite3_close(database);
                printf ("操作完成,已关闭数据库\n");   
                return;
            }
            if (strcmp(resultp[i+4], "0") != 0)
            {
                // 普通用户
                msg->cmd = 1002;
                msg->sig = atoi(resultp[i+4]);
                printf ("普通用户 %s 验证通过\n", msg->fromname);
                write (client_socket, msg, sizeof(struct Msg));
            }
            else 
            {
                // 管理员
                msg->cmd = 1003;
                msg->sig = atoi(resultp[i+4]);
                printf ("管理员 %s 验证通过\n", msg->fromname);
                write (client_socket, msg, sizeof(struct Msg));
            }           

            // 修改在线状态、更新套接字
            char buf[100];
            errmsg = NULL;
            sprintf (buf, "update user set socket = '%d',flag = 1 where name = '%s'",client_socket,msg->fromname);
            ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
            if (ret != SQLITE_OK)
            {
                printf ("数据库操作失败:%s\n", errmsg);
                msg->cmd = -1;
                write (client_socket, msg, sizeof(struct Msg));
                return;
            }
            sqlite3_free_table(resultp);

            // 关闭数据库
            sqlite3_close(database);
            printf ("在线状态已更新,已关闭数据库\n");

            printf ("进入用户操作请求处理功能\n");
            user_do (client_socket);

            return;
        }
    }   
    printf ("%s 验证不通过\n", msg->fromname);
    msg->cmd = -3;
    write (client_socket, msg, sizeof(struct Msg));
    sqlite3_free_table(resultp);
    // 关闭数据库
    sqlite3_close(database);
    printf ("操作完成,已关闭数据库\n");
}

// 处理客户端的注册请求
void reg(int client_socket, struct Msg *msg)
{
    printf ("%s 进行注册\n", msg->fromname);

    // 将用户进行保存
    // 打开数据库
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        msg->cmd = -2;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }

    // 往User表中添加信息
    char buf[100];
    char *errmsg = NULL;
    sprintf (buf, "insert into user values('%s','%s',%d,%d,%d)",msg->fromname,msg->msg,client_socket,0,msg->sig);
    ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        msg->cmd = -1;
        write (client_socket, msg, sizeof(struct Msg));
        return;
    }
    // 返回确认信息
    msg->cmd = 1001;
    printf ("%s 注册成功\n", msg->fromname);
    write (client_socket, msg, sizeof(struct Msg));
    // 关闭数据库
    sqlite3_close(database);
    printf ("操作完成,已关闭数据库\n");
}

// 线程的工作函数,即处理客户端请求的函数
void* hanld_client(void* v)
{
    int client_socket = (int)v;
    struct Msg msg;
    while(1)
    {
        printf("处理客户端请求的函数函数已就绪\n");
        // 从客户端读一个结构体数据
        int ret = read(client_socket, &msg, sizeof(msg));
        if (ret == -1)
        {
            perror ("read");
            break;
        }

        // 代表客户端退出
        if (ret == 0)
        {
            printf ("客户端退出\n");
            break;
        }

        switch (msg.cmd)
        {
            case 1 :    // 客户端进行注册
                reg(client_socket, &msg);
                break;
            case 2 :    // 客户端进行登录
                log_in(client_socket, &msg);
                break;
        }
    }

    close (client_socket);
}

int main()
{
    // 打开数据库User.db
    int ret = sqlite3_open("User.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return -1;
    }
    // 创建 user 表
    char *errmsg = NULL;
    char *sql = "create table if not exists user(name TEXT,password TEXT,socket INTEGER,flag INTEGER,sig INTEGER,primary key(name))";
    ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return -1;
    }
    printf ("数据库准备就绪......\n");
    // 关闭数据库
    sqlite3_close(database);

    // 初始化套接字
    int listen_socket = init_socket();

    while (1)
    {
        // 获取与客户端连接的套接字
        int client_socket = MyAccept(listen_socket);

        // 创建一个线程去处理客户端的请求,主线程依然负责监听
        pthread_t id;
        pthread_create(&id, NULL, hanld_client, (void *)client_socket);

        pthread_detach(id); // 线程分离
    }   
    close (listen_socket);

    return 0;
}



你可能感兴趣的:(实战项目,网络编程,clientserver,服务器,聊天,密码)