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

C语言多人聊天室 ---s(服务端)

head.h

#ifndef __HEAD_H
#define __HEAD_H

// 常用头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 网络编程涉及的头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#include <sys/types.h>

// 本机字节序和网络字节序转换相关函数的头文件
#include <arpa/inet.h>

// 关闭套接字用close函数需要的头文件
#include <unistd.h>

//线程相关的函数头, mutex相关的函数
#include <pthread.h>

// 类型重命名:地址结构体的规范
typedef struct sockaddr SockAddr;

// 地址结构体的规范的实现结构体
typedef struct sockaddr_in SockAddrIn;


// if_nametoindex
#include <net/if.h>

#include <sys/select.h>
#include <sys/time.h>

// JSON字符串的封装
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 文件操作需要的头文件
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "cJSON-master/cJSON.h"
#include <fcntl.h>
#include <mysql/mysql.h>

#include "TCPserver.h"
#include "sql.h"
#include "json.h"

// 定义外部变量
//int sockfdNUM[10] = {0};

#endif

json.h

#ifndef __JSON_H
#define __JSON_H

#include "head.h"
#include "cJSON-master/cJSON.h"
typedef struct // 设置字节不对其齐
{
    char option[2];
    char name[20];
    char pass[20];
} __attribute__((packed)) data;

// 解析json为data类型
data *login_user(char *buf);

// 把数据打包成json格式
char *packingJsontoString(int s_sockfd, char *option, char *name, char *password);

#endif

json.c

#include "json.h"


// typedef struct // 设置字节不对其齐
// {
//     char option;
//     char name[20];
//     char pass[20];
// } __attribute__((packed)) data;

// 把json格式的字符串解析成data结构体
data *login_user(char *buf)
{
    data *tmp = (data *)malloc(sizeof(data));
    // 1.调用cJSON库中的cJSON_Parse函数解析json格式的字符串
    cJSON *root = cJSON_Parse(buf);
    if (root == NULL)
    {
        puts("login_user:解析失败");
        // 若解析失败,跳转到结束部分进行资源清理
        /// goto end;
    }

    // 从根对象中获取名为 "name" 的JSON项
    cJSON *name = cJSON_GetObjectItem(root, "name");
    // 检查 "name" 项是否存在,是否为字符串类型,且字符串值不为空
    if (name != NULL && cJSON_IsString(name) && name->valuestring != NULL)
    {
        // 打印 "name" 项的值
        printf("name: %s\n", name->valuestring);
        strcpy(tmp->name, name->valuestring);
    }

    // 从根对象中获取名为 "password" 的JSON项
    cJSON *password = cJSON_GetObjectItem(root, "password");
    // 检查 "password" 项是否存在,是否为字符串类型,且字符串值不为空
    if (password != NULL && cJSON_IsString(password) && password->valuestring != NULL)
    {
        // 打印 "password" 项的值
        printf("password: %s\n", password->valuestring);
        strcpy(tmp->pass, password->valuestring);
    }

    // 从根对象中获取名为 "option" 的JSON项
    cJSON *option = cJSON_GetObjectItem(root, "option");
    // 检查 "password" 项是否存在,是否为字符串类型,且字符串值不为空
    if (option != NULL && cJSON_IsString(option) && option->valuestring != NULL)
    {
        // 打印 "option" 项的值
        printf("option: %s\n", option->valuestring);
        strcpy(tmp->option, option->valuestring);
    }

    

    // 释放cJSON对象占用的内存
    cJSON_Delete(root);
    return tmp;
}

// 把数据打包成json格式
char *packingJsontoString(int s_sockfd, char *option, char *name, char *password)
{
    // 1.创建json对象
    cJSON *root = cJSON_CreateObject(); // 创建根JSON对象
    if (root == NULL)
    {
        puts("packingJsontoString:创建json对象失败");
        return NULL; // 若创建失败,返回NULL
    }

    cJSON *nameItem = cJSON_CreateString(name); 
    cJSON_AddStringToObject(root, "name", name);

    cJSON *passwordInfo = cJSON_CreateString(password); 
    cJSON_AddStringToObject(root, "password", password);

    cJSON *optionInfo = cJSON_CreateString(option); 
    cJSON_AddStringToObject(root, "option", option);

    // 将封装的json转换成字符串,测试打印
    char *all_data = cJSON_Print(root); // 将JSON对象转换为格式化字符串
    puts(all_data);

    // 转存数据
    char *str = (char *)malloc(strlen(all_data) + 1); // 为新字符串分配内存,注意+1用于存储字符串结束符
    strcpy(str, all_data);                            // 复制字符串

    send(s_sockfd, str, strlen(all_data) + 1, 0);

    // 清理内存
    cJSON_Delete(root); // 删除根JSON对象,释放内存
    free(all_data);     // 释放cJSON_Print分配的内存
    puts("打包成功");
    return str;         // 返回封装好的JSON字符串
}

main.c

#include "head.h"

// 宏定义id地址
#define IP_PATH "192.168.118.129"
// 宏定义端口号
#define PORT 8888

int main(int argc, char const *argv[])
{
    // 创建线程id
    pthread_t tid1;
    // 设置线程分离状态
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    // 1.创建socket
    int s_sockfd = socket_init(IP_PATH, PORT);
    if (s_sockfd == -1)
    {
        printf("main:socket创建失败\n");
        return -1;
    }
    puts("main:socket创建成功");
    
    // 2.循环监听客户端连接
    while (1)
    {
        // 2.1 建立连接
        int c_sockfd = socket_accept(s_sockfd);
        if (c_sockfd == -1)
        {
            printf("main:socket_accept建立连接失败\n");
            return -1;
        }
        puts("main:socket_accept建立连接成功");
        // 2.2 创建线程
        pthread_create(&tid1, &attr, register_service, &c_sockfd);
    }
    // 销毁线程
    pthread_attr_destroy(&attr);

    return 0;
}

sql.h

#ifndef __SQL_H
#define __SQL_H

#include "head.h"


// 数据库连接函数
MYSQL *mysql_connect();

// 数据库查询函数
MYSQL_RES *mysql_query_m(MYSQL *conn, char *sql);

// 数据库增、删、改
int mysql_update(MYSQL *conn, char *sql);

// 数据库输出函数
void mysql_print(MYSQL_RES *res);

// 数据库断开连接函数
void mysql_disconnect(MYSQL *conn);

// 查看个人信息
int check_info(char *sql,char *buf);

// 修改个人信息函数
int modify_info(char *sql);

// 修改密码
int modify_pass(char *sql);

// 添加好友(执行没有输出结果的sql语句均可)
int add_friend(char *sql);

// 查询信息执行函数(一般查询通用)
int query_info(char *sql,char *buf);

