本文介绍: 聊天室系统聊天室系统设计一个聊天室系统时,会涉及到许多方面的知识简单项目结构,从不同的方面对聊天室设计进行分析。请注意,以下的结构只是一个示例,实际项目可能会更加复杂,具体要求取决于项目的规模和功能

聊天室系统
SQLite库
服务器
客户端

聊天室系统

设计一个聊天室系统时,会涉及到许多方面的知识。简单的项目结构,从不同的方面对聊天室设计进行分析。请注意,以下的结构只是一个示例,实际项目可能会更加复杂,具体要求取决于项目的规模和功能

项目结构

1. 服务器端(Server):
2. 客户端(Client):
3. 共享数据结构 (Common):

关键知识点

  1. 接字编程(Socket Programming):

  2. 多线程 (Multithreading):

  3. 文件传输 (File Transfer):

  4. 用户管理 (User Management):

  5. 聊天处理 (Chat Handling):

  6. 用户界面 (User Interface):

  7. Makefile:

项目的实现

  1. 服务器端(Server):

  2. 客户端(Client):

  3. 共享数据结构 (Common):

如何一步完善

  1. 数据存储:

  2. 安全性:

  3. 异常处理:

  4. 扩展性:

  5. 用户验证:

  6. 图形界面 (GUI):

以上只是一个简单的示例项目结构和知识点,实际项目中可能需要更多的细节优化。希望这个示例对你理解聊天室系统的设计有所帮助。如果有进一步问题或需要详细的解释,请随时提问

SQLite库

安装 SQLite3 库通常包括以下步骤

1. 安装 SQLite3 库

【SQLite】环境安装

在 Ubuntu 或 Debian 上:
sudo apt-get update
sudo apt-get install sqlite3 libsqlite3-dev
在 CentOS 或 RHEL 上:
sudo yum install sqlite sqlite-devel
macOS 上:
brew install sqlite

2. 编译链接 SQLite3 库

编译你的程序时,确保链接 SQLite3 库。在编译命令添加 -lsqlite3例如

gcc -o your_executable your_source_code.c -lpthread -lsqlite3

3. 使用 SQLite3 命令行工具

SQLite3 还提供了一个命令行工具,你可以用来管理查询 SQLite3 数据库。你可以通过以下方式运行它:

sqlite3

这些步骤应该能够满足你在使用 SQLite3 库时的基本需求。请注意,SQLite3 库是一个轻量级嵌入式数据库,无需独立数据库服务器。

服务器

实现了一个聊天室的功能选择函数,包含了不同的功能分支。以下是对每个功能分支的详细分析

  1. 整体结构和输入参数

  2. 变量声明初始化

  3. 退出群聊功能:

  4. 群发消息功能:

  5. 私聊功能:

  6. 群聊功能:

  7. 文件传输功能:

  8. 修改密码功能:

  9. 禁言、解除禁言、踢人功能:

    • head.flag5 时,表示用户选择进行禁言、解除禁言、踢人操作
    • 根据 head.forbit 的值执行相应的操作,包括禁言用户、解除禁言、踢出群聊等。在操作过程中发送通知给相关用户。

总体而言,实现了多个聊天室功能,包括私聊、群聊、文件传输、修改密码、禁言、解禁等。注释中使用了不同颜色样式以便终端更好展示不同类型的消息。

server.h

#ifndef SERVER_H
#define SERVER_H

#include <stdio.h&gt;
#include <stdlib.h&gt;
#include <time.h&gt;
#include <string.h&gt;
#include <unistd.h&gt;
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
#include <sqlite3.h>

int serverfd; // 服务器 socket描述

int clientfd[100]; // 存储客户端的 socket 描述数组,最多支持 100 个客户端,100个元素,clientfd[0]~clientfd[99]

int size = 50; // 控制聊天室中的最大用户数

char *IP = "192.168.1.145"; // 主机 IP 地址

short PORT = 12345; // 端口号

typedef struct sockaddr meng;

time_t nowtime; // 用于存储时间

// 创建结构体 表示客户端的信息包含标志位、权限标志位、禁言标志位、用户名、密码、消息、操作对象等信息
struct client
{
	int flag;		   // 功能标志位  -1:退出群聊   0:通知所有在线用户   1:私聊   2:群聊   3.发送文件   4.修改密码
	int root;		   // 权限标志位  -1:首次进入聊天室   0:普通用户   1:管理员  2:VIP用户
	int forbit;		   // 管理权限   1:禁言   2:解除禁言   3:踢人
	char name[50];	   // 账号
	char password[20]; // 密码
	char msg[500];	   // 聊天信息
	char to_name[50];  // 操作对象
	struct client *next;
};

struct Forbit // 存放被禁言人员
{
	char name[50];
	struct Forbit *next;
};

struct Root // 存放管理员名单
{
	char root[50]; // 管理员用户
	struct Root *next;
};

struct VIP // 存放vip用户名
{
	char name[50]; // 管理员用户
	struct VIP *next;
};

struct user // 用来存放登录用户
{
	int c_fd;
	char name[50];
	struct user *next;
};

struct Group // 存放群内成员
{
	char name[50]; // 群内成员用户
	struct Group *next;
};

struct i_fd // 线程索引和客户端文件描述符
{
	int i;
	int c_fd;
};

struct Node // 存放已注册用户
{
	char name[50];
	char password[20];
	struct Node *next;
};

// 把文件里的存到链表里,每次打开初始化使用
void insert_fp(struct Root *root);

// 创建管理员用户链表头节点
int R_init(struct Root **root);

// 把文件里的存到链表里,每次打开初始化使用
void V_insert_fp(struct VIP *vip);

// 创建VIP用户链表头节点
int V_init(struct VIP **vip);

// 创建已登录用户链表头节点
int init_l(struct user **head);

// 创建已注册用户链表头节点
int init_r(struct Node **head);

// 创建被禁言人员表头节点
int F_init(struct Forbit **head);

// 创建群内成员链表头节点
int G_init(struct Group **head);

// 添加被禁言人员
int insert_forbit(struct Forbit *head, char *name);

// 添加群内成员
int insert_group(struct Group *head, char *name);

// 移除被禁言人员
int delete_forbit(struct Forbit *head, char *name);

// 移除群内成员
int delete_group(struct Group *head, char *name);

// 添加已登录用户
struct user *insert_tail(struct user *head, int c_fd, char *name);

// 添加注册用户
struct Node *insert(struct Node *head, char *name, char *password);

// 搜索存在用户名是否存在,存在返回线程
int searchuser(struct user *head, char *name);

// 初始化服务器
void Socket();

// 插入用户信息到数据库
void insertdb(struct Node *head);

// 搜索密码
void search_pw(char *n, char *pw);

// 搜索用户名是否存在
int search_user(char *n);

// 搜索是否已经登录
int search_log(char *n);

// 搜索该成员是否为群成员
int seek_Group(char *n);

// 搜索该成员是否为VIP用户
int seek_VIP(char *n);

// 搜索该成员是否被禁言
int seek_forbit(char *n);

// 更新用户密码
void update_pw(char *name, char *pw);

// 显示在线用户
void display(struct user *head);

// 创建数据库
void createtable();

// 检查数据库中是否存在指定
int searchtable(char *tablename);

// 初始化数据库
void initdb();

// 执行SQL命令
void carryout(char *sql);

// 更新数据库中注册用户信息
void updatedb(char *name, char *pw);

// 将已注册的用户信息载入链表
void downdb(struct Node *head);

// 聊天室功能选择
int Function(struct client head);

// 接收客户端结构体,并作出处理
void *server_thread(void *p);

// 登录
int logon(int c_fd);

// 注册
int user_register(int c_fd);

// 登录、注册
void *register_or_log(void *tt);

// 启动服务器
void server();

#endif // SERVER_H

server.c

/* 聊天室服务器端的主程序 */
#include "server.h"

int personalflag[500] = {-1};
sem_t sem[500];
sem_t sem1[500];
struct user *hl; // 已登录用户头节点
struct Node *hr; // 已注册用户头节点
pthread_t pthuser[500];
struct Forbit *F_head; // 禁言人员头节点
struct Root *R_head;   // 管理员人员头节点
struct Group *G_head;  // 群内成员头结点
struct VIP *V_head;	   // VIP用户头结点

// 把文件里的存到链表里,每次打开时初始化使用
void insert_fp(struct Root *root)
{
	int cnt;
	FILE *fp;
	FILE *dp;
	dp = fopen("./R_num.txt", "r"); // 打开文件R_num.txt,该文件用于记录链表中元素的数量
	fscanf(dp, "%d", &amp;cnt);			// 读取表中元素的数量
	fclose(dp);

	fp = fopen("./root.txt", "a+"); // 打开文件root.txt,该文件用于存储链表中的元素数

	while (cnt != 0) // 遍历文件中的数据插入链表
	{
		struct Root *newcilent = (struct Root *)malloc(sizeof(struct Root)); // 申请新节点的空间
		if (NULL == newcilent)
		{
			// 处理内存分配失败的情况
			return;
		}
		fscanf(fp, "%s n", newcilent->root); // 从文件中读取数据到新节点
		newcilent->next = NULL;				  // 设置新节点的下一个节点为NULL
		root->next = newcilent;				  // 将新节点插入到链表中
		root = root->next;					  // 更新链表尾部指针
		cnt--;
	}
	fclose(fp); // 关闭文件
}

// 创建管理员用户链表头节点
int R_init(struct Root **root)
{
	struct Root *newcilent = (struct Root *)malloc(sizeof(struct Root)); // 为链表头节点分配内存
	if (NULL == newcilent)
	{
		return -1; // 内存分配失败返回错误
	}

	newcilent->next = NULL; // 设置表头节点的下一个节点为NULL
	*root = newcilent;		// 将链表头指针指向新创建的节点
	int cnt = 0;			// 计数储存文件初始化
	FILE *dp;
	char ch;

	dp = fopen("./R_num.txt", "a"); // 如果没有,创建一个,并且不会覆盖原有的数据
	fclose(dp);
	dp = fopen("./R_num.txt", "r"); // 关闭之后,再以只读模式打开
	ch = fgetc(dp);
	fclose(dp);

	if (ch == EOF) // 如果ch=EOF,证明文件为空,初始化应该存个零进去
	{
		dp = fopen("./R_num.txt", "w"); // 写会覆盖
		fprintf(dp, "%d", cnt);			// 将计数写入文件
		fclose(dp);
	}
	else if (ch != EOF) // 如果不为空,则把之前存的文件放进链表
	{
		insert_fp(*root); // 调用insert_fp函数将文件内容插入链表
	}

	return 0; // 返回成功
}

