# 基于Linux平台TCP协议的多用户聊天室 **Repository Path**: lovefleuret/multi-user-chat-room ## Basic Information - **Project Name**: 基于Linux平台TCP协议的多用户聊天室 - **Description**: 熟悉网络编程,还有sqlite3数据库的基本操作 - **Primary Language**: Unknown - **License**: 0BSD - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-01-16 - **Last Updated**: 2024-02-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 多用户聊天室 ## 项目设计框图 ![image-20240218214702867](readme.assets/image-20240218214702867.png) ## 效果展示 ![image-20240220181311934](readme.assets/image-20240220181311934.png) ## 基本的TCP网络编程步骤 服务端: 1. 建立`socket` 2. 初始化`IP`,`PORT`,协议簇 3. `bind`套接字 4. `listen`套接字 5. `accept`客户端的请求 6. 消息处理 7. 关闭`socket` 客户端: 1. 建立`socket` 2. 初始化服务器`IP`,`PORT`,协议簇 3. `connect`服务端 4. 消息处理 5. 关闭`scoket` ## 创建线程部分 创建多少个通过`MAXCLIENT`这个宏来改,不要改太多😊除非你的电脑顶得住。 ```c pthread_t client_tid[MAXCLIENT]; for (int i = 0; i < MAXCLIENT; i++) { pthread_create(&client_tid[i], NULL, client_handler, NULL); } for (int i = 0; i < MAXCLIENT; i++) { pthread_join(client_tid[i], NULL); } ``` ## `sqlite3`的基本操作 通过`sqlite3`数据库来保存客户端的信息 1. ```c int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); //创建sqlite3数据库文件 ``` 2. ```c int sqlite3_exec( sqlite3*, /* 一个SQLite数据库连接 */ const char *sql, /* 需要执行的SQL语句 */ int (*callback)(void*,int,char**,char**), /* 回调函数 */ void *, /* 传入到回调函数的第一个参数 */ char **errmsg /* 错误信息 */ ); // 这个接口比较全能,可以执行sqlite3的所有语句 ``` 3. ```c int sqlite3_prepare_v2( sqlite3 *db, /* 数据库对象 */ const char *zSql, /* 需要被预处理的SQL语句 */ int nByte, /* 在上述SQL语句上最多需要预处理的字节数 */ sqlite3_stmt **ppStmt, /* 输出参数:预处理后的语句对象 */ const char **pzTail /* 输出参数:指向SQL字符串中未被使用的部分 */ ); //用于对一条SQL语句进行预处理(即编译),并返回一个sqlite3_stmt对象,这样可以多次执行该sql语句,提高效率。 ``` 4. ```c int sqlite3_step(sqlite3_stmt*); // 配合sqlite3_prepare_v2使用,用来执行编译后的sqlite3语句 ``` 5. ```c int sqlite3_column_int(sqlite3_stmt* stmt, int iCol); // 使用sqlite3_step 来获取数据库中某一列的值 integer类型的数据 ``` 6. ```c int sqlite3_column_text(sqlite3_stmt* stmt, int iCol) // 同上,用来获取这个text 类型的数据 ``` ## 群聊部分 ```c else { //! 群聊 取出数据库中的所有fd,然后发送消息 stmt = NULL; char *client_name = NULL; // 通过fd查到对应的name memset(sql, 0, sizeof(sql)); sprintf(sql, "select name from chatlog where fd = %d", clientfd); if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) { perror("sqlite3_prepare_v2"); return NULL; } pthread_mutex_lock(&mutex); while (sqlite3_step(stmt) == SQLITE_ROW) { const unsigned char *column_text = sqlite3_column_text(stmt, 0); client_name = malloc(strlen((const char *)column_text) + 3); // Add space for ": " and the null character. strcpy(client_name, (const char *)column_text); strcat(client_name, ": "); } pthread_mutex_unlock(&mutex); stmt = NULL; memset(sql, 0, sizeof(sql)); sprintf(sql, "select fd from chatlog where fd != %d", clientfd); if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) { perror("sqlite3_prepare_v2"); return NULL; } pthread_mutex_lock(&mutex); while (sqlite3_step(stmt) == SQLITE_ROW) { int fd = sqlite3_column_int(stmt, 0); // fd is the first column (index is 0) printf("---------fd: %d-----------\r\n", fd); int ret1 = send(fd, client_name, strlen(client_name), 0); send(fd, buf, strlen((const char *)buf), 0); // Send the actual number of characters in buf, not sizeof(buf). if (ret1 > 0) { printf("send success\r\n"); } } pthread_mutex_unlock(&mutex); free(client_name); } ``` ## 私聊部分 ```c //! 添加私聊功能,格式为 @name: msg if (buf[0] == '@') { int i = 0; while(buf[i] != ':') { i++; } buf[i] = '\0'; char *name = buf + 1; char *buf_private = buf + i + 1; memset(sql, 0, sizeof(sql)); sprintf(sql, "select fd from chatlog where name = '%s'", name); if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) { perror("sqlite3_prepare_v2"); return NULL; } pthread_mutex_lock(&mutex); while (sqlite3_step(stmt) == SQLITE_ROW) { int fd = sqlite3_column_int(stmt, 0); // fd is the first column (index is 0) printf("---------fd: %d-----------\r\n", fd); int ret1 = send(fd, buf_private, strlen((const char *)buf_private), 0); // Send the actual number of characters in buf, not sizeof(buf). if (ret1 > 0) { printf("send success\r\n"); } } pthread_mutex_unlock(&mutex); ```