// 查询某条信息是否存在
int query_info_is(char *sql);

//获取某一个字段的值
char *get_field_value(char *sql,char *buf);

// 查询聊天记录
int query_chat(char *sql,char *buf);



#endif

sql.c

#include "sql.h"

// 数据库连接函数
MYSQL *mysql_connect()
{
    // 1.初始化数据库连接对象
    MYSQL *conn = mysql_init(NULL);
    if (NULL == conn)
    {
        perror("数据库连接mysql_connect:mysql_init失败");
        return NULL;
    }
    puts("数据库连接mysql_connect:mysql_init 初始化成功!");
    // 2.连接数据库                   ip地址             用户名  密码   数据库名  端口号    认证方式    标志位
    conn = mysql_real_connect(conn, "192.168.118.129", "sgc", "sgc", "chat", 0, NULL, 0);
    if (NULL == conn)
    {
        perror("数据库连接mysql_connect:mysql_real_connect失败");
        return NULL;
    }
    puts("数据库连接mysql_connect:mysql_real_connect 连接成功!");

    // 设置客户端连接字符集为 UTF - 8
    if (mysql_set_character_set(conn, "utf8mb4") != 0)
    {
        fprintf(stderr, "数据库连接mysql_set_character_set() failed: %s\n", mysql_error(conn));
        mysql_close(conn);
        return NULL;
    }

    return conn;
}

// 数据库查询并输出函数
MYSQL_RES *mysql_query_m(MYSQL *conn, char *sql)
{
    // 1.执行sql语句
    int ret = mysql_query(conn, sql);
    if (0 != ret)
    {
        perror("数据库查询mysql_query:mysql_query失败");
        return NULL;
    }
    puts("数据库查询mysql_query:mysql_query 执行成功!");
    // 2.获取查询结果
    MYSQL_RES *res = mysql_store_result(conn);
    if (NULL == res)
    {
        puts("数据库查询mysql_query:mysql_store_result失败,无匹配数据");
        return NULL;
    }
    // 3.获取结果集的行数
    if (0 == mysql_num_rows(res))
    {
        puts("数据库查询mysql_query:mysql_num_rows 无匹配数据");
        return NULL;
    }
    puts("数据库查询mysql_query:mysql_store_result 获取结果成功!");

    // 3.返回结果
    return res;
}

// 数据库输出函数
void mysql_print(MYSQL_RES *res)
{
    puts("数据库输出函数mysql_print:开始输出结果!");
    // 1.获取结果集中的字段数
    int num_fields = mysql_num_fields(res);
    // 2.获取结果集中的行数
    int num_rows = mysql_num_rows(res);
    // 3.获取结果集中的字段信息
    MYSQL_FIELD *field = mysql_fetch_fields(res);
    // 4.获取结果集中的每一行数据
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        for (int i = 0; i < num_fields; i++)
        {
        }
    }
    puts("数据库输出函数mysql_print:mysql_fetch_row 获取每一行数据成功!");
}

// 数据库增删改
int mysql_update(MYSQL *conn, char *sql)
{
    // 1.执行sql语句
    int ret = mysql_query(conn, sql);
    if (0 != ret)
    {
        perror("数据库增删改mysql_update:mysql_query 失败");
        return -1;
    }
    puts("数据库增删改mysql_update:mysql_query 执行成功!");

    // 3.返回结果
    return 0;
}

// 数据库断开连接函数
void mysql_disconnect(MYSQL *conn)
{
    // 1.断开连接
    mysql_close(conn);
    puts("数据库断开连接mysql_disconnect:mysql_close 断开连接成功!");
}

// 查看个人信息
int check_info(char *sql, char *buf)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("查看个人信息check_info:mysql_connect 连接失败");
        return -1;
    }
    // 2.查询数据库
    MYSQL_RES *res = mysql_query_m(conn, sql);
    if (NULL == res)
    {
        puts("查看个人信息check_info:mysql_query_m 查询失败");
        return -1;
    }

    // 将结果拼接到buf中
    // 1.获取结果集中的字段数
    int num_fields = mysql_num_fields(res);
    // 2.获取结果集中的行数
    int num_rows = mysql_num_rows(res);
    // 3.获取结果集中的字段信息
    MYSQL_FIELD *field = mysql_fetch_fields(res);
    // 4.获取结果集中的每一行数据
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(res)) != NULL) // 遍历每一行数据
    {
        for (int i = 0; i < num_fields; i++) // 遍历每一行数据的每一个字段
        {
            printf("%s---%s\t", field[i].name, row[i]);
            // 将字段名和字段值拼接到buf中
            sprintf(buf, "%s%s---%s\t\n", buf, field[i].name, row[i]);
        }
    }
    // 3.断开连接
    mysql_disconnect(conn);
    return 0;
}

// 修改个人信息函数
int modify_info(char *sql)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("修改个人信息modify_info:mysql_connect 连接失败");
        return -1;
    }
    // 2.修改数据库
    int ret = mysql_update(conn, sql);
    if (0 != ret)
    {
        puts("修改个人信息modify_info:mysql_update 修改失败");
    }
    // 3.断开连接
    mysql_disconnect(conn);
    return 0;
}

// 修改密码
int modify_pass(char *sql)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("修改密码modify_pass:mysql_connect 连接失败");
        return -1;
    }
    // 2.修改数据库
    int ret = mysql_update(conn, sql);
    if (0 != ret)
    {
        puts("修改密码modify_pass:mysql_update 修改失败");
    }
    // 3.断开连接
    mysql_disconnect(conn);
    return 0;
}

// 添加好友
int add_friend(char *sql)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("添加好友add_friend:mysql_connect 连接失败");
        return -1;
    }
    // 2.添加好友
    int ret = mysql_update(conn, sql);
    if (0 != ret)
    {
        puts("添加好友add_friend:mysql_update 添加失败");
        return -1;
    }
    // 3.断开连接
    mysql_disconnect(conn);
    return 0;
}

// 查询信息执行函数
int query_info(char *sql, char *buf)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("查询信息query_info:mysql_connect 连接失败");
        return -1;
    }
    // 2.查询数据库
    MYSQL_RES *res = mysql_query_m(conn, sql);
    if (NULL == res)
    {
        puts("查询信息query_info:mysql_query_m 查询失败");
        return -1;
    }
    // 3.将结果拼接到buf中
    // 1.获取结果集中的字段数
    int num_fields = mysql_num_fields(res);
    // 2.获取结果集中的行数
    int num_rows = mysql_num_rows(res);
    // 3.获取结果集中的字段信息
    MYSQL_FIELD *field = mysql_fetch_fields(res);
    // 4.获取结果集中的每一行数据
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(res)) != NULL) // 遍历每一行数据
    {
        for (int i = 0; i < num_fields; i++) // 遍历每一行数据的每一个字段
        {
            printf("%s---%s\t", field[i].name, row[i]);
            // 将字段名和字段值拼接到buf中
            sprintf(buf, "%s %s\t\n", buf, row[i]);
        }
    }
    // 3.断开连接
    mysql_disconnect(conn);
    return 0;
}