// 把文件里的存到链表里,每次打开时初始化使用
void V_insert_fp(struct VIP *vip)
{
	int cnt;
	FILE *fp;
	FILE *dp;
	dp = fopen("./V_num.txt", "r"); // 打开文件V_num.txt以读取计数
	fscanf(dp, "%d", &amp;cnt);			// 读取文件中的计数
	fclose(dp);

	fp = fopen("./VIP.txt", "a+"); // 以附加和读取方式打开VIP.txt文件

	while (cnt != 0)
	{
		struct VIP *newcilent = (struct VIP *)malloc(sizeof(struct VIP)); // 为VIP链表节点分配内存
		if (NULL == newcilent)
		{
			return; // 内存分配失败退出函数
		}

		fscanf(fp, "%s n", newcilent->name); // 从文件中读取VIP用户信息
		newcilent->next = NULL;				  // 设置VIP链表节点的下一个节点为NULL
		vip->next = newcilent;				  // 将新节点插入VIP链表
		vip = vip->next;					  // 移动VIP链表指针到新节点
		cnt--;
	}

	fclose(fp); // 关闭文件
}

// 创建VIP用户链表头节点
int V_init(struct VIP **vip)
{
	struct VIP *newcilent = (struct VIP *)malloc(sizeof(struct VIP)); // 为VIP链表头节点分配内存
	if (NULL == newcilent)
	{
		return -1; // 内存分配失败返回错误
	}

	newcilent->next = NULL; // 设置VIP链表头节点的下一个节点为NULL
	*vip = newcilent;		// 将VIP链表头节点指针传递调用者

	int cnt = 0; // 计数变量,用于存储文件初始化
	FILE *dp;
	char ch;

	dp = fopen("./V_num.txt", "a"); // 如果文件不存在,则创建一个,而且不会覆盖原有的数据
	fclose(dp);

	dp = fopen("./V_num.txt", "r"); // 关闭之后,再以只读模式打开
	ch = fgetc(dp);
	fclose(dp);

	if (ch == EOF) // 如果ch=EOF,证明文件为空,初始化应该存个零进去
	{
		dp = fopen("./V_num.txt", "w"); // 以写模式打开,会覆盖原有数据
		fprintf(dp, "%d", cnt);			// 将计数值写入文件
		fclose(dp);
	}
	else if (ch != EOF) // 如果文件不为空,将之前存的文件放进链表
	{
		V_insert_fp(*vip); // 调用V_insert_fp函数将文件中的VIP用户信息插入VIP链表
	}

	return 0; // 返回成功
}

// 创建已登录用户链表头节点
int init_l(struct user **head)
{
	struct user *newnode = (struct user *)malloc(sizeof(struct user)); // 为已登录用户链表头节点分配内存
	if (NULL == newnode)
	{
		return -1; // 内存分配失败返回错误
	}

	newnode->c_fd = 0;	  // 初始化已登录用户链表头节点的客户端文件描述符为0
	newnode->name[0] = 0; // 初始化已登录用户链表头节点的用户名为空字符串
	newnode->next = NULL; // 设置已登录用户链表头节点的下一个节点为NULL
	*head = newnode;	  // 将已登录用户链表头节点指针传递调用者

	return 0; // 返回成功
}

// 创建已注册用户链表头节点
int init_r(struct Node **head)
{
	struct Node *newnode = (struct Node *)malloc(sizeof(struct Node)); // 为已注册用户链表头节点分配内存
	if (NULL == newnode)
	{
		return -1; // 内存分配失败,返回错误
	}

	newnode->name[0] = 0;	  // 初始化已注册用户链表头节点的用户名为空字符串
	newnode->password[0] = 0; // 初始化已注册用户链表头节点的密码为空字符
	newnode->next = NULL;	  // 设置已注册用户链表头节点的下一个节点为NULL
	*head = newnode;		  // 将已注册用户链表头节点指针传递给调用者

	return 0; // 返回成功
}

// 创建被禁言人员链表头节点
int F_init(struct Forbit **head)
{
	struct Forbit *newnode = (struct Forbit *)malloc(sizeof(struct Forbit)); // 为被禁言人员链表头节点分配内存
	if (NULL == newnode)
	{
		return -1; // 内存分配失败,返回错误
	}

	newnode->name[0] = 0; // 初始化被禁言人员链表头节点的用户名为空字符
	newnode->next = NULL; // 设置被禁言人员链表头节点的下一个节点为NULL
	*head = newnode;	  // 将被禁言人员链表头节点指针传递给调用者

	return 0; // 返回成功
}

// 创建群内成员链表头节点
int G_init(struct Group **head)
{
	struct Group *newnode = (struct Group *)malloc(sizeof(struct Group)); // 为群内成员链表头节点分配内存
	if (NULL == newnode)
	{
		return -1; // 内存分配失败,返回错误
	}

	newnode->name[0] = 0; // 初始化群内成员链表头节点的用户名为空字符
	newnode->next = NULL; // 设置群内成员链表头节点的下一个节点为NULL
	*head = newnode;	  // 将群内成员链表头节点指针传递给调用者

	return 0; // 返回成功
}

// 添加被禁言人员
int insert_forbit(struct Forbit *head, char *name)
{
	struct Forbit *newnode = (struct Forbit *)malloc(sizeof(struct Forbit)); // 为新的被禁言节点分配内存
	if (NULL == newnode)
	{
		return -1; // 内存分配失败,返回错误
	}

	strcpy(newnode->name, name); // 将被禁言人员的用户名复制到新节点
	newnode->next = NULL;		 // 设置新节点的下一个节点为NULL

	while (head->next != NULL)
	{
		if (strcmp(head->next->name, name) == 0)
		{
			return 1; // 如果被禁言人员已存在,返回错误
		}
		head = head->next; // 移动到下一个节点
	}

	head->next = newnode; // 将新节点添加到链表的末尾
	return 0;			  // 返回成功
}

// 添加群内成员
int insert_group(struct Group *head, char *name)
{
	struct Group *newnode = (struct Group *)malloc(sizeof(struct Group)); // 为新的群内成员节点分配内存
	if (NULL == newnode)
	{
		return -1; // 内存分配失败,返回错误
	}

	strcpy(newnode->name, name); // 将群内成员的用户名复制到新节点
	newnode->next = NULL;		 // 设置新节点的下一个节点为NULL

	while (head->next != NULL)
	{
		if (strcmp(head->next->name, name) == 0)
		{
			return 1; // 如果群内成员已存在,返回错误
		}
		head = head->next; // 移动到下一个节点
	}

	head->next = newnode; // 将新节点添加到链表的末尾
	return 0;			  // 返回成功
}

// 移除被禁言人员
int delete_forbit(struct Forbit *head, char *name)
{
	int count = 0; // 计数器,记录删除的节点数

	while (head->next != NULL)
	{
		if (strcmp(head->next->name, name) == 0) // 如果找到被禁言的用户
		{
			struct Forbit *ptr = head->next; // 保存找到的节点的指针
			head->next = ptr->next;			 // 调整链表指针,跳过被禁言的用户节点
			free(ptr);						 // 释放被禁言用户的节点内存
			count++;						 // 增加计数器
		}
		else
		{
			head = head->next; // 移动到下一个节点
		}
	}

	if (count == 0)
	{
		return 1; // 未找到被禁言用户,返回错误码
	}

	return 0; // 返回成功
}

// 移除群内成员
int delete_group(struct Group *head, char *name)
{
	int count = 0; // 计数器,记录删除的节点数

	while (head->next != NULL)
	{
		if (strcmp(head->next->name, name) == 0) // 如果找到群成员
		{
			struct Group *ptr = head->next; // 保存找到的节点的指针
			head->next = ptr->next;			// 调整链表指针,跳过群成员节点
			free(ptr);						// 释放群成员节点内存
			count++;						// 增加计数器
		}
		else
		{
			head = head->next; // 移动到下一个节点
		}
	}

	if (count == 0)
	{
		return 1; // 该用户不在群内,返回错误码
	}

	return 0; // 返回成功
}

// 添加已登录用户
struct user *insert_tail(struct user *head, int c_fd, char *name)
{
	struct user *newnode = (struct user *)malloc(sizeof(struct user)); // 申请新节点的内存空间
	if (NULL == newnode)
	{
		return NULL; // 内存分配失败,返回空指针
	}

	newnode->c_fd = c_fd;		 // 设置新节点的客户端文件描述符
	strcpy(newnode->name, name); // 复制用户名到新节点
	newnode->next = NULL;		 // 新节点的下一个节点设为空

	while (head->next != NULL)
	{
		head = head->next; // 移动到链表尾部
	}

	head->next = newnode; // 将新节点添加到链表尾部
	return newnode;		  // 返回新节点的指针
}

// 添加注册用户
struct Node *insert(struct Node *head, char *name, char *password)
{
	struct Node *newnode = (struct Node *)malloc(sizeof(struct Node)); // 申请新节点的内存空间
	if (NULL == newnode)
	{
		return NULL; // 内存分配失败,返回空指针
	}

	strcpy(newnode->name, name);		 // 复制用户名到新节点
	strcpy(newnode->password, password); // 复制密码到新节点
	newnode->next = NULL;				 // 新节点的下一个节点设为空

	while (head->next != NULL)
	{
		head = head->next; // 移动到链表尾部
	}

	head->next = newnode; // 将新节点添加到链表尾部
	return newnode;		  // 返回新节点的指针
}

// 搜索存在用户名是否存在,存在返回线程
int searchuser(struct user *head, char *name)
{
	while (head->next != NULL)
	{
		if (strcmp(head->next->name, name) == 0)
		{
			return head->next->c_fd; // 如果找到用户名,返回对应用户的线程
		}
		else
		{
			head = head->next;
		}
	}

	int flag = search_user(name); // 如果在已登录用户链表中未找到调用search_user函数在已注册用户链表中查找
	return flag;				  // 如果用户存在,返回 1;否则,返回 0
}

