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 8888int main(int argc, char const *argv[])
{// 创建线程idpthread_t tid1;// 设置线程分离状态pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 1.创建socketint 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 - 8if (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.创建socketint 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;}// 注册成功,返回用户的UIDmysql_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);// 获取UIDint 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语句// 清空sqlmemset(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和密码是否正确// 清空sqlmemset(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是否已经登录// 清空sqlmemset(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);// 接收客户端登录后发送的UIDrecv(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));}// 情况sqlmemset(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;// 获取登录用户的UIDint 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) // 查看个人信息{// 查看个人信息// 根据套接字获取UIDint 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) // 修改个人信息{// 修改个人信息// 根据套接字获取UIDint 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) // 修改密码{// 修改密码// 根据套接字获取UIDint 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) // 好友私聊{// 接收好友UIDint 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;// 接收用户UIDint 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);// 接收好友UIDrecv(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语句// 清空sqlchar 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修改成0sprintf(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);// 清空sqlmemset(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;// 接收用户UIDint 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语句// 清空sqlchar 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 *.orun :./$(TARGET)show :echo $(SRCS) / $(OBJS)