// 查询某条信息是否存在
int query_info_is(char *sql)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("查询某条信息是否存在query_info_is:mysql_connect 连接失败");
        return -1;
    }
    // 执行 SQL 查询语句
    if (mysql_query(conn, sql) != 0)
    {
        // 若查询执行失败,输出失败的错误信息
        fprintf(stderr, "mysql_query() 执行查询失败: %s\n", mysql_error(conn));
        return -1;
    }

    // 获取查询结果集
    MYSQL_RES *res = mysql_store_result(conn);
    // 检查结果集是否获取成功
    if (res == NULL)
    {
        // 使用 mysql_field_count(conn) 来判断是正常的无结果集查询还是出现了错误
        if (mysql_field_count(conn) == 0)
        {
            // 若返回 0,说明查询没有返回结果集(如 INSERT、UPDATE 等操作)
            fprintf(stderr, "查询未返回结果集。\n");
        }
        else
        {
            // 否则表示获取结果集时出现错误,输出错误信息
            fprintf(stderr, "mysql_store_result() 获取结果集失败: %s\n", mysql_error(conn));
        }
        return -1;
    }

    // 获取结果集中的行数
    int row_count = mysql_num_rows(res);
    // 释放结果集占用的内存,避免内存泄漏
    mysql_free_result(res);

    // 返回结果集的行数
    return row_count;
}

// 获取某一个字段的值
char *get_field_value(char *sql, char *buf)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("获取某一个字段的值get_field_value:mysql_connect 连接失败");
        return NULL;
    }
    // 2.查询数据库
    MYSQL_RES *res = mysql_query_m(conn, sql);
    if (NULL == res)
    {
        puts("获取某一个字段的值get_field_value:mysql_query_m 查询失败");
        // 4.断开连接
        mysql_disconnect(conn);
        return NULL;
    }
    // 3.判断结果是否为空
    if (0 == mysql_num_rows(res))
    {
        puts("获取某一个字段的值get_field_value:mysql_num_rows 查询结果为空");
        // 4.断开连接
        mysql_disconnect(conn);
        return NULL;
    }
    // 4.获取结果集中的每一行数据
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(res)) != NULL) // 遍历每一行数据
    {
        for (int i = 0; i < mysql_num_fields(res); i++) // 遍历每一行数据的每一个字段
        {
            // 将字段值拼接到buf中
            sprintf(buf, "%s", row[i]);
            return buf;
        }
    }
}

// 查询聊天记录
int query_chat(char *sql, char *buf)
{
    // 1.连接数据库
    MYSQL *conn = mysql_connect();
    if (NULL == conn)
    {
        puts("查询聊天记录query_info:mysql_connect 连接失败");
        return -1;
    }
    // 2.查询数据库
    MYSQL_RES *res = mysql_query_m(conn, sql);
    if (NULL == res)
    {
        puts("查询聊天记录query_info:mysql_query_m 查询失败");
        return -1;
    }
    // 3.将结果拼接到buf中
    // 1.获取结果集中的字段数
    int num_fields = mysql_num_fields(res);
    // 2.获取结果集中的行数
    int num_rows = mysql_num_rows(res);
    // 3.获取结果集中的字段信息
    MYSQL_FIELD *field = mysql_fetch_fields(res);
    // 4.获取结果集中的每一行数据
    MYSQL_ROW row;
    int n = 1;
    while ((row = mysql_fetch_row(res)) != NULL) // 遍历每一行数据
    {
        for (int i = 0; i < num_fields; i++) // 遍历每一行数据的每一个字段
        {
            printf("%s---%s\t", field[i].name, row[i]);

            if (n == 1)
            {
                // 将字段名和字段值拼接到buf中
                sprintf(buf, "%s %s", buf, row[i]);
            }
            else if (n == 2)
            {
                // 将字段名和字段值拼接到buf中
                sprintf(buf, "%s---%s", buf, row[i]);
            }
            else if (n == 3)
            {
                // 将字段名和字段值拼接到buf中
                sprintf(buf, "%s:%s\n", buf, row[i]);
                n = 1;
                continue;
            }
            n++;
        }
    }
    // 3.断开连接
    mysql_disconnect(conn);
    return 0;
}

TCPserver.h

#ifndef __THREAD_POOL_H
#define __THREAD_POOL_H

// 引入头文件
#include "head.h"

// 定义一个套接字数据,存储所有连接的客户端
typedef struct sockaddr_in SockAddrIn;
// 定义一个结构体,存储客户端的信息最大存储10个
//int sockfdNUM[10] = {0};

//用户注册登录的结构体
typedef struct
{
    char option[2];
    char username[20];
    char password[20];
} __attribute__((packed)) User;

// 根据ip地址和端口号初始化socket
int socket_init(char *ip, int port);

// 监听客户端,建立连接
int socket_accept(int listenfd);

// 注册登录服务函数
void * register_service(void *args);

// 登录后的个人信息界面服务函数
int login_service(void *args);

// 登陆主界面用户选择
int login_main(void *args);

// 联系人服务函数
int contact_service(void *args);

// 好友申请处理函数
int friend_apply_service(int c_socket);

// 聊天界面函数
int chat_service(void *args);

// 私聊界面函数
int private_chat_service(void *args);

// 公屏聊天界面函数
int public_chat_service(void *args);


#endif

TCPserver.c

#include "TCPserver.h"

int sockfdNUM[40] = {0};

typedef struct
{
    int uid;
    int sockfd;
} UID_t;

typedef struct
{
    int uid;
    int sockfd;
} CHAT_t;

UID_t uid[40] = {0};
CHAT_t chat[20] = {0};
CHAT_t g_chat[20] = {0};

// 根据ip地址和端口号初始化socket
int socket_init(char *ip, int port)
{
    puts("进入初始化");
    // 1.创建socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        perror("初始化:socket失败");
        return -1;
    }
    puts("初始化:socket成功");
    // 2.绑定地址
    SockAddrIn serverAddr;
    // ipv4协议
    serverAddr.sin_family = AF_INET;
    // 端口
    serverAddr.sin_port = htons(port);
    // ip地址
    serverAddr.sin_addr.s_addr = inet_addr(ip);
    // 3.绑定地址
    int ret = bind(sockfd, (SockAddr *)&serverAddr, sizeof(serverAddr));
    if (-1 == ret)
    {
        perror("初始化:bind失败");
        return -1;
    }
    puts("初始化:bind成功");
    // 4.监听
    ret = listen(sockfd, 10);
    if (-1 == ret)
    {
        perror("初始化:listen失败");
    }
    puts("初始化:listen成功");

    // 返回监听的套接字
    return sockfd;
}