// 初始化服务器
void Socket()
{
	// 创建 socket
	serverfd = socket(PF_INET, SOCK_STREAM, 0);
	printf("33[0;34m创建socket成功!33[0mn");
	if (serverfd == -1)
	{
		perror("33[0;31m创建socket失败33[0m");
		exit(-1);
	}

	// 为套接字设置ip协议 设置端口号自动获取本机ip转化为网络ip
	struct sockaddr_in addr;			  // 存储套接字的信息
	addr.sin_family = PF_INET;			  // 地址
	addr.sin_port = htons(PORT);		  // 设置server端端口号,你可以随便设置,当sin_port = 0时,系统随机选择一个未被使用的端口号
	addr.sin_addr.s_addr = inet_addr(IP); // 把127.0.0.1改为自己server端的ip地址,当sin_addr = INADDR_ANY时,表示从本机的任一网卡接收数据

	// 绑定套接字
	if (bind(serverfd, (meng *)&amp;addr, sizeof(addr)) == -1)
	{
		perror("33[0;31m绑定失败33[0m");
		exit(-1);
	}

	// 监听最大连接数
	if (listen(serverfd, 100) == -1)
	{
		perror("33[0;31m设置监听失败!33[0m");
		exit(-1);
	}
}

// 插入用户信息到数据库
void insertdb(struct Node *head)
{
	char sql[500] = "";
	// 构造插入 SQL 语句
	sprintf(sql, "insert into user (name,password) values('%s','%s');", head->name, head->password); // 插入,修改,删除可以用这条语句
	// 执行 SQL 语句
	carryout(sql);
}

// 搜索密码
void search_pw(char *n, char *pw)
{
	struct Node *head = hr;
	while (head->next != NULL)
	{
		head = head->next;
		if (strcmp(n, head->name) == 0)
		{
			strcpy(pw, head->password);
		}
	}
}

// 搜索用户名是否存在
int search_user(char *n)
{
	int count = 0;
	struct Node *head = hr;
	while (head->next != NULL)
	{
		head = head->next;
		if (strcmp(n, head->name) == 0)
		{
			count++;
			// printf("count:%dn", count);
		}
	}
	return count;
}

// 搜索是否已经登录
int search_log(char *n)
{
	// printf("搜索是否已经登录n");
	int flag = 0;
	struct user *head = hl;
	while (head->next != NULL)
	{
		head = head->next;
		// printf("head.name:%sn", head->name);
		if (strcmp(n, head->name) == 0)
		{
			// printf("已经登录n");
			flag = 1;
			// printf("搜索完成n");
			return 1;
		}
	}
	if (flag == 0)
	{
		return 0;
	}
	// printf("搜索完成n");
}

// 搜索该成员是否为群成员
int seek_Group(char *n)
{
	struct Group *head = G_head;
	while (head->next != NULL)
	{
		head = head->next;
		if (strcmp(n, head->name) == 0) // 该用户为群成员
		{
			return 1;
		}
	}
	return 0; // 该用户不是群成员
}

// 搜索该成员是否为VIP用户
int seek_VIP(char *n)
{
	struct VIP *head = V_head;
	while (head->next != NULL)
	{
		head = head->next;
		if (strcmp(n, head->name) == 0) // 该用户为VIP用户
		{
			return 1;
		}
	}
	return 0; // 该用户不是VIP用户
}

// 搜索该成员是否被禁言
int seek_forbit(char *n)
{
	struct Forbit *head = F_head;
	while (head->next != NULL)
	{
		head = head->next;
		if (strcmp(n, head->name) == 0) // 该用户被禁言
		{
			return 1;
		}
	}
	return 0; // 该用户未被禁言
}

// 更新用户密码
void update_pw(char *name, char *pw)
{
	struct Node *head = hr;
	while (head->next != NULL)
	{
		head = head->next;
		if (strcmp(name, head->name) == 0)
		{
			strcpy(head->password, pw);
		}
	}
}

// 显示在线用户
void display(struct user *head)
{
	while (head->next != NULL)
	{
		// printf("c_fd=%dnname=%sn", head->next->c_fd, head->next->name);
		head = head->next;
	}
}

// 创建数据库
void createtable()
{
	sqlite3 *db = NULL;
	int ret = sqlite3_open("info.db", &db); // 打开数据
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_open:");
		exit(1);
	}
	char *errmsg;
	char sql[500] = ""; // 写入命令
	strcpy(sql, "create table user(name text,password text);");
	ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_exec:");
		printf("errmsg:%sn", errmsg);
		exit(2);
	}
	ret = sqlite3_close(db); // 关闭数据
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_close:");
		exit(3);
	}
	return;
}

// 检查数据库中是否存在指定
int searchtable(char *tablename)
{
	sqlite3 *db = NULL;
	int ret = sqlite3_open("info.db", &db); // 打开数据库
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_open:");
		exit(1);
	}
	char sql[200] = "";
	sprintf(sql, "select name from sqlite_master where type='table' AND name='%s';", tablename);
	char *errmsg;
	char **result;
	int row, column;
	ret = sqlite3_get_table(db, sql, &result, &row, &column, &errmsg); // 分别为文件描述符,数据库命令,查询结果,行,列,错误信息
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_exec:");
		printf("errmsg:%sn", errmsg);
		exit(2);
	}
	if (row >= 1)
	{
		return 1;
	}
	else
	{
		return 0;
	}
	sqlite3_free_table(result); // 释放内存
	ret = sqlite3_close(db);	// 关闭数据库
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_close:");
		exit(3);
	}
}

// 初始化数据库
void initdb()
{
	printf("正在进行初始化!n");
	if (searchtable("user") == 0)
	{
		createtable();
	}
}

// 执行SQL命令
void carryout(char *sql)
{
	sqlite3 *db = NULL;
	int ret = sqlite3_open("info.db", &db); // 打开数据库
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_open:");
		exit(1);
	}
	char *errmsg;
	ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_exec1:");
		printf("errmsg:%sn", errmsg);
		exit(2);
	}
	ret = sqlite3_close(db); // 关闭数据库
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_close:");
		exit(3);
	}
}

// 更新数据库中注册用户信息
void updatedb(char *name, char *pw) // 插入,修改数据库注册用户信息
{
	char sql[500] = "";
	sprintf(sql, "update user set password='%s' where name='%s';", pw, name); // 插入,修改,删除都可以用这条语句
	carryout(sql);
}

// 将已注册的用户信息载入链表
void downdb(struct Node *head)
{
	// printf("进入downdbn");
	sqlite3 *db = NULL;
	int ret = sqlite3_open("info.db", &db); // 打开数据库
	// printf("打开数据库n");
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_open:");
		exit(1);
	}
	const char *sql = "select *from user;"; // 写入命令
	// printf("写入命令n");
	char *errmsg;
	char **result;
	int row = 0, column = 0;
	ret = sqlite3_get_table(db, sql, &result, &row, &column, &errmsg); // 分别为文件描述符,数据库命令,查询结果,行,列,错误信息
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_exec:");
		printf("errmsg:%sn", errmsg);
		exit(2);
	}
	printf("row=%d    column=%dn", row, column);
	if (row == 0)
	{
		return;
	}
	int i, j;
	int num;
	char name[20] = "";
	char password[20] = "";
	printf("开始载入n");
	for (i = 1; i <= row; i++)
	{
		for (j = 0; j < column; j++)
		{
			num = i * column + j;
			// printf("%sn",result[num]);  //查询到的信息按一维数组方式储存,0到2为id,name,age这些字段,3到5为第一行数据,6到8为第二行数据
			if (j == 0)
			{
				strcpy(name, result[num]);
			}
			if (j == 1)
			{
				strcpy(password, result[num]);
			}
		}
		insert(head, name, password);
	}
	sqlite3_free_table(result); // 释放内存
	// printf("释放内存n");
	ret = sqlite3_close(db); // 关闭数据库
	// printf("关闭数据库n");
	if (SQLITE_OK != ret)
	{
		perror("sqlite3_close:");
		exit(3);
	}
	return;
}