// 根据监听,建立连接
int socket_accept(int listenfd)
{
    puts("进入监听,连接");
    // 阻塞等待客户端的连接
    int c_socket = accept(listenfd, NULL, NULL);
    if (-1 == c_socket)
    {
        perror("监听,连接:accept失败");
        return -1;
    }
    puts("监听,连接:accept成功");

    // 存储客户端套接字描述符
    for (size_t i = 0; i < 10; i++)
    {
        if (0 == sockfdNUM[i])
        {
            sockfdNUM[i] = c_socket;
            puts("监听,连接:accept成功");
            break;
        }
    }

    // 测试打印
    printf("监听,连接:c_socket=%d\n", c_socket);
    // 获取客户端的ip地址和端口号
    SockAddrIn clientAddr;
    socklen_t len = sizeof(clientAddr);
    int ret = getpeername(c_socket, (SockAddr *)&clientAddr, &len);
    if (-1 == ret)
    {
        perror("监听,连接:getpeername失败");
        return -1;
    }
    puts("监听,连接:getpeername成功");
    printf("监听,连接:client ip=%s, port=%d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
    // 给客户端做出响应---发送连接成功的标识
    char *msg = "连接服务器成功";
    send(c_socket, msg, strlen(msg), 0);

    // 返回客户端的套接字
    return c_socket;
}

// 注册登录服务函数
void *register_service(void *args)
{
    
    puts("进入注册登录服务函数");
    // // 创建互斥锁
    // pthread_mutex_t mutex;
    // // 初始化互斥锁
    // pthread_mutex_init(&mutex, NULL);

    // 获取客户端的套接字
    int c_socket = *(int *)args;
    // 测试打印
    printf("注册登录服务函数:c_socket=%d\n", c_socket);
    // 接收客户端的数据
    char buf[1024] = {0};
    // 接收json数据
    char jsonString[1024] = {0};
    User *user;
    ssize_t n = -1;

    // 清理缓冲区
    memset(buf, 0, sizeof(buf));

    // 接收客户端的数据
    int stat = 0;
    recv(c_socket, &stat, sizeof(stat), 0);
    printf("%d\n", stat);

    if (stat == 1)
    {
        // 进入私聊服务函数,创建线程
        private_chat_service(&c_socket);
        goto end;
    }
    else if (stat == 2)
    {
        // 进入公屏聊天服务函数,创建线程
        public_chat_service(&c_socket);
        goto end;
    }

    while (1)
    {
        printf("注册登录服务函数c_socket:%d\n", c_socket);
        n = recv(c_socket, jsonString, sizeof(jsonString), 0);
        // 解析json数据
        user = (User *)login_user(jsonString);

        if (-1 == n) // 接收失败
        {
            perror("注册登录服务函数:recv失败");
            break;
        }
        else if (0 == n) // 客户端关闭连接
        {
            printf("注册登录服务函数:client %d closed\n", c_socket);
            break;
        }
        else // 接收成功
        {
            printf("注册登录服务函数:recv成功\n");
            printf("注册登录服务函数:recv name=%s , pass=%s\n", user->username, user->password);
            printf("注册登录服务函数:recv option=%s\n", user->option);
        }

        // 判断用户的选择
        if (strcmp(user->option, "1") == 0)
        {
            // 加锁
            //pthread_mutex_lock(&mutex);
            // 调用sql.c的数据库连接函数,连接数据库
            MYSQL *conn = mysql_connect();
            char sql[128] = {0};
            // 拼接sql语句
            // 注册用户
            
            sprintf(sql, "insert into user(username,userpass) values('%s','%s');", user->username, user->password);
            
            puts(sql);
            // 执行sql语句
            if (-1 == mysql_update(conn, sql))
            {
                // 注册失败
                // 发送消息给客户端
                send(c_socket, "注册失败", strlen("注册失败"), 0);
                // 关闭数据库
                mysql_disconnect(conn);
                continue;
            }

            // 注册成功,返回用户的UID
            mysql_query(conn, "SELECT * FROM user ORDER BY uid DESC;");
            // 获取结果集
            MYSQL_RES *res = mysql_store_result(conn);
            // 获取结果集的行数
            int row = mysql_num_rows(res);
            // 获取结果集的第一行第一列的数据
            MYSQL_ROW row_data = mysql_fetch_row(res);
            // 获取UID
            int uid = atoi(row_data[0]);
            printf("注册登录服务函数:uid=%d\n", uid);
            // 拼接UID和密码
            sprintf(buf, "注册成功,你的UID是:%d", uid);
            // 发送UID给客户端
            send(c_socket, buf, sizeof(buf), 0);
            // 拼接插入个人信息的sql语句
            // 清空sql
            memset(sql, 0, sizeof(sql));
            sprintf(sql, "insert into user_info(info_uid,info_name) values('%d','%s');", uid, user->username);
            // 执行sql语句
            if (-1 == mysql_update(conn, sql))
            {
                // 注册失败
                // 发送消息给客户端
                send(c_socket, "注册失败", strlen("注册失败"), 0);
                // 关闭数据库
                mysql_disconnect(conn);
                continue;
            }
            // 关闭数据库
            mysql_disconnect(conn);
            
            // 解锁
            //pthread_mutex_unlock(&mutex);

            continue;
        }
        else if (strcmp(user->option, "2") == 0)
        {
            // 加锁
            //pthread_mutex_lock(&mutex);
            // 检测UID和密码是否正确
            // 调用sql.c的数据库连接函数,连接数据库
            MYSQL *conn = mysql_connect();
            char sql[128] = {0};
            // 拼接sql语句
            // 检测UID和密码是否正确
            // 清空sql
            memset(sql, 0, sizeof(sql));
            sprintf(sql, "select * from user where uid='%d' and userpass='%s';", atoi(user->username), user->password);
            puts(sql);
            // 执行sql语句
            if (NULL == mysql_query_m(conn, sql))
            {
                // 登录失败
                // 发送消息给客户端
                send(c_socket, "登录失败,uid或密码不正确", strlen("登录失败,uid或密码不正确"), 0);
                // 关闭数据库
                mysql_disconnect(conn);
                continue;
            }
            // 检查该UID是否已经登录
            // 清空sql
            memset(sql, 0, sizeof(sql));
            sprintf(sql, "select * from user where uid='%d' and state='0';", atoi(user->username));
            puts(sql);
            // 执行sql语句
            if (NULL == mysql_query_m(conn, sql))
            {
                // 登录失败
                // 发送消息给客户端
                send(c_socket, "该用户处于在线状态", strlen("该用户处于在线状态"), 0);
                // 关闭数据库
                mysql_disconnect(conn);
                continue;
            }
            // 登录成功
            // 发送消息给客户端
            send(c_socket, "登录成功", strlen("登录成功"), 0);
            // 关闭数据库
            mysql_disconnect(conn);
            // 创建线程
            login_main(&c_socket);
            // 解锁
            //pthread_mutex_unlock(&mutex);
            break;
        }
        else if (strcmp(buf, "q") == 0) // 退出
        {
            // 退出
            return 0;
        }

        // 清空缓冲区
        memset(&user, 0, sizeof(user));
        memset(buf, 0, sizeof(buf));

        // 释放内存
        free(user);
        user = NULL;
    }

end:

    // 销毁互斥锁
    //pthread_mutex_destroy(&mutex);
    // 关闭客户端的套接字
    close(c_socket);
}

// 登陆主界面用户选择
int login_main(void *args)
{
    char ch;
    puts("进入用户主界面");

    UID_t tmp_uid;

    int c_socket = *(int *)args;
    // 测试打印
    printf("用户主界面:c_socket=%d\n", c_socket);

    // 接收客户端登录后发送的UID
    recv(c_socket, &tmp_uid.uid, sizeof(tmp_uid.uid), 0);
    // if (strlen(tmp_uid.uid) == 0)
    // {
    //     /* code */
    // }
    printf("用户主界面:uid=%d\n", tmp_uid.uid);
    
    // 将该UID的用户设置为登录状态
    // 拼接sql语句
    char sql[128] = {0};
    sprintf(sql, "update user set state='1' where uid='%d';", tmp_uid.uid);
    puts(sql);
    // 执行sql语句
    add_friend(sql);

    printf("用户主界面:uid=%d\n", tmp_uid.uid);

    // 接收客户端的套接字
    tmp_uid.sockfd = c_socket;

    int falg = 1;
    // 存储登录的客户端UID和套接字
    for (size_t i = 0; i < 20; i++)
    {
        if (0 == uid[i].uid)
        {
            uid[i] = tmp_uid;
            falg = 0;
            break;
        }
    }
    if (1 == falg)
    {
        printf("用户主界面:uid数组已满\n");
    }

    // 接收客户端的数据
    char buf[1024] = {0};
    ssize_t n = -1;
    while (1)
    {
        char option[50] = {0};
        n = recv(c_socket, option, sizeof(option), 0);
        if (-1 == n) // 接收失败
        {
            perror("用户主界面:recv失败");
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", tmp_uid.uid);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            break;
        }
        else if (0 == n) // 客户端关闭连接
        {
            printf("用户主界面:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", tmp_uid.uid);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            break;
        }
        else // 接收成功
        {
            printf("用户主界面:recv成功\n");
            printf("用户主界面:recv 选择=%s\n", option);
        }

        // 判断用户的选择
        if (strcmp(option, "1") == 0) // 查看个人信息
        {
            // 创建线程,进入个人信息主页面
            login_service(&c_socket);
        }
        else if (strcmp(option, "2") == 0) // 联系人
        {
            // 创建线程,进入联系人主页面
            contact_service(&c_socket);
        }
        else if (strcmp(option, "3") == 0) // 聊天
        {
            // 创建线程,进入聊天主页面
            chat_service(&c_socket);
        }
        else if (strcmp(option, "q") == 0) // 退出
        {
            // 关闭客户端的套接字
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", tmp_uid.uid);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            puts("登录主界面退出");
            close(c_socket);
            puts("c_socket:套接字关闭");
            return 0;
        }

        // 清空缓冲区
        memset(buf, 0, sizeof(buf));
    }

    // 情况sql
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "update user set state='0' where uid='%d';", tmp_uid.uid);
    puts(sql);
    // 执行sql语句
    add_friend(sql);
    puts("登录主界面退出");
    close(c_socket);
    puts("c_socket:套接字关闭");
    return 0;
}

// 登录后的个人信息界面服务函数
int login_service(void *args)
{
    puts("进入个人信息界面服务函数");

    int c_socket = *(int *)args;

    // 获取登录用户的UID
    int user_id = 0;
    for (size_t i = 0; i < 20; i++)
    {
        if (c_socket == uid[i].sockfd)
        {
            user_id = uid[i].uid;
            break;
        }
    }

    // 接收客户端的数据
    char buf[1024] = {0};
    ssize_t n = -1;
    while (1)
    {
        n = recv(c_socket, buf, sizeof(buf), 0);
        if (-1 == n) // 接收失败
        {
            perror("个人信息界面服务函数:recv失败");
            printf("个人信息界面服务函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            break;
        }
        else if (0 == n) // 客户端关闭连接
        {
            printf("个人信息界面服务函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            break;
        }
        else // 接收成功
        {
            printf("个人信息界面服务函数:recv成功\n");
            printf("个人信息界面服务函数:recv 选择=%s\n", buf);
        }
        // 判断用户的选择
        if (strcmp(buf, "1") == 0) // 查看个人信息
        {
            // 查看个人信息
            // 根据套接字获取UID
            int user_id = 0;
            for (size_t i = 0; i < 20; i++)
            {
                if (c_socket == uid[i].sockfd)
                {
                    user_id = uid[i].uid;
                    break;
                }
            }
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "SELECT * FROM user_info WHERE info_uid='%d';", user_id);
            puts(sql);
            // 调用查询个人信息的函数
            if (check_info(sql, buf) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "查看个人信息失败", strlen("查看个人信息失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, buf, strlen(buf), 0);
            continue;
        }
        else if (strcmp(buf, "2") == 0) // 修改个人信息
        {
            // 修改个人信息
            // 根据套接字获取UID
            int user_id = 0;
            for (size_t i = 0; i < 20; i++)
            {
                if (c_socket == uid[i].sockfd)
                {
                    user_id = uid[i].uid;
                    break;
                }
            }
            // 拼接sql语句
            char sql[512] = {0};
            // 接收客户端传来的数据
            recv(c_socket, buf, sizeof(buf), 0);
            // 拼接sql语句
            sprintf(sql, "%s WHERE info_uid='%d';", buf, user_id);
            puts(sql);
            // 调用修改个人信息的函数
            if (modify_info(sql) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "修改个人信息失败", strlen("修改个人信息失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, "修改个人信息成功", strlen("修改个人信息成功"), 0);
            continue;
        }
        else if (strcmp(buf, "3") == 0) // 修改密码
        {
            // 修改密码
            // 根据套接字获取UID
            int user_id = 0;
            for (size_t i = 0; i < 20; i++)
            {
                if (c_socket == uid[i].sockfd)
                {
                    user_id = uid[i].uid;
                    break;
                }
            }
            // 拼接sql语句
            char sql[512] = {0};
            // 接收客户端传来的数据
            recv(c_socket, buf, sizeof(buf), 0);
            // 拼接sql语句
            sprintf(sql, "%s'%d';", buf, user_id);
            puts(sql);
            // 调用修改密码的函数
            if (modify_pass(sql) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "修改密码失败", strlen("修改密码失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, "修改密码成功", strlen("修改密码成功"), 0);
            continue;
        }
        else if (strcmp(buf, "q") == 0) // 退出
        {
            return 0;
        }

        // 清空缓冲区
        memset(buf, 0, sizeof(buf));
    }
    puts("个人信息界面服务函数:退出");
}

// 联系人服务函数
int contact_service(void *args)
{
    puts("进入联系人函数");

    int c_socket = *(int *)args;

    int user_id = 0;
    for (size_t i = 0; i < 20; i++)
    {
        if (c_socket == uid[i].sockfd)
        {
            user_id = uid[i].uid;
            break;
        }
    }

    // 接收客户端的数据
    char buf[1024] = {0};
    ssize_t n = -1;
    while (1)
    {
        n = recv(c_socket, buf, sizeof(buf), 0);
        if (-1 == n) // 接收失败
        {
            perror("联系人函数:recv失败");
            printf("个人信息界面服务函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            break;
        }
        else if (0 == n) // 客户端关闭连接
        {
            printf("联系人函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            break;
        }
        else // 接收成功
        {
            printf("联系人函数:recv成功\n");
            printf("联系人函数:recv 选择===%s\n", buf);
        }

        // 判断用户的选择
        if (strcmp(buf, "1") == 0) // 查看好友列表
        {
            // 查看好友列表
            // 拼接sql语句
            char sql[512] = {0};
            sprintf(sql, "SELECT * FROM friends WHERE id='%d';", user_id);
            puts(sql);
            // 调用sql函数
            if (check_info(sql, buf) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "查看好友列表失败", strlen("查看好友列表失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, buf, strlen(buf), 0);
            continue;
        }
        else if (strcmp(buf, "2") == 0) // 添加好友
        {
            int id = 0;
            // 接收客户端传来的数据
            recv(c_socket, &id, sizeof(id), 0);
            // 获取自己的昵称
            char nick_name[50] = {0};
            // 拼接sql语句
            char sql[512] = {0};
            sprintf(sql, "SELECT username FROM user WHERE uid='%d';", user_id);
            puts(sql);
            // 调用查询个人信息的函数
            if (query_info(sql, nick_name) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "查看该uid信息失败", strlen("查看uid信息失败"), 0);
                continue;
            }
            printf("联系人函数:nick_name:%s\n", nick_name);
            // 清空sql缓冲区
            memset(sql, 0, sizeof(sql));
            // 拼接sql语句
            sprintf(sql, "INSERT INTO Friend_application(uid,f_uid,f_name) VALUES('%d','%d','%s');", id, user_id, nick_name);
            puts(sql);
            // 调用添加好友的函数
            if (add_friend(sql) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "好友申请发送失败", strlen("好友申请发送失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, "好友申请发送成功", strlen("好友申请发送成功"), 0);
            continue;
        }
        else if (strcmp(buf, "3") == 0) // 删除好友
        {
            // 删除好友
            // 接收客户端传来的数据
            recv(c_socket, buf, sizeof(buf), 0);
            // 拼接sql语句
            char sql[512] = {0};
            sprintf(sql, "DELETE FROM friends WHERE id='%d' AND Friends_id='%d';", user_id, atoi(buf));
            puts(sql);
            // 调用没有返回值的sql执行函数
            if (add_friend(sql) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "删除好友失败", strlen("删除好友失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, "删除好友成功", strlen("删除好友成功"), 0);
            continue;
        }
        else if (strcmp(buf, "4") == 0) // 好友申请
        {
            friend_apply_service(c_socket);
        }
        else if (strcmp(buf, "q") == 0) // 退出
        {
            // 创建线程,返回登陆后主界面
            return 0;
        }

        // 清空缓冲区
        memset(buf, 0, sizeof(buf));
    }
    puts("联系人界面退出");
}

// 好友申请处理函数
int friend_apply_service(int c_socket)
{
    puts("进入好友申请处理函数");
    int user_id = 0;
    for (size_t i = 0; i < 20; i++)
    {
        if (c_socket == uid[i].sockfd)
        {
            user_id = uid[i].uid;
            break;
        }
    }

    // 循环接收客户端数据
    char buf[1024] = {0};
    ssize_t n = -1;
    while (1)
    {
        n = recv(c_socket, buf, sizeof(buf), 0);
        if (-1 == n) // 接收失败
        {
            perror("好友申请处理函数:recv失败");
            printf("个人信息界面服务函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            return -1;
        }
        else if (0 == n) // 客户端关闭连接
        {
            printf("好友申请处理函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            return -1;
        }
        else // 接收成功
        {
            printf("好友申请处理函数:recv成功\n");
            printf("好友申请处理函数:recv 选择===%s\n", buf);
        }
        // 判断用户的选择
        if (strcmp(buf, "1") == 0) // 查看好友申请
        {
            // 查看好友申请
            // 拼接sql语句
            char sql[512] = {0};
            sprintf(sql, "SELECT f_uid,f_name FROM Friend_application WHERE uid='%d';", user_id);
            puts(sql);
            // 调用sql函数
            if (check_info(sql, buf) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "查看好友申请失败", strlen("查看好友申请失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, buf, strlen(buf), 0);
            continue;
        }
        else if (strcmp(buf, "2") == 0) // 同意好友申请
        {
            // 同意好友申请
            // 接收客户端传来的数据
            int id = 0;
            recv(c_socket, &id, sizeof(id), 0);
            // 拼接sql语句
            char sql[512] = {0};
            // 根据id删除好友申请
            sprintf(sql, "DELETE FROM Friend_application WHERE uid='%d' AND f_uid='%d';", user_id, id);
            puts(sql);
            // 调用没有返回值的sql执行函数
            if (add_friend(sql) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "同意好友申请失败", strlen("同意好友申请失败"), 0);
                continue;
            }
            // 清空sql缓冲区
            memset(sql, 0, sizeof(sql));
            // 根据id查询好友昵称
            char nick_name[50] = {0};
            // 拼接sql语句
            sprintf(sql, "SELECT username FROM user WHERE uid='%d';", id);
            puts(sql);
            // 调用查询个人信息的函数
            if (query_info(sql, nick_name) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "查看该uid信息失败", strlen("查看uid信息失败"), 0);
                continue;
            }
            // 清空sql缓冲区
            memset(sql, 0, sizeof(sql));
            // 拼接sql语句,添加好友
            sprintf(sql, "INSERT INTO friends(id,Friends_id,Friends_name) VALUES('%d','%d','%s');", id, user_id, nick_name);
            puts(sql);
            // 调用添加好友的函数,没有返回值的sql执行函数
            if (add_friend(sql) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "添加好友失败", strlen("添加好友失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, "添加好友成功", strlen("添加好友成功"), 0);
            continue;
        }
        else if (strcmp(buf, "3") == 0) // 拒绝好友申请
        {
            // 拒绝好友申请
            // 接收客户端传来的数据
            int id = 0;
            recv(c_socket, &id, sizeof(id), 0);
            // 拼接sql语句
            char sql[512] = {0};
            // 根据id删除好友申请
            sprintf(sql, "DELETE FROM Friend_application WHERE uid='%d' AND f_uid='%d';", user_id, id);
            puts(sql);
            // 调用没有返回值的sql执行函数
            if (add_friend(sql) != 0)
            {
                // 发送消息给客户端
                send(c_socket, "拒绝好友申请失败", strlen("拒绝好友申请失败"), 0);
                continue;
            }
            // 发送消息给客户端
            send(c_socket, "拒绝好友申请成功", strlen("拒绝好友申请成功"), 0);
            continue;
        }
        else if (strcmp(buf, "q") == 0) // 退出
        {
            return 0;
        }
        // 清空缓冲区
        memset(buf, 0, sizeof(buf));
    }
    puts("好友申请页面");
}

// 聊天界面函数
int chat_service(void *args)
{
    puts("进入聊天界面函数");
    int c_socket = *(int *)args;

    int user_id = 0;
    for (size_t i = 0; i < 20; i++)
    {
        if (c_socket == uid[i].sockfd)
        {
            user_id = uid[i].uid;
            break;
        }
    }

    // 循环接收客户端数据
    char buf[1024] = {0};
    ssize_t n = -1;
    while (1)
    {
        puts("开始下一次接收");
        n = recv(c_socket, buf, sizeof(buf), 0);
        if (-1 == n) // 接收失败
        {
            perror("聊天界面函数:recv失败");
            printf("个人信息界面服务函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            return 0;
        }
        else if (0 == n) // 客户端关闭连接
        {
            printf("聊天界面函数:client %d 关闭\n", c_socket);
            // 将该UID的用户设置为离线状态
            // 拼接sql语句
            char sql[128] = {0};
            sprintf(sql, "update user set state='0' where uid='%d';", user_id);
            puts(sql);
            // 执行sql语句
            add_friend(sql);
            return 0;
        }
        else // 接收成功
        {
            printf("聊天界面函数:recv成功\n");
            printf("聊天界面函数:recv 选择===%s\n", buf);
        }

        // 判断用户的选择
        if (strcmp(buf, "1") == 0) // 好友私聊
        {
            // 接收好友UID
            int f_id = 0;
            recv(c_socket, &f_id, sizeof(f_id), 0);
            // 拼接sql语句
            char sql[512] = {0};
            // 查询有没有该好友
            sprintf(sql, "SELECT * FROM friends WHERE (id='%d' AND Friends_id='%d') or (id='%d' AND Friends_id='%d');", user_id, f_id, f_id, user_id);
            puts(sql);
            // 调用查询信息是否存在的函数
            if (query_info_is(sql) == 1)
            {
                printf("好友存在\n");
                int i = 1;
                // 发送消息给客户端
                send(c_socket, &i, sizeof(i), 0);
                continue;
            }
            else
            {
                printf("好友不存在\n");
                int i = 0;
                // 发送消息给客户端
                send(c_socket, &i, sizeof(i), 0);
                continue;
            }

            continue;
        }
        else if (strcmp(buf, "2") == 0) // 好友群聊
        {
            continue;
        }
        else if (strcmp(buf, "q") == 0) // 退出
        {
            return 0;
        }
        // 清空缓冲区
        memset(buf, 0, sizeof(buf));
    }
    puts("聊天界面退出");
}

// 私聊界面函数
int private_chat_service(void *args)
{
    puts("进入私聊函数");
    // 接收客户端套接字
    int c_socket = *(int *)args;
    int f_socket = 0;
    // 接收用户UID
    int user_id, f_id;
    recv(c_socket, &user_id, sizeof(user_id), 0);

    // 存储该聊天用户的UID已经套接字
    for (size_t i = 0; i < 20; i++)
    {
        if (chat[i].sockfd == 0)
        {
            chat[i].sockfd = c_socket;
            chat[i].uid = user_id;
            break;
        }
    }

    printf("user_id:%d\n", user_id);
    // 接收好友UID
    recv(c_socket, &f_id, sizeof(f_id), 0);

    // 根据好友UID获取好友套接字
    for (size_t i = 0; i < 20; i++)
    {
        if (f_id == chat[i].uid)
        {
            f_socket = chat[i].sockfd;
            break;
        }
    }

    // 拼接sql语句,检测有没有和该好友的聊天记录
    char sql[512] = {0};
    sprintf(sql, "SELECT * FROM p_chat_history  WHERE (a_uid='%d' OR b_uid='%d') AND (a_uid='%d' OR b_uid='%d');", user_id, user_id, f_id, f_id);
    puts(sql);

    // 调用查询信息是否存在的函数
    int ret = query_info_is(sql);
    printf("ret:%d\n", ret);
    if (-1 == ret) // 查询为空,没有聊天记录
    {
        // 给客户端发送通知
        send(c_socket, "暂无聊天记录", strlen("暂无聊天记录"), 0);
        // 拼接sql语句,插入聊天记录
        sprintf(sql, "INSERT INTO p_chat_history(a_uid,b_uid,content) VALUES('%d','%d','标识');", user_id, f_id);
        puts(sql);
        // 调用没有返回值的sql执行函数
        if (add_friend(sql) != 0)
        {
            puts("插入聊天记录失败");
        }
    }
    else // 查询不为空,有聊天记录
    {
        // 拼接sql语句,返回聊天记录
        sprintf(sql, "SELECT f_uid,f_name,content FROM p_chat_history  WHERE (a_uid='%d' OR b_uid='%d') AND (a_uid='%d' OR b_uid='%d') AND content != '标识';", user_id, user_id, f_id, f_id);
        puts(sql);
        char buf[2048] = {0};
        // 查询聊天记录函数
        if (query_chat(sql, buf) != 0)
        {
            strcpy(buf, "暂无聊天记录");
            puts("查询聊天记录失败");
        }
        // 发送消息给客户端
        send(c_socket, buf, sizeof(buf), 0);
    }

    int flag = 0;

    char buf[2048] = {0};
    ssize_t n = -1;

    while (1)
    {
        // 获取自己的用户名
        // 拼接对应的sql语句
        // 清空sql
        char name[20] = {0};
        memset(sql, 0, sizeof(sql));
        sprintf(sql, "SELECT username FROM user WHERE uid='%d';", user_id);
        puts(sql);
        get_field_value(sql, name);
        puts(name);
        // 清除缓冲区
        memset(buf, 0, sizeof(buf));
        n = recv(c_socket, buf, sizeof(buf), 0);
        if (-1 == n) // 接收失败
        {
            perror("私聊函数:recv失败");
            return 0;
        }
        else if (0 == n) // 客户端关闭连接
        {
            // 拼接sql语句,修改为不可聊天状态,将chat_stat修改成0
            sprintf(sql, "UPDATE p_chat_history SET chat_stat = '0' WHERE (a_uid='%d' OR b_uid='%d') AND (a_uid='%d' OR b_uid='%d') AND content = '标识';", user_id, user_id, f_id, f_id);
            puts(sql);
            // 调用没有返回值的sql执行函数
            if (add_friend(sql) != 0)
            {
                puts("私聊状态修改失败");
            }
            return 0;
        }
        else // 接收成功
        {
            printf("私聊函数:recv成功\n");
            printf("私聊函数:recv 选择===%s\n", buf);
        }
        // 判断用户的选择
        if (strcmp(buf, "quit") == 0) // 私聊
        {
            continue;
        }

        puts(buf);
        // 清空sql
        memset(sql, 0, sizeof(sql));
        // 拼接sql语句,插入聊天记录
        sprintf(sql, "INSERT INTO p_chat_history(a_uid,b_uid,f_uid,f_name,content) VALUES('%d','%d','%d','%s','%s');", user_id, f_id, f_id, name, buf);
        puts(sql);
        // 调用没有返回值的sql执行函数
        if (add_friend(sql) != 0)
        {
            puts("插入聊天记录失败");
        }

        // 根据好友UID获取好友套接字
        for (size_t i = 0; i < 20; i++)
        {
            if (f_id == chat[i].uid)
            {
                f_socket = chat[i].sockfd;
                break;
            }
        }
        printf("===============f_socket:%d\n", f_socket);
        char tmp[2048] = {0};
        // 拼接发送的消息
        sprintf(tmp, "%d---%s:%s", user_id, name, buf);
        // 给好友发送消息
        send(f_socket, tmp, strlen(tmp), 0);
        // 清除缓冲区
        memset(buf, 0, sizeof(buf));
    }

    // 关闭自己的套接字
    close(c_socket);
    puts("私聊结束");
}

// 公屏聊天界面函数
int public_chat_service(void *args)
{
    puts("进入公屏聊天函数");
    // 接收客户端套接字
    int c_socket = *(int *)args;
    int f_socket = 0;
    // 接收用户UID
    int user_id;
    recv(c_socket, &user_id, sizeof(user_id), 0);

    // 存储该聊天用户的UID已经套接字
    for (size_t i = 0; i < 20; i++)
    {
        if (g_chat[i].sockfd == 0)
        {
            g_chat[i].sockfd = c_socket;
            g_chat[i].uid = user_id;
            break;
        }
    }

    printf("user_id:%d\n", user_id);

    char buf[2048] = {0};
    ssize_t n = -1;
    char sql[512] = {0};

    while (1)
    {
        // 获取自己的用户名
        // 拼接对应的sql语句
        // 清空sql
        char name[20] = {0};
        memset(sql, 0, sizeof(sql));
        sprintf(sql, "SELECT username FROM user WHERE uid='%d';", user_id);
        puts(sql);
        get_field_value(sql, name);
        puts(name);

        n = recv(c_socket, buf, sizeof(buf), 0);
        if (-1 == n) // 接收失败
        {
            perror("公屏聊天函数:recv失败");
            return 0;
        }
        else if (0 == n) // 客户端关闭连接
        {
            // 调用没有返回值的sql执行函数
            if (add_friend(sql) != 0)
            {
                puts("私聊状态修改失败");
            }
            return 0;
        }
        else // 接收成功
        {
            printf("私聊函数:recv成功\n");
            printf("私聊函数:recv 选择===%s\n", buf);
        }
        // 判断用户的选择
        if (strcmp(buf, "quit") == 0) // 私聊
        {
            continue;
        }

        puts(buf);
        char tmp[2048] = {0};
        // 拼接发送的消息
        sprintf(tmp, "%d---%s:%s", user_id, name, buf);

        // 获取所有进入聊天界面的套接字
        for (size_t i = 0; i < 20; i++)
        {
            if (g_chat[i].sockfd != 0)
            {
                // 给好友发送消息
                send(g_chat[i].sockfd, tmp, strlen(tmp), 0);
            }
        }
        // 清除缓冲区
        memset(buf, 0, sizeof(buf));
    }

    // 关闭自己的套接字
    close(c_socket);
    puts("公屏聊天结束");
}

makefile

SRCS = $(wildcard *.c) ./cJSON-master/cJSON.c
OBJS = $(patsubst *.c,*.o,$(SRCS))
CC = gcc
TARGET = app
LDFLAGS = -lpthread -lmysqlclient

.PHONY : clean

$(TARGET) : $(OBJS)
	$(CC)  $^ -o $@ $(LDFLAGS)
	
clean :
	rm *.o

run :
	./$(TARGET)

show :
	echo $(SRCS) / $(OBJS)


http://www.kler.cn/a/559720.html

相关文章:

  • 哈希表入门到精通:从原理到 Python 实现全解析
  • PcVue : 点亮马来西亚砂拉越偏远村庄
  • Windows 下 Visual Studio Code 常用快捷键指南
  • Docker部署中SQLite数据库同步问题解析
  • MacOS安装Emacs
  • Linux下基本指令(4)
  • 基于python+django的家教预约网站-家教信息管理系统源代码+运行步骤
  • C语言番外篇(3)------------>break、continue
  • Linux提权之metasploit 提权(五)
  • Seata1.5.2学习(二)——使用分布式事务锁@GlobalLock
  • Gin从入门到精通 (三)路由
  • [特殊字符] 蓝桥杯 Java B 组 之位运算(异或性质、二进制操作)
  • 在生产环境中部署和管理 PostgreSQL:实战经验与最佳实践
  • 如何在java中用httpclient实现rpc get请求
  • java实现二维码图片生成和编解码
  • 关于 BK3633 上电时受串口 UART2 影响而无法启动的问题说明
  • git常用指令详解
  • SQL写法技巧
  • vllm部署LLM(qwen2.5,llama,deepseek)
  • 【愚公系列】《鸿蒙原生应用开发从零基础到多实战》001-TypeScript概述‌