// 聊天室功能选择
int Function(struct client head)
{
	// (变量声明和初始化)
	// printf("msg:%sn", head.msg);
	struct Forbit *p = F_head;
	struct Root *Root = R_head;
	struct user *all = hl;
	struct Group *group = G_head;
	struct VIP *vip = V_head;

	int temp = head.root;			// 保存原始 root 值
	int flag = seek_VIP(head.name); // 检查用户是否为 VIP
	if (flag == 1)
	{
		head.root = 2; // 如果用户是 VIP,将 root 设置为 2
	}

	if (-1 == head.flag)
	{
		delete_group(group, head.name); // 退出群聊
	}
	else if (0 == head.flag) // 发送给所有在线用户
	{
		// 遍历在线用户,将消息发送给所有用户
		while (all->next != NULL)
		{
			all = all->next;

			// 遍历在线用户,将消息发送给所有用户
			if (strcmp(all->name, head.name) == 0)
			{
				if (all->next != NULL)
				{
					all = all->next;
				}
				else
				{
					break;
				}
			}

			if (all->c_fd != 0)
			{
				send(all->c_fd, head.msg, sizeof(head.msg), 0);
			}
		}
	}
	else if (1 == head.flag) // 私聊
	{
		char buf[1024];
		int toc_fd = searchuser(hl, head.to_name);
		if (toc_fd == 1)
		{
			toc_fd = searchuser(hl, head.name);
			sprintf(buf, "33[0;33m该用户未登录!33[0mn");
			send(toc_fd, buf, sizeof(head.msg), 0);
			memset(buf, 0, sizeof(buf));
		}
		else if (toc_fd == 0)
		{
			toc_fd = searchuser(hl, head.name);
			sprintf(buf, "33[0;31m该用户不存在!33[0mn");
			send(toc_fd, buf, sizeof(head.msg), 0);
			memset(buf, 0, sizeof(buf));
		}
		else
		{
			// 格式化私聊消息
			int flag1 = seek_Group(head.to_name); // 查找发送用户是否处在群聊
			if (flag1 == 1)
			{
				sprintf(buf, "33[34m%s33[35m(私密消息)33[36m:%s33[0mn", head.name, head.msg);
				strcpy(head.msg, buf);
				memset(buf, 0, sizeof(buf));
			}
			else
			{
				sprintf(buf, "33[0;34m%s33[36m:%s33[0mn", head.name, head.msg);
				strcpy(head.msg, buf);
				memset(buf, 0, sizeof(buf));
			}

			// 如果是 VIP,添加 VIP 标记
			if (head.root == 2)
			{
				sprintf(buf, "33[33m(VIP)%s", head.msg);
				strcpy(head.msg, buf);
				memset(buf, 0, sizeof(buf));
			}

			// 如果在群聊中,添加群聊标记
			if (flag1 == 1)
			{
				sprintf(buf, "33[5m%s", head.msg);
				strcpy(head.msg, buf);
				memset(buf, 0, sizeof(buf));
			}
			// 发送私聊消息
			send(toc_fd, head.msg, sizeof(head.msg), 0);
		}
	}
	else if (head.flag == 2) // 群聊
	{
		char buf[1024];
		int t_fd;
		if (temp == -1) // 进入群聊
		{
			insert_group(group, head.name);
			return 0;
		}
		int flag1 = seek_Group(head.name); // 查找该用户是否为群成员
		if (flag1 == 1)
		{
			int c_fd = searchuser(hl, head.name);
			int ret = seek_forbit(head.name);
			if (ret == 1) // 该用户处于禁言状态
			{
				sprintf(buf, "33[0;31m发送失败!你已被禁言!33[0mn");
				send(c_fd, buf, sizeof(buf), 0);
				memset(buf, 0, sizeof(buf));
				return 0;
			}
			if (head.root == 2)
			{
				sprintf(buf, "33[0;33m(VIP)%s", head.msg);
				strcpy(head.msg, buf);
				memset(buf, 0, sizeof(buf));
			}
			while (group->next != NULL) // 遍历所有群成员,,发送群消息
			{
				group = group->next;
				if (strcmp(group->name, head.name) == 0)
				{
					if (group->next != NULL)
					{
						group = group->next;
					}
					else
					{
						break;
					}
				}
				t_fd = searchuser(hl, group->name);
				send(t_fd, head.msg, sizeof(head.msg), 0);
			}

			// 记录聊天记录
			FILE *logs = fopen("log.txt", "a+");
			if (logs == NULL)
			{
				printf("open file erroe: n");
			}
			else
			{
				fputs(head.msg, logs);
				fclose(logs);
			}
		}
		else
		{
			t_fd = searchuser(hl, head.name);
			sprintf(buf, "33[0;31m发送失败!你不是该群成员!33[0mn");
			send(t_fd, buf, strlen(buf) + 1, 0);
		}
	}
	else if (3 == head.flag) // 文件传输
	{
		char buf[1024];
		int toc_fd = searchuser(hl, head.to_name);
		printf("%s向%s发送文件!n", head.name, head.to_name);
		sprintf(buf, "33[0;33m%s向你发来一份文件!33[0mn", head.name);
		send(toc_fd, buf, sizeof(buf), 0);
		usleep(30);
		memset(buf, 0, sizeof(buf));
		strcpy(buf, "flag==3");
		send(toc_fd, buf, strlen(buf) + 1, 0); // 让客户端的进行文件接收准备
		memset(buf, 0, sizeof(buf));
		printf("文件内容:n%sn", head.msg);
		send(toc_fd, head.msg, sizeof(head.msg), 0); // 发送文件内容
	}
	else if (4 == head.flag) // 修改密码
	{
		int toc_fd = searchuser(hl, head.name);
		printf("等待用户进行密码修改n");
		int flag, flag1;
		char pw1[20] = "";
		char pw2[20] = "";
		char name[20] = "";

		read(toc_fd, pw1, 20);
		update_pw(head.name, pw1);
		updatedb(head.name, pw1);

		printf("用户密码修改成功n");
	}
	else if (5 == head.flag) // 禁言/解禁/踢人
	{
		char buf[1024];
		int c_fd = searchuser(hl, head.name);
		int toc_fd = searchuser(hl, head.to_name);
		if (head.forbit == 1) // 禁言
		{
			int count = 0;
			while (Root->next != NULL)
			{
				if (strcmp(Root->next->root, head.to_name) == 0)
				{
					sprintf(buf, "33[0;31m禁言失败!用户%s为管理员!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
					count++;
					break;
				}
				Root = Root->next;
			}

			if (count == 0)
			{
				int flag1 = seek_VIP(head.to_name);
				if (flag1 == 1)
				{
					sprintf(buf, "33[0;31m禁言失败!用户%s为VIP用户!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
					count++;
				}
			}

			if (count == 0)
			{
				int ret = insert_forbit(p, head.to_name);
				if (ret == 1) // 该用户已处于禁言状态
				{
					sprintf(buf, "33[0;31m禁言失败!用户%s已处于禁言状态,不可重复禁言!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
				}
				else
				{
					sprintf(buf, "33[0;33m禁言成功!你已将用户%s禁言!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));

					sprintf(buf, "33[0;31m管理员%s将你禁言!33[0mn", head.name);
					send(toc_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
				}
			}
		}
		else if (head.forbit == 2) // 解除禁言
		{
			int ret1 = delete_forbit(p, head.to_name);

			if (ret1 == 1)
			{
				sprintf(buf, "33[0;33m用户%s未被禁言!33[0mn", head.to_name);
				send(c_fd, buf, sizeof(buf), 0);
				memset(buf, 0, sizeof(buf));
			}
			else
			{
				sprintf(buf, "33[0;33m解除成功!你已将用户%s解除禁言!33[0mn", head.to_name);
				send(c_fd, buf, sizeof(buf), 0);
				memset(buf, 0, sizeof(buf));

				sprintf(buf, "33[0;33m你被管理员%s解除禁言!33[0mn", head.name);
				send(toc_fd, buf, sizeof(buf), 0);
				memset(buf, 0, sizeof(buf));
			}
		}
		else if (head.forbit == 3) // 踢人
		{
			int count = 0;
			while (Root->next != NULL)
			{
				if (strcmp(Root->next->root, head.to_name) == 0)
				{
					sprintf(buf, "33[0;31m踢出群聊失败!用户%s为管理员!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
					count++;
					break;
				}
				Root = Root->next;
			}

			if (count == 0)
			{
				int flag1 = seek_VIP(head.to_name);
				if (flag1 == 1)
				{
					sprintf(buf, "33[0;31m踢出群聊失败!用户%s为VIP用户!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
					count++;
				}
			}

			if (count == 0)
			{
				int ret = delete_group(group, head.to_name);
				if (ret == 1) // 该用户不是该群成员
				{
					sprintf(buf, "33[0;31m踢出群聊失败!用户%s不是该群成员!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
				}
				else
				{
					sprintf(buf, "33[0;33m用户%s已被你踢出群聊!33[0mn", head.to_name);
					send(c_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));

					sprintf(buf, "33[0;33m你被管理员%s踢出群聊!33[0mn", head.name);
					send(toc_fd, buf, sizeof(buf), 0);
					memset(buf, 0, sizeof(buf));
				}
			}
		}
	}
}

// 接收客户端结构体,并作出处理
void *server_thread(void *p)
{
	struct client head;					// 用于存储客户端信息的结构体
	struct user *c = hl;				// 指向用户链表的指针
	struct i_fd tt = *(struct i_fd *)p; // 包含线程索引和客户端文件描述符的结构体
	int i = tt.i;						// 线程索引
	int fd = tt.c_fd;					// 客户端文件描述符
	sem_wait(&sem1[i]);					// 等待信号量,保证线程按照顺序执行

	if (personalflag[0] == 0) // 检查是否是私聊模式
	{
		char user[50] = {}; // 存储用户名称
		while (1)
		{
			char buf[100] = {};

			// 接收客户端发送的数据
			if (recv(fd, &head, sizeof(struct client), 0) <= 0)
			{
				// 客户端退出处理
				while (c->next != NULL)
				{
					c = c->next;
					if (fd == c->c_fd)
					{
						c->c_fd = 0;
						memset(c->name, 0, sizeof(c->name)); // 初始化
						break;
					}
				}

				printf("33[0;31m退出:%s 退出了聊天室.33[0mn", user);

				char buf[1024];
				sprintf(buf, "33[0;31m%s退出了聊天室. 33[0mn", user);

				head.flag = 0;
				strcpy(head.name, user);
				strcpy(head.msg, buf);
				Function(head); // 调用处理函数

				FILE *logs = fopen("log.txt", "a");

				if (logs == NULL)
				{
					printf("open file error: n");
				}
				else
				{
					fputs(buf, logs);
					fclose(logs);
				}

				pthread_exit(0); // 线程退出
			}

			// 把服务器接受到的信息发给所有的客户端
			strcpy(user, head.name); // 记录进入群聊的用户

			Function(head); // 调用处理函数
		}
	}
}

// 登录
int logon(int c_fd)
{
	struct Root *root;
	root = R_head;

	printf("等待用户进行登录n");
	int flag, flag1, flag2;
	char pw1[20] = "";
	char pw2[20] = "";
	char name[20] = "";

	// 接收客户端发送的用户名
	read(c_fd, name, 20);
	printf("登录时接收到的用户名:%sn", name);

	// 检查该用户名是否已经进行注册
	flag2 = search_user(name);
	write(c_fd, &flag2, sizeof(flag2));

	if (flag2 > 0) // 已经注册,可以登录
	{
		// 查找该用户是否已经登录
		flag1 = search_log(name);
		printf("用户%s的登录状态:%dn", name, flag1);
		write(c_fd, &flag1, sizeof(flag1));

		if (flag1 == 0)
		{
			int m_root = -1;
			read(c_fd, &m_root, sizeof(m_root));

			if (m_root == 1) // 管理员模式登录
			{
				printf("33[0;33m管理员模式登录中!33[0mn");
				int count = 0;
				while (root != NULL)
				{
					if (strcmp(root->root, name) == 0)
					{
						write(c_fd, &m_root, sizeof(m_root));
						count++;
						printf("33[0;33m管理员%s进入了聊天室!33[0mn", name);
						break;
					}
					root = root->next;
				}
				if (count == 0) // 该用户不是管理员
				{
					printf("33[0;33m该用户不是管理员!33[0mn");
					m_root = 0;
					write(c_fd, &m_root, sizeof(m_root));
					logon(c_fd);
				}
			}

			// 接收客户端发送的密码
			read(c_fd, pw1, 20);
			// 查找数据库中存储的密码
			search_pw(name, pw2);

			// 验证密码是否正确
			if ((strcmp(pw1, pw2)) == 0)
			{
				flag = 1;
				write(c_fd, &flag, sizeof(flag));

				// 存入在线用户链表
				insert_tail(hl, c_fd, name);

				// 检查用户是否为VIP
				int vip = seek_VIP(name);
				printf("vip:%dn", vip);
				write(c_fd, &vip, sizeof(vip));

				return 1; // 登录成功
			}
			else if ((strcmp(pw1, pw2)) != 0)
			{
				flag = 0;
				write(c_fd, &flag, sizeof(flag)); // 让客户端重新进行用户登录
				logon(c_fd);
			}
		}
		else if (flag1 != 0)
		{
			printf("该用户已经登录,等待客户端重新输入n");
			logon(c_fd);
		}
	}
	else if (flag2 == 0)
	{
		printf("该用户不存在,等待客户端重新输入n");
		logon(c_fd);
	}
}

// 注册
int user_register(int c_fd)
{
	char pw[20] = "";
	char name[20] = "";

	printf("等待客户端进行注册n");

	// 接收客户端发送的用户名
	read(c_fd, name, 20);
	printf("读取到用户名:%sn", name);

	// 检查该用户名是否已经存在
	int flag = search_user(name);
	write(c_fd, &flag, sizeof(flag));

	if (flag == 0) // 不存在,可以注册,读取密码
	{
		// 接收客户端发送的密码
		read(c_fd, pw, 20);

		// 将用户信息插入链表
		struct Node *text = insert(hr, name, pw);
		printf("用户注册成功n用户名:%sn密码:%sn", text->name, text->password);

		// 将用户信息插入数据库
		insertdb(text);

		return 1; // 注册成功
	}
	else if (flag == 1)
	{
		printf("用户名已存在,等待客户端重新输入n");
		user_register(c_fd);
	}
}

// 登录、注册
void *register_or_log(void *tt)
{
	// 从传入的参数获取客户端套接字和线程索引
	struct i_fd *iandfd = (struct i_fd *)tt;
	int c_fd = iandfd->c_fd;
	int i = iandfd->i;
	int y = 0;
	int flag, flag1;

	// 循环等待客户端选择登录或注册
	while (1)
	{
		// 从客户端读取选择信息
		int ret = read(c_fd, &y, 4);

		if (y == 1)
		{
			// 客户端选择登录
			flag = logon(c_fd); // 登录成功返回1
			if (flag)
			{
				// 登录成功,取消个人功能线程的阻塞
				personalflag[i] = 0;
				sem_post(&sem1[i]);
				break; // 退出循环
			}
		}
		else if (y == 2)
		{
			// 客户端选择注册
			user_register(c_fd);
		}
	}

	// 等待个人功能线程结束
	pthread_join(pthuser[i], NULL);
}

// 启动服务器
void server()
{
	int c_fd;
	struct user *head;	  // 用来存放已登录用户
	struct Node *head1;	  // 存放已注册用户
	struct Forbit *head2; // 存放被禁言人员
	struct Root *head3;	  // 存放管理员名单
	struct Group *head4;  // 存放群内成员
	struct VIP *head5;	  // 存放 VIP 用户名单
	struct i_fd iandfd;	  // 线程索引和客户端文件描述符

	// 初始化用户链表和数据库
	init_l(&head);
	init_r(&head1);
	initdb();
	downdb(head1);

	// 初始化禁言用户、管理员、群组、VIP 用户链表头节点
	F_init(&head2);
	R_init(&head3);
	G_init(&head4);
	V_init(&head5);

	hl = head;
	hr = head1;
	F_head = head2;
	R_head = head3;
	G_head = head4;
	V_head = head5;

	printf("服务器启动n");
	int i = 0;

	// 服务器主循环,等待客户端连接
	while (1)
	{
		struct sockaddr_in fromaddr;
		socklen_t len = sizeof(fromaddr);

		// 接受客户端连接请求
		int fd = accept(serverfd, (meng *)&fromaddr, &len);

		if (fd == -1)
		{
			printf("客户端连接出错...n");
			continue;
		}
		printf("客户端连接成功n");

		iandfd.i = i;
		iandfd.c_fd = fd;

		// 创建线程处理客户端登录或注册
		int ret = pthread_create(&pthuser[i], NULL, register_or_log, (void *)&iandfd);
		if (ret != 0)
		{
			perror("pthread_create1:");
			exit(1);
		}

		sem_init(&sem[i], 0, 0);
		sem_init(&sem1[i], 0, 0);

		if (clientfd[i] == 0)
		{
			// 记录客户端的 socket
			clientfd[i] = fd;
			printf("线程号= %dn", fd);

			// 有客户端连接之后,启动线程为此客户服务
			pthread_t tid;
			pthread_create(&tid, 0, server_thread, &iandfd);
		}

		if (size == i)
		{
			// 发送给客户端说聊天室满了
			char *str = "33[0;31m对不起,聊天室已经满了!33[0mn";
			send(fd, str, strlen(str), 0);
			close(fd);
		}
		i++;
	}
}

int main()
{
	system("clear"); // 清屏
	Socket();		 // 初始化
	server();		 // 启动服务器
	close(serverfd); // 关闭服务器的 socket 连接

	return 0;
}

客户端

客户端代码主要涉及以下几个方面的知识:

  1. Socket编程:使用了socketconnect等系统调用来建立客户端与服务器的网络连接

  2. 多线程编程:通过pthread库创建了一个用于接收消息的线程。这种方式可以在程序执行其他任务的同时异步接收服务器的消息。

  3. 文件操作:使用readwrite进行文件的读写操作,实现了文件的传输功能。

  4. 字符界面交互:使用了一些ANSI转义码,通过控制台打印不同颜色的消息,实现了简单的字符界面

  5. 结构体和链表定义struct client结构体表示客户端的信息,通过链表组织这些结构体。

  6. 进度条:使用了进度条效果,提升了用户体验

下面是你的代码的功能分析

  • Socket初始化与连接Socket函数创建了客户端套接字,connect建立了与服务器的连接。

  • 用户登录/注册log_in函数提供了用户登录和注册的功能,根据用户选择进行相应的操作。

  • 接收消息线程recv_thread函数作为接收消息的线程,通过recv从服务器接收消息,根据消息的内容进行不同的处理。

  • 用户聊天group_chat函数实现了群聊和私聊功能,根据用户输入的命令进行不同的操作。

  • 文件传输transfer_file函数实现了文件的传输功能,根据用户选择进行文件的发送或接收。

  • 修改密码alter函数提供了用户修改密码的功能。

  • 管理员权限manage函数处理了管理员的权限操作,如禁言、解除禁言、踢人等。

  • 字符界面交互:使用了一系列的ANSI转义码来控制字符颜色显示效果,提供了良好的用户交互界面

  • 清除消息和提示:提供了清除输入框内容和系统提示信息的功能,提高了用户体验

总体来说,你的客户端实现了一个简单的聊天室系统,包括登录、注册、群聊、私聊、文件传输、修改密码等功能。使用了多线程异步接收消息,通过字符界面提供友好的交互体验

client.h

#ifndef CLIENT_H
#define CLIENT_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <semaphore.h>
#include <pthread.h>

// 表示客户端的信息,包含标志位、权限标志位、禁言标志位、用户名、密码、消息、操作对象等信息
struct client
{
	int flag;		   // 功能标志位 -1:退出群聊 0:通知所有在线用户 1:私聊 2:群聊 3.发送文件 4.修改密码 5.管理员权限操作
	int root;		   // 权限标志位 -1:首次进入聊天室 0:普通用户 1:管理员 2;VIP用户
	int forbit;		   // 管理员权限 1:禁言 2:解除禁言 3:踢人
	char name[50];	   // 账号
	char password[20]; // 密码
	char msg[500];	   // 聊天信息
	char to_name[50];  // 私聊对象
	struct client *next;
};
struct client Head;

// 进度
void progress_bar();

// 清除输入框内容
void Clear_news();

// 清除系统提示信息
void Clear_hint();

// 打印消息
int print_msg(char *msg, int flag);

// 打印聊天框
void Chat_frame(struct client *head);

// 打印管理员群聊框
void Root_frame(struct client *head);

// 打印功能选择界面
void Function_frame(struct client *head);

// 打印系统提示消息
void print_hint(char *msg);

// 读取文件
int fileread(char *massage);

// 写文件
void filewrite(char *massage);

// 登录
int search(struct client *head);

// 用户帐号注册
int sign_in(struct client *head);

// 初始化
void n_init();

// 文件传输
void transfer_file(struct client *head, int temp);

// 私聊、群聊
void group_chat(struct client *head);

// 修改密码
void alter(struct client *head);

// 管理员权限
void manage(struct client *head, int flag);

// 聊天模式选择
void Chat_choose(struct client *head);

// 登录&注册
void log_in(struct client *head);

#endif // CLIENT_H

client.c

/* 聊天室客户端的主程序 */
#include "client.h"

int clientfd2; // 客户端socket

char *IP = "192.168.1.145"; // 服务器的IP

short PORT = 12345; // 服务器服务端

typedef struct sockaddr meng;

// char user[50]; //设置支持的用户名长度

time_t nowtime; // 用于存储时间

sem_t sem1;

int line = 8; // 打印消息时候行数

int frame = 0; // 是否在聊天框内 0:处在功能选择 1:处在聊天框

// 进度
void progress_bar()
{

	// ****************************** 配置 ***************************
	// 最后100%时的输出形式
	const char *LastStr = "[--------------------] 100%";

	// 进度条标志,可以改用"*"或其它符号
	const char ProgressIcon = '-';

	// 进度每加5,进度条就会加一格,注意Step值要和LastStr中的"-"数量相匹配,两者相乘等于100
	const int Step = 5;

	// 总共需要多少进度标志,即LastStr中"-"的数量
	const int IconMaxNum = 20;

	// 每隔100ms打印一次,这里同时每隔200ms会让进度加1
	const int PrintInterval = 20000;
	// ****************************************************************

	for (int i = 0; i <= 100; ++i)
	{
		// -------------- 打印进度条 --------------
		printf("33[0;34m[33[0m");
		int currentIndex = i / Step;
		for (int j = 0; j < IconMaxNum; ++j)
		{
			if (j < currentIndex)
			{
				printf("33[0;34m%c33[0m", ProgressIcon); // 打印进度条标志
			}
			else
			{
				printf(" "); // 未达进度则打印空格
			}
		}

		printf("33[0;34m]33[0m ");
		// ----------------------------------------

		// -------- 打印数字进度 ----------
		printf("33[0;34m%3d%%33[0m", i);
		fflush(stdout);
		// -------------------------------

		usleep(PrintInterval);

		for (int j = 0; j < strlen(LastStr); ++j)
		{
			printf("b"); // 回删字符,让数字和进度条原地变化
		}
		fflush(stdout);
	}
	printf("n");
}

// 清除输入框内容
void Clear_news()
{
	printf("33[33;12H"); // 将光标移动回到消息发送区
	printf("33[K");	   // 清除发送框内容
	printf("33[72C");
	printf("33[0;34m|                 |33[0mn");
	printf("33[33;12H 33[0;36m"); // 将光标移动回到消息发送区
	printf("33[36m");
}

// 清除系统提示信息
void Clear_hint()
{
	printf("33[31;4H"); // 将光标系统提示信息头位置
	printf("33[K");	  // 清除系统提示信息
	printf("33[80C");
	printf("33[0;34m|                 |33[0mn");
	printf("33[33;12H 33[0;36m"); // 将光标移动回到消息发送区
}

// 打印消息
int print_msg(char *msg, int flag)
{
	if (frame == 1)
	{
		if ((*msg == '') || (*msg == 'n'))
		{
			return 0;
		}

		char i_msg[500];
		printf("33[s"); // 保存光标位置
		fflush(stdout);

		if (line == 8)
			printf(" 33[8;4H"); // 消息打印位置
		else if (line == 10)
			printf(" 33[10;4H"); // 消息打印位置
		else if (line == 12)
			printf(" 33[12;4H"); // 消息打印位置
		else if (line == 14)
			printf(" 33[14;4H"); // 消息打印位置
		else if (line == 16)
			printf(" 33[16;4H"); // 消息打印位置
		else if (line == 18)
			printf(" 33[18;4H"); // 消息打印位置
		else if (line == 20)
			printf(" 33[20;4H"); // 消息打印位置
		else if (line == 22)
			printf(" 33[22;4H"); // 消息打印位置
		else if (line == 24)
			printf(" 33[24;4H"); // 消息打印位置
		else if (line == 26)
			printf(" 33[26;4H"); // 消息打印位置
		else if (line == 28)
			printf(" 33[28;4H"); // 消息打印位置
		else if (line == 30)
			printf(" 33[30;4H"); // 消息打印位置
		else if (line == 32)	   // 行数到达最后一行
		{
			if ((Head.root == 1) && (Head.flag == 2))
			{
				Root_frame(&Head);
			}
			else
			{
				Chat_frame(&Head);
			}

			printf(" 33[8;4H"); // 消息打印位置
		}

		if (flag == 1) // 打印收到的消息
		{
			printf("%s", msg);
			// printf("33[0;34m |33[0m");
		}
		else if (flag == 2) // 打印发送的消息
		{
			sprintf(i_msg, "33[0;36m%s33[0;33m:我33[0mn", msg);
			printf("%*s", 99, i_msg);
		}
		// printf("%*s",80,msg);
		line = line + 2;
		printf(" 33[u"); // 恢复光标位置
		fflush(stdout);
	}
	else if (frame == 0)
	{
		return 0;
	}
}

// 打印聊天框
void Chat_frame(struct client *head)
{

	system("clear");
	printf("33[1;1H");
	fflush(stdout);
	line = 8;
	frame = 1;
	char name[100];
	char to_name[100];
	printf("33[34m +===================================================================================================+33[0mn");
	printf("33[34m |                                                                                                   |33[0mn");
	printf("33[34m |33[35m*********************************************33[33m33[1m年糕聊天室33[0;35m********************************************33[34m|33[0mn");
	printf("33[34m |                                                                                         33[35m版本:3.033[34m  |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------------------------|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------|                 |33[0mn");
	printf("33[34m |                                                                                 |33[33m33[1m用户:            33[0;34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 | 33[33m33[1mw!:33[0;36m文件发送     33[34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------|                 |33[0mn");
	printf("33[34m |33[33m消息输入:33[0;36m                                                                        33[34m|                 |33[0mn");
	printf("33[34m |                                                                                 |33[35m输入33[31mq!33[0;35m退出聊天框33[34m |33[0mn");
	printf("33[34m +===================================================================================================+33[0mn");

	if (head->root == 1)
	{
		sprintf(name, "%s(管理员)n", head->name);
	}
	else if (head->root == 2)
	{
		sprintf(name, "%s(VIP)n", head->name);
	}
	else
	{
		sprintf(name, "%sn", head->name);
	}

	if (head->flag == 2)
	{
		sprintf(to_name, "群聊n");
	}
	else
	{
		sprintf(to_name, "%sn", head->to_name);
	}
	printf("33[6;40H");						   // 将管标移到聊天对象打印位置
	printf("33[33m33[1m%s33[0mn", to_name); // 聊天对象

	printf(" 33[9;85H");						 // 将管标移到用户名打印位置
	printf("33[36m33[1m%s33[0m n", name); // 用户名

	printf(" 33[33;12H 33[0;36m"); // 将光标移动回到消息发送区
}

// 打印管理员群聊框
void Root_frame(struct client *head)
{
	// printf("n");
	system("clear");
	printf("33[1;1H");
	fflush(stdout);
	line = 8;
	frame = 1;
	char name[100];
	char to_name[100];
	printf("33[34m +===================================================================================================+33[0mn");
	printf("33[34m |                                                                                                   |33[0mn");
	printf("33[34m |33[35m*********************************************33[33m33[1m年糕聊天室33[0;35m********************************************33[34m|33[0mn");
	printf("33[34m |                                                                                         33[35m版本:3.033[34m  |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------------------------|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------|                 |33[0mn");
	printf("33[34m |                                                                                 |33[33m33[1m用户:            33[0;34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 | 33[33m33[1m管理员权限:    33[0;34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 | 33[36m33[1m1!:禁言         33[0;34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 | 33[36m33[1m2!:解除禁言     33[0;34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 | 33[36m33[1m3!:踢人        33[0;34m |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 | 33[35m输入关键字      33[0;34m|33[0mn");
	printf("33[34m |                                                                                 | 33[35m进入对应权限    33[0;34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 | 33[33m33[1mw!:33[0;36m文件发送     33[34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------|                 |33[0mn");
	printf("33[34m |33[33m消息输入:33[0;36m                                                                        33[34m|                 |33[0mn");
	printf("33[34m |                                                                                 |33[35m输入33[31mq!33[0;35m退出聊天框33[34m |33[0mn");
	printf("33[34m +===================================================================================================+33[0mn");

	sprintf(name, "%s(管理员)n", head->name);

	sprintf(to_name, "群聊n");

	printf("33[6;40H");						   // 将管标移到聊天对象打印位置
	printf("33[33m33[1m%s33[0mn", to_name); // 聊天对象

	printf(" 33[9;85H");						 // 将管标移到用户名打印位置
	printf("33[36m33[1m%s33[0m n", name); // 用户名

	printf(" 33[33;12H 33[0;36m"); // 将光标移动回到消息发送区
}

// 打印功能选择界面
void Function_frame(struct client *head)
{
	system("clear");
	line = 8;
	frame = 0;
	char name[100];
	printf("33[34m +===================================================================================================+33[0mn");
	printf("33[34m |                                                                                                   |33[0mn");
	printf("33[34m |33[35m*********************************************33[33m33[1m年糕聊天室33[0;35m********************************************33[34m|33[0mn");
	printf("33[34m |                                                                                         33[35m版本:3.033[34m  |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------------------------|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |33[33m33[1m用户:            33[0;34m|33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                    33[0;36m1:私聊功能33[0;34m                                   |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                    33[0;36m2:进入群聊33[0;34m                                   |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                    33[0;36m3:修改密码33[0;34m                                   |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                   33[0;31m0:退出聊天室33[0;34m                                  |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |                                                                                 |                 |33[0mn");
	printf("33[34m |---------------------------------------------------------------------------------|                 |33[0mn");
	printf("33[34m |33[33m消息输入:33[0;36m                                                                        33[34m|                 |33[0mn");
	printf("33[34m |                                                                                 |33[0;35m输入33[31mq!33[0;35m返回本界面33[0;34m |n");
	printf("33[34m +===================================================================================================+33[0mn");

	if (head->root == 1)
	{
		sprintf(name, "%s(管理员)n", head->name);
	}
	else if (head->root == 2)
	{
		sprintf(name, "%s(VIP)n", head->name);
	}
	else
	{
		sprintf(name, "%sn", head->name);
	}
	printf(" 33[9;85H");						 // 将管标移到用户名打印位置
	printf("33[36m33[1m%s33[0m n", name); // 用户名

	printf(" 33[33;12H 33[0;36m"); // 将光标移动回到消息发送区
}

// 打印系统提示消息
void print_hint(char *msg)
{
	Clear_hint();					  // 清除系统提示信息
	printf("33[31;4H");			  // 将光标系统提示信息头位置
	printf("%s", msg);				  // 打印提示内容
	printf("33[33;12H 33[0;36m"); // 将光标移动回到消息发送区
}

// 读取文件
int fileread(char *massage)
{
	int flag = 0;
	int i;
	FILE *fp;
	char past[20];
	char filename[20];
	char buffer[1024];
	char buffer1[1024 * 1024];
	char msg[100];

	sprintf(msg, "33[0;33m请输入文件路径:33[0mn");
	print_hint(msg); // 打印系统提示信息
	memset(msg, 0, sizeof(msg));
	scanf("%s", past);
	getchar();
	Clear_news(); // 清除输入框内容

	sprintf(msg, "33[0;33m请输入文件名:33[0mn");
	print_hint(msg); // 打印系统提示信息
	memset(msg, 0, sizeof(msg));
	scanf("%s", filename);
	getchar();
	Clear_news(); // 清除输入框内容

	strcat(past, filename);
	fp = fopen(filename, "r");
	if (fp == NULL)
	{
		sprintf(msg, "33[0;31m文件不存在!33[35m(任意键继续,放弃请按1)33[0mn");
		print_hint(msg); // 打印系统提示信息
		memset(msg, 0, sizeof(msg));
		scanf("%d", &flag);
		Clear_news(); // 清除输入框内容
		if (flag == 1)
		{
			return 0;
		}
	}
	else
	{
		sprintf(msg, "33[0;33m文件打开成功!33[0mn");
		print_hint(msg); // 打印系统提示信息
		memset(msg, 0, sizeof(msg));
		fread(buffer, 1, sizeof(buffer), fp);
		sleep(1);
		// printf("33[0;33m文件内容:33[0;36m%s33[0mn", buffer);
	}
	if (strlen(buffer) == 1)
	{
		sprintf(msg, "33[0;31m文件为空!n是否往文件里写入信息.(写入请按1,其他任意键退出)33[0m");
		print_hint(msg); // 打印系统提示信息
		memset(msg, 0, sizeof(msg));
		scanf("%d", &i);
		getchar();
		Clear_news(); // 清除输入框内容
		if (i == 1)
		{
			scanf("%s", buffer);
			getchar();
			fprintf(fp, "%s", buffer);
		}
		else
		{
			fclose(fp);
			return 0;
		}
	}
	if (strlen(buffer) != 0)
	{
		sprintf(buffer1, "%sn%s", filename, buffer);
		// printf("buffer1:%sn",buffer1);
		strcpy(massage, buffer1);
		fclose(fp);
		return 1;
	}
}

// 写文件
void filewrite(char *massage)
{
	int i = 0, len = 0, flag;
	FILE *fp;
	char filename[100] = "./gyf/";
	char filename1[100];
	char buffer[1024];
	char msg[1024];
	while (1)
	{
		if (massage[i] == 'n')
		{
			break;
		}
		len++;
		i++;
	}
	strncpy(filename1, massage, len);
	sprintf(msg, "33[0;33m文件名:33[0;36m%s33[0mn", filename1);
	print_hint(msg); // 打印系统提示信息
	memset(msg, 0, sizeof(msg));
	sleep(1);
	strcat(filename, filename1);
	strcpy(buffer, &massage[len + 1]);
	// printf("33[0;33m文件内容:33[0;36m%s33[0;36m", buffer);
	while (1)
	{
		flag = access(filename, 0);
		if (flag == -1)
		{
			break;
		}
		else if (flag == 0)
		{
			len = strlen(filename);
			while (filename[len - 1] != '.')
			{
				len--;
			}
			char post[10];
			strcpy(post, &filename[len - 1]);
			strcpy(&filename[len - 1], "");
			strcat(filename, "(1)");
			strcat(filename, post);
		}
	}
	fp = fopen(filename, "w+");
	fprintf(fp, "%s", buffer);
	fclose(fp);
	memset(filename, 0, sizeof(filename));
	memset(filename1, 0, sizeof(filename1));
	memset(buffer, 0, sizeof(buffer));

	Clear_hint(); // 清除系统提示信息
}

// 登录
int search(struct client *head)
{
	void log_in();	// 声明
	int flag = -1;	// 密码正确错误标志位     0:密码错误 1:密码正确
	int flag1 = -1; // 该账号是否登录标志位   0:未登录 1:已登录
	int flag2 = -1; // 该用户是否存在标志位   0:不存在 1:存在

	printf("33[0;34m请输入用户名:33[0mn");
	scanf("%s", head->name);
	getchar();
	write(clientfd2, head->name, strlen(head->name));
	int readflag = read(clientfd2, &flag2, sizeof(flag2)); // 读取用户是否存在的标识
	if (flag2 > 0)
	{
		read(clientfd2, &flag1, sizeof(flag1)); // 读取用户是否已经被登录的标识
		if (flag1 == 0)							// 0为未被登录,即可进行登录
		{
			if (head->root == 1)
			{
				write(clientfd2, &head->root, sizeof(head->root));
				head->root = -1;
				read(clientfd2, &head->root, sizeof(head->root));
				if (head->root == 0)
				{
					int key = 0;
					printf("33[0;31m该用户不是管理员!33[0mn");
					printf("33[0;33m是否继续以管理员身份登录?(任意键继续,按1返回初始界面!)33[0mn");
					scanf("%d", &key);
					if (key == 1)
					{
						log_in(head); // 返回初始界面
					}
					else
					{
						search(head);
					}
				}
			}
			else if (head->root == 0)
			{
				write(clientfd2, &head->root, sizeof(head->root));
			}

			printf("33[0;34m请输入密码:33[0mn");
			scanf("%s", head->password);
			getchar();
			write(clientfd2, head->password, 20);
			read(clientfd2, &flag, sizeof(flag));
			if (flag)
			{
				printf("33[0;34m正在登录中33[0m");
				int vip;
				read(clientfd2, &vip, sizeof(vip)); // 判断该用户是否为VIP
				if (vip == 1)
				{
					head->root = 2;
				}
				progress_bar();

				return 1;
			}
			else if (flag == 0)
			{
				printf("33[0;31m用户名或密码输入错误!33[0mn");
				printf("33[0;31m请重新输入!33[0mn");
				search(head);
			}
		}
		else if (flag1 != 0)
		{
			printf("33[0;31m该用户名已在其他客户端登录!33[0mn");
			printf("33[0;31m请重新输入!33[0mn");
			search(head);
		}
	}
	else if (flag2 == 0)
	{
		printf("33[0;31m该用户不存在!33[0mn");
		printf("33[0;31m请重新输入!33[0mn");
		search(head);
	}
}

// 用户帐号注册
int sign_in(struct client *head)
{
	// 获取注册用户名和密码
	while (1)
	{
		int flag = -1;
		// printf("flag:%dn", flag);
		printf("33[0;34m请输入用户名:33[0mn");
		scanf("%s", head->name);
		getchar();
		write(clientfd2, head->name, strlen(head->name));
		int readflag = read(clientfd2, &flag, sizeof(flag));

		if (readflag < 0)
		{
			break;
		}

		if (flag == 0)
		{
			printf("33[0;34m请输入用户密码:33[0mn");
			scanf("%s", head->password);
			getchar();
			if (head->name[0] != '')
			{
				int ret = write(clientfd2, head->password, strlen(head->password));
				if (ret > 0)
				{
					printf("33[0;34m注册中33[0m");
					progress_bar();
					printf("33[0;34m注册成功!33[0mn");
					printf("33[0;36m用户名:%s33[0mn", head->name);
					printf("33[0;36m用户密码:%s33[0mnn", head->password);
					break;
				}
			}
		}
		else if (flag > 0)
		{
			printf("33[0;31m用户名已被占用!n请重新输入!n33[0m");
		}
	}
}

// 初始化
void Socket()
{
	clientfd2 = socket(PF_INET, SOCK_STREAM, 0); // 创建套接字
	struct sockaddr_in addr;					 // 将套接字存在sockaddr_in结构体中
	addr.sin_family = PF_INET;					 // 地址族
	addr.sin_port = htons(PORT);				 // 端口号 可随意设置,不过不可超过规定的范围
	addr.sin_addr.s_addr = inet_addr(IP);		 // inet_addr()函数将点分十进制的字符串转换为32位的网络字节顺序的ip信息

	// 发起连接
	if (connect(clientfd2, (meng *)&addr, sizeof(addr)) == -1)
	{
		perror("33[0;31m无法连接到服务器33[0m");
		exit(-1);
	}
	printf("33[0;33m客户端启动成功n33[0m");
}

// 用于接收聊天信息
void *recv_thread(void *p)
{
	char flag[20] = {};
	strcpy(flag, "flag==3");
	char buf[1024] = {};
	char msg[100];
	while (1)
	{
		char *str = buf;
		if (recv(clientfd2, buf, sizeof(buf), 0) <= 0)
		{
			break;
		}

		if (strcmp(buf, flag) == 0) // 进行文件接收准备
		{
			recv(clientfd2, buf, 500, 0);
			while (*str == '')
			{
				str++;
			}
			printf("33[s"); // 保存光标位置
			fflush(stdout);

			// printf("33[?25l");  //隐藏光标
			printf("33[31;4H"); // 将光标移动到系统提示信息头位置
			printf("33[0;33m文件接收中33[0m");
			progress_bar();
			memset(msg, 0, sizeof(msg));
			// printf("33[33;12H 33[0;36m"); //将光标移动回到消息发送区
			// print_msg(buf, 3); //打印接收文件的消息
			sprintf(msg, "33[0;35m文件接收成功!33[0;36m");
			print_msg(msg, 1); // 打印信息
			sleep(1);
			memset(msg, 0, sizeof(msg));
			filewrite(str); // 写文件

			// printf(" 33[u"); //恢复光标位置
			fflush(stdout);
			// Clear_news(); //清除输入框内容
			printf("33[36m");
			// printf("1n");
			// Clear_news(); //清除输入框内容
			printf("33[?25h"); // 显示光标

			// printf("33[12C 33[1A");
		}
		else
		{
			print_msg(buf, 1); // 打印接收的消息
		}
		memset(buf, 0, sizeof(buf));
	}
}

// 文件传输
void transfer_file(struct client *head, int temp)
{
	char msg[100];
	if (temp == 1) // 该用户处在群聊
	{
		sprintf(msg, "33[0;33m请输入发送对象 :33[0mn");
		print_hint(msg); // 打印系统提示信息
		memset(msg, 0, sizeof(msg));
		scanf("%s", head->to_name);
		getchar();
		Clear_news(); // 清除输入框内容
	}

	while (1)
	{
		int y;
		int i = fileread(head->msg);
		if (i == 0)
		{
			break;
		}
		else if (i == 1)
		{
			send(clientfd2, head, sizeof(struct client), 0);
			printf("33[?25l");  // 隐藏光标
			printf("33[31;4H"); // 将光标系统提示信息头位置
			printf("33[0;33m文件发送中33[0m");
			progress_bar();
			memset(msg, 0, sizeof(msg));
			printf("33[33;12H 33[0;36m"); // 将光标移动回到消息发送区
			printf("33[?25h");			  // 显示光标
			sprintf(msg, "33[0;33m文件发送成功!33[0;35m(按任意键继续,退出请按1)33[0;36mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			scanf("%d", &y);
			getchar();
			Clear_news(); // 清除输入框内容
			// printf("1n");
			// Clear_news(); //清除输入框内容
			printf("33[36m");
		}
		else
		{
			sprintf(msg, "33[0;31m文件为空,无法传送!33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
		}

		if (y == 1)
		{
			break;
		}
	}
}

// 私聊、群聊
void group_chat(struct client *head)
{
	void Chat_choose(); // 声明

	if (1 == head->flag) // 私聊
	{
		char msg[100];
		sprintf(msg, "33[0;35m请输入私聊对象:33[0m");
		print_hint(msg); // 打印系统提示信息
		memset(msg, 0, sizeof(msg));
		scanf("%s", head->to_name);
		getchar();
		Clear_news(); // 清除输入框内容
					  // printf("33[0;35m已进入与%s的私聊界面!33[0mn", head->to_name);
	}
	else if (2 == head->flag) // 群聊
	{
		int temp = head->root; // 记录用户root值
		head->root = -1;	   // 让服务器将该用户加入到群成员
		send(clientfd2, head, sizeof(struct client), 0);
		head->root = temp;
		// printf("33[0;35m欢迎进入群聊!33[0mn");
	}

	Head = *head;
	// 打印聊天框
	if ((head->root == 1) && (head->flag == 2))
	{
		Root_frame(head);
	}
	else
	{
		Chat_frame(head);
	}

	// 消息输入
	while (1)
	{
		char buf[1024] = {};
		// printf("33[0;36m");
		scanf("%s", head->msg);
		getchar();

		// 判断关键字
		if (strcmp(head->msg, "q!") == 0) // 群出聊天
		{
			if (head->flag == 2)
			{
				head->flag = -1;
				send(clientfd2, head, sizeof(struct client), 0); // 退出群聊
			}
			Chat_choose(head);
		}

		if (strcmp(head->msg, "w!") == 0) // 发送文件
		{
			Clear_news(); // 清除输入框内容
			if (head->flag == 1)
			{
				head->flag = 3;
				transfer_file(head, 0); // 文件传输
				head->flag = 1;
			}
			else if (head->flag == 2)
			{
				head->flag = 3;
				transfer_file(head, 1); // 文件传输
				head->flag = 2;
			}
			Clear_hint(); // 清除系统提示信息
			continue;	  // 结束本次循环,进入下次循环
		}

		if (strcmp(head->msg, "1!") == 0) // 禁言
		{
			Clear_news(); // 清除输入框内容
			if ((head->root == 1) && (head->flag == 2))
			{
				head->flag = 5;
				manage(head, 1);
				head->flag = 2;
				continue;
			}
		}
		if (strcmp(head->msg, "2!") == 0) // 解除禁言
		{
			Clear_news(); // 清除输入框内容
			if ((head->root == 1) && (head->flag == 2))
			{
				head->flag = 5;
				manage(head, 2);
				head->flag = 2;
				continue;
			}
		}
		if (strcmp(head->msg, "3!") == 0) // 踢出群聊
		{
			Clear_news(); // 清除输入框内容
			if ((head->root == 1) && (head->flag == 2))
			{
				head->flag = 5;
				manage(head, 3);
				head->flag = 2;
				continue;
			}
		}

		Clear_news(); // 清除聊天框内容

		// 靠左边打印发送的消息
		/*sprintf(buf, "33[0;33m33[1m我:33[0;36m%s33[0mn", head->msg);
		  print_msg(buf, 1); //靠左边打印发送的消息*/

		// 靠右边打印发送的消息
		print_msg(head->msg, 2); // 靠右边打印发送的消息

		if (head->flag == 2)
		{
			if (head->root == 1)
			{
				sprintf(buf, "33[0;33m(管理员)33[0;34m%s33[0;36m:%s33[0mn", head->name, head->msg);
			}
			else
			{
				sprintf(buf, "33[0;34m%s:33[0;36m%s33[0mn", head->name, head->msg);
			}
			strcpy(head->msg, buf);
			memset(buf, 0, sizeof(buf));
		}

		send(clientfd2, head, sizeof(struct client), 0);
	}
}

// 修改密码
void alter(struct client *head)
{
	void Chat_choose();
	int flag = -1;									 // 用来判断密码输入是否正确
	send(clientfd2, head, sizeof(struct client), 0); // 让服务器进入修改密码等待
	char pw[20];
	char pw1[20];
	char msg[100];
	while (1)
	{
		sprintf(msg, "33[0;33m请输入旧密码:33[0mn");
		print_hint(msg); // 打印系统提示信息
		memset(msg, 0, sizeof(msg));
		scanf("%s", pw);
		Clear_news(); // 清除输入框内容
		if (strcmp(head->password, pw) == 0)
		{
			sprintf(msg, "33[0;33m请输入新密码:33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			memset(pw, 0, 20);
			scanf("%s", pw);
			getchar();
			Clear_news(); // 清除输入框内容

			sprintf(msg, "33[0;33m请确认密码:33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			memset(pw1, 0, 20);
			scanf("%s", pw1);
			getchar();
			Clear_news(); // 清除输入框内容

			if (strcmp(pw, pw1) == 0)
			{
				strcmp(head->password, pw);
				int ret = write(clientfd2, head->password, strlen(head->password));
				if (ret > 0)
				{
					printf("33[31;4H"); // 将光标移动到系统提示信息头位置
					printf("33[0;33m服务器修改中33[0m");
					progress_bar();
					memset(msg, 0, sizeof(msg));

					sprintf(msg, "33[0;35m密码修改成功!33[0mn");
					print_hint(msg); // 打印系统提示信息
					memset(msg, 0, sizeof(msg));
					sleep(1);
					Clear_hint();
					break;
				}
				else
				{
					sprintf(msg, "33[0;31m密码修改失败,请重新输入!33[0mn");
					print_hint(msg); // 打印系统提示信息
					memset(msg, 0, sizeof(msg));
					sleep(1);
				}
			}
			else
			{
				sprintf(msg, "33[0;31m;两次密码输入有误!请重新输入!33[0mn");
				print_hint(msg); // 打印系统提示信息
				memset(msg, 0, sizeof(msg));
				sleep(1);
			}
		}
		else
		{
			int temp = 0;
			sprintf(msg, "33[0;31m密码输入错误!是否继续输入33[0;35m(任意键继续,放弃请按1)33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			scanf("%d", &temp);
			Clear_news(); // 清除输入框内容
			if (temp == 1)
			{
				Chat_choose(head);
			}
		}
	}
}

// 管理员权限
void manage(struct client *head, int flag)
{
	Clear_news(); // 清除输入框内容
	char msg[100];
	int temp = 0;
	// sprintf(msg,"33[0;33m请输入禁言用户:33[0mn");
	//         print_hint(msg);//打印系统提示信息
	while (1)
	{
		if (1 == flag)
		{
			head->forbit = 1;
			sprintf(msg, "33[0;33m请输入禁言用户:33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			scanf("%s", head->to_name);
			getchar();
			Clear_news(); // 清除输入框内容
			send(clientfd2, head, sizeof(struct client), 0);
			usleep(100);
		}
		else if (2 == flag)
		{
			head->forbit = 2;
			sprintf(msg, "33[0;33m请输入解禁用户:33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			scanf("%s", head->to_name);
			getchar();
			Clear_news(); // 清除输入框内容
			send(clientfd2, head, sizeof(struct client), 0);
			usleep(100);
		}
		else if (3 == flag)
		{
			head->forbit = 3;
			sprintf(msg, "33[0;33m请输入被踢用户:33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			scanf("%s", head->to_name);
			getchar();
			Clear_news(); // 清除输入框内容
			send(clientfd2, head, sizeof(struct client), 0);
		}
		memset(msg, 0, sizeof(msg));
		sprintf(msg, "33[0;33m33[1m是否继续:33[0;33m(任意键继续,结束请按1)33[0m n");
		print_hint(msg); // 打印系统提示信息
		memset(msg, 0, sizeof(msg));
		scanf("%d", &temp);
		getchar();
		Clear_news(); // 清除输入框内容
		if (1 == temp)
		{
			Clear_hint(); // 清除系统提示信息
			break;
		}
	}
}

// 聊天模式选择
void Chat_choose(struct client *head)
{

	int count = 0;
	char msg[100];
	frame = 0;
	Function_frame(head); // 打印功能选择框
	sprintf(msg, "33[0;34m请输入功能选择项:33[0m n");
	print_hint(msg); // 打印系统提示信息
	memset(msg, 0, sizeof(msg));
	while (1)
	{
		scanf("%d", &count);
		getchar();
		Clear_news(); // 清除输入框内容
		printf("n");
		switch (count)
		{
		case 1:
			Clear_hint(); // 清除系统提示信息
			head->flag = 1;
			group_chat(head); // 私聊功能

		case 2:
			Clear_hint(); // 清除系统提示信息
			head->flag = 2;
			group_chat(head); // 群聊功能
			break;
		case 3:
			Clear_hint(); // 清除系统提示信息
			head->flag = 4;
			alter(head); // 修改密码
			break;
		case 0:
			printf(msg, "33[0;33m退出成功!欢迎下次使用!33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			close(clientfd2);
			printf("33[35,0H");
			exit(-1);
			break;
		default:
			printf("33[0;31m选择有误!请重新输入!33[0mn");
			print_hint(msg); // 打印系统提示信息
			memset(msg, 0, sizeof(msg));
			break;
		}
	}
}

// 登录&注册
void log_in(struct client *head)
{
	int select;
	system("clear");
	printf("33[0;34m+================================================================+33[0mn");
	printf("33[0;34m|                                                                |33[0mn");
	printf("33[0;34m|***********************33[0;33m欢迎来到年糕聊天室33[0;34m***********************|33[0mn");
	printf("33[0;34m|                                                      版本:3.0 |33[0mn");
	printf("33[0;34m|----------------------------------------------------------------|33[0mn");
	printf("33[0;34m|                                                               |33[0mn");
	printf("33[0;34m|                         33[0;36m1.用户登录                            33[0;34m|33[0mn");
	printf("33[0;34m|                                                               |33[0mn");
	printf("33[0;34m|                         33[0;36m2.帐号注册                             33[0;34m|33[0mn");
	printf("33[0;34m|                                                               |33[0mn");
	printf("33[0;34m|                         33[0;33m3.管理员登录                           33[0;34m|33[0mn");
	printf("33[0;34m|                                                               |33[0mn");
	printf("33[0;34m|                        33[0;31m 0:退出聊天室                           33[0;34m|33[0mn");
	printf("33[0;34m|                                                               |33[0mn");
	printf("33[0;34m+================================================================+33[0mn");
	printf("n");
	while (1)
	{
		printf("33[0;34m请选择:33[0mn");
		scanf("%d", &select);
		getchar();

		if (1 == select)
		{
			write(clientfd2, &select, sizeof(select));
			head->root = 0; // 普通用户登录
			search(head);
			break;
		}
		else if (2 == select)
		{
			write(clientfd2, &select, sizeof(select));
			sign_in(head);
		}
		else if (3 == select)
		{
			select = 1;
			write(clientfd2, &select, sizeof(select));
			head->root = 1; // 管理员登录
			search(head);
			break;
		}
		else if (0 == select)
		{
			printf("33[0;33m退出成功!33[0mn");
			printf("33[0;33m欢迎下次使用!33[0mn");
			exit(-1);
			break;
		}
		else
		{
			printf("33[0;31m选择错误!请重新输入!33[0mn");
		}
	}
}

int main()
{
	struct client head;
	Socket();
	log_in(&head); // 进入登录注册界面

	// 创建一个线程用于数据的接收
	pthread_t id;
	void *recv_thread(void *);
	int ret1 = pthread_create(&id, 0, recv_thread, 0);
	if (ret1 != 0)
	{
		perror("pthread_create1:");
		exit(1);
	}

	// 让服务器告诉所有在线用户,该用户进入聊天室
	if (head.root == 1)
	{
		sprintf(head.msg, "33[0;33m%s33[0;35m(管理员)33[0;33m进入了聊天室!33[0mn", head.name);
	}
	else
	{
		sprintf(head.msg, "33[0;33m%s进入了聊天室!33[0mn", head.name);
	}
	head.flag = 0;
	send(clientfd2, &head, sizeof(struct client), 0);
	memset(head.msg, 0, sizeof(head.msg));

	Chat_choose(&head); // 进入聊天室功能界面

	close(clientfd2);
	return 0;
}

原文地址:https://blog.csdn.net/m0_62140641/article/details/134072074

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_24632.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注