聊天室(C语言)

源码连接
https://download.csdn.net/download/G1842965496/83970608

项目面试问题
C/C++/嵌入式—面试题汇总

——基于文件编程、网络通信、数据库实现

一、项目功能介绍

1.登录、注册、密码修改
2.私聊
3.群聊:在群聊中若收到私密消息,会以消息闪烁形式通知
4.文件传输
5.管理员权限:禁言、解除禁言、踢人
6.VIP用户:独特显示用户名、不可被禁言和踢出群聊
7.心跳检测:当一用户推出聊天室或掉线,其他在线用户会收到该用户下线通知
用户上线时,其他在线用户也会接到通知

二、使用须知

1.注册用户初始为普通用户,可手动添加为管理员或VIP用户

2.文本说明 :
1).root.txt:存放管理员
2). R_num.txt:存放管理员数量
3). VIP.txt:存放VIP用户
4).V_num.txt:存放VIP用户数量
5).log.txt:群聊记录
6).info.db:存放已注册用户

3.提醒:
管理员和VIP用户均需要先注册为普通用户后,在手动添加到相应文本中

4.注意:!!!
1).由于中文字符所占字节数和英文不同,发送中文靠右边打印会出现打印错位
所需要发送中文,请在聊天函数中,将发送消息靠右打印,改为靠左打印
2).若被禁言,则为永久禁言,直到被管理员解除禁言,或服务器重启
3).若被踢出群聊,退出聊天框后重新进入即可

三、项目演示:

聊天室

四、界面展示

1.登录

2.聊天室功能界面

3.群聊(管理员界面)

五、代码展示

1.服务器

#include <stdio.h> //服务器
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <semaphore.h>
#include <sqlite3.h>
#include <string.h>
#include <unistd.h>int serverfd; //服务器socketint clientfd[100]; //客户端的socketfd,100个元素,clientfd[0]~clientfd[99]int size = 50; //用来控制进入聊天室的人数为50以内char *IP = "192.168.12.13"; //主机ip地址short PORT = 6666; //端口号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;
};int personalflag[500] = {-1};
sem_t sem[500];
sem_t sem1[500];
struct user *h;  //已登录用户头节点
struct Node *h1; //已注册用户头节点
pthread_t pti1[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");fscanf(dp, "%d", &cnt);fclose(dp);fp = fopen("./root.txt", "a+");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;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;*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);}
}void V_insert_fp(struct VIP *vip) //把文件里的存到链表里,每次打开时初始化使用
{int cnt;FILE *fp;FILE *dp;dp = fopen("./V_num.txt", "r");fscanf(dp, "%d", &cnt);fclose(dp);fp = fopen("./VIP.txt", "a+");while (cnt != 0){struct VIP *newcilent = (struct VIP *)malloc(sizeof(struct VIP)); //申请空间if (NULL == newcilent){return;}fscanf(fp, "%s \n", newcilent->name);newcilent->next = NULL;vip->next = newcilent;vip = vip->next;cnt--;}fclose(fp);
}int V_init(struct VIP **vip) //创建VIP用户链表头节点
{struct VIP *newcilent = (struct VIP *)malloc(sizeof(struct VIP));if (NULL == newcilent){return -1;}newcilent->next = NULL;*vip = newcilent;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);}
}int init(struct user **head) //创建已登录用户链表头节点
{struct user *newnode = (struct user *)malloc(sizeof(struct user));if (NULL == newnode){return -1;}newnode->c_fd = 0;newnode->name[0] = 0;newnode->next = NULL;*head = newnode;
}int init1(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;*head = newnode;
}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;*head = newnode;
}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;*head = newnode;
}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;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;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 = 0;int flag = search1(name);return flag;
}void Init() //初始化
{serverfd = socket(PF_INET, SOCK_STREAM, 0);printf("\033[0;34m创建socket成功!\033[0m\n");if (serverfd == -1){perror("\033[0;31m创建socket失败\033[0m");exit(-1);}//为套接字设置ip协议 设置端口号  并自动获取本机ip转化为网络ipstruct 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 *)&addr, sizeof(addr)) == -1){perror("\033[0;31m绑定失败\033[0m");exit(-1);}//printf("\033[0;34m绑定套接字成功!\033[0m\n");if (listen(serverfd, 100) == -1){ //监听最大连接数perror("\033[0;31m设置监听失败!\033[0m");exit(-1);}//printf("\033[0;34m监听成功!\033[0m\n");
}void insertdb(struct Node *head)
{char sql[500] = "\0";sprintf(sql, "insert into user (name,password) values('%s','%s');", head->name, head->password); //插入,修改,删除都可以用这条语句carryout(sql);
}void search(char *n, char *pw) //搜索密码
{struct Node *head = h1;while (head->next != NULL){head = head->next;if (strcmp(n, head->name) == 0){strcpy(pw, head->password);}}
}int search1(char *n) //搜索用户名是否存在
{int count = 0;struct Node *head = h1;while (head->next != NULL){head = head->next;if (strcmp(n, head->name) == 0){count++;//printf("count:%d\n", count);}}return count;
}int search2(char *n) //搜索是否已经登录
{//printf("搜索是否已经登录\n");int flag = 0;struct user *head = h;while (head->next != NULL){head = head->next;//printf("head.name:%s\n", 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; //该用户不是群成员
}int seek_VIP(char *n) //搜索该成员是否为VIP用户
{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 = h1;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=%d\nname=%s\n", 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] = "\0"; //写入命令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:%s\n", 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] = "\0";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:%s\n", 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();}
}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:%s\n", 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] = "\0";sprintf(sql, "update user set password='%s' where name='%s';", pw, name); //插入,修改,删除都可以用这条语句carryout(sql);
}void downdb(struct Node *head) //把已经注册的用户载入链表
{//printf("进入downdb\n");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:%s\n", errmsg);exit(2);}printf("row=%d    column=%d\n", row, column);if (row == 0){return;}int i, j;int num;char name[20] = "\0";char password[20] = "\0";printf("开始载入\n");for (i = 1; i <= row; i++){for (j = 0; j < column; j++){num = i * column + j;//printf("%s\n",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:%s\n", head.msg);struct Forbit *p = F_head;struct Root *Root = r_head;struct user *all = h;struct Group *group = G_head;struct VIP *vip = V_head;int temp = head.root;int flag = seek_VIP(head.name);if (flag == 1){head.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[500];int toc_fd = searchuser(h, head.to_name);if (toc_fd == 1){toc_fd = searchuser(h, head.name);sprintf(buf, "\033[0;33m该用户未登录!\033[0m\n");send(toc_fd, buf, sizeof(head.msg), 0);memset(buf, 0, sizeof(buf));}else if (toc_fd == 0){toc_fd = searchuser(h, head.name);sprintf(buf, "\033[0;31m该用户不存在!\033[0m\n");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, "\033[34m%s\033[35m(私密消息)\033[36m:%s\033[0m\n", head.name, head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}else{sprintf(buf, "\033[0;34m%s\033[36m:%s\033[0m\n", head.name, head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}if (head.root == 2){sprintf(buf, "\033[33m(VIP)%s", head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}if (flag1 == 1){sprintf(buf, "\033[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[500];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(h, head.name);int ret = seek_forbit(head.name);if (ret == 1) //该用户处于禁言状态{sprintf(buf, "\033[0;31m发送失败!你已被禁言!\033[0m\n");send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));return 0;}if (head.root == 2){sprintf(buf, "\033[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(h, 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(h, head.name);sprintf(buf, "\033[0;31m发送失败!你不是该群成员!\033[0m\n");send(t_fd, buf, strlen(buf) + 1, 0);}}else if (3 == head.flag) //文件传输{char buf[500];int toc_fd = searchuser(h, head.to_name);printf("%s向%s发送文件!\n", head.name, head.to_name);sprintf(buf, "\033[0;33m%s向你发来一份文件!\033[0m\n", 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%s\n", head.msg);send(toc_fd, head.msg, sizeof(head.msg), 0); //发送文件内容}else if (4 == head.flag) //修改密码{int toc_fd = searchuser(h, head.name);printf("等待用户进行密码修改\n");int flag, flag1;char pw1[20] = "\0";char pw2[20] = "\0";char name[20] = "\0";read(toc_fd, pw1, 20);update_pw(head.name, pw1);updatedb(head.name, pw1);printf("用户密码修改成功\n");}else if (5 == head.flag){char buf[500];int c_fd = searchuser(h, head.name);int toc_fd = searchuser(h, 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, "\033[0;31m禁言失败!用户%s为管理员!\033[0m\n", 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, "\033[0;31m禁言失败!用户%s为VIP用户!\033[0m\n", 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, "\033[0;31m禁言失败!用户%s已处于禁言状态,不可重复禁言!\033[0m\n", head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}else{sprintf(buf, "\033[0;33m禁言成功!你已将用户%s禁言!\033[0m\n", head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));sprintf(buf, "\033[0;31m管理员%s将你禁言!\033[0m\n", 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, "\033[0;33m用户%s未被禁言!\033[0m\n", head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}else{sprintf(buf, "\033[0;33m解除成功!你已将用户%s解除禁言!\033[0m\n", head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));sprintf(buf, "\033[0;33m你被管理员%s解除禁言!\033[0m\n", 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, "\033[0;31m踢出群聊失败!用户%s为管理员!\033[0m\n", 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, "\033[0;31m踢出群聊失败!用户%s为VIP用户!\033[0m\n", 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, "\033[0;31m踢出群聊失败!用户%s不是该群成员!\033[0m\n", head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}else{sprintf(buf, "\033[0;33m用户%s已被你踢出群聊!\033[0m\n", head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));sprintf(buf, "\033[0;33m你被管理员%s踢出群聊!\033[0m\n", 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 = h;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("\033[0;31m退出:%s 退出了聊天室.\033[0m\n", user);char buf[500];sprintf(buf, "\033[0;31m%s退出了聊天室. \033[0m\n", 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] = "\0";char pw2[20] = "\0";char name[20] = "\0";read(c_fd, name, 20);printf("登录时接收到的用户名:%s\n", name);flag2 = search1(name); //检查该用户名是否已经进行注册write(c_fd, &flag2, sizeof(flag2));if (flag2 > 0) //已经注册,可以登录{flag1 = search2(name); //查找该用户是否已经登录printf("用户%s的登录状态:%d\n", 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("\033[0;33m管理员模式登录中!\033[0m\n");int count = 0;while (root != NULL){if (strcmp(root->root, name) == 0){write(c_fd, &m_root, sizeof(m_root));count++;printf("\033[0;33m管理员%s进入了聊天室!\033[0m\n", name);break;}root = root->next;}if (count == 0) //该用户不是管理员{printf("\033[0;33m该用户不是管理员!\033[0m\n");m_root = 0;write(c_fd, &m_root, sizeof(m_root));logon(c_fd);}}read(c_fd, pw1, 20);search(name, pw2);if ((strcmp(pw1, pw2)) == 0){flag = 1;write(c_fd, &flag, sizeof(flag));insert_tail(h, c_fd, name); //存入在线用户链表int vip = seek_VIP(name);printf("vip:%d\n", 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 register1(int c_fd) //注册
{char pw[20] = "\0";char name[20] = "\0";printf("等待客户机进行注册\n");read(c_fd, name, 20);printf("读取到用户名:%s\n", name);int flag = search1(name); //检查该用户名是否已经存在write(c_fd, &flag, sizeof(flag));if (flag == 0) //不存在,可以注册,读取密码{read(c_fd, pw, 20);struct Node *text = insert(h1, name, pw);printf("用户注册成功\n用户名:%s\n密码:%s\n", text->name, text->password);insertdb(text);return 1;}else if (flag == 1){printf("用户名已存在,等待客户机重新输入\n");register1(c_fd);}
}void *r_or_l(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); //登录成功返回1if (flag){personalflag[i] = 0;sem_post(&sem1[i]); //成功则向聊天功能发送一个信号量,让其取消阻塞break;}}else if (y == 2){register1(c_fd);}}pthread_join(pti1[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(&head);init1(&head1);initdb();downdb(head1);F_init(&head2); //创建禁言人员头节点r_init(&head3); //创建管理员用户头节点G_init(&head4); //创建群内成员用户头节点V_init(&head5); //创建VIP用户头节点h = head;h1 = 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);//调用accept进入堵塞状态,等待客户端的连接if (fd == -1){printf("客户端连接出错...\n");continue;}printf("客服端连接成功\n");iandfd.i = i;iandfd.c_fd = fd;int ret = pthread_create(&pti1[i], NULL, r_or_l, (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){//记录客户端的socketclientfd[i] = fd;printf("线程号= %d\n", fd); ////有客户端连接之后,启动线程给此客户服务pthread_t tid;pthread_create(&tid, 0, server_thread, &iandfd);}if (size == i){//发送给客户端说聊天室满了char *str = "\033[0;31m对不起,聊天室已经满了!\033[0m\n";send(fd, str, strlen(str), 0);close(fd);}i++;}
}int main()
{system("clear");Init();   //初始化server(); //服务器开始工作close(serverfd);return 0;
}

2.客服端

#include <stdio.h> //客户端
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int clientfd2; //客户端socketchar *IP = "192.168.12.13"; //服务器的IPshort PORT = 6666; //服务器服务端口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中的"-"数量相匹配,两者相乘等于100const int Step = 5;// 总共需要多少进度标志,即LastStr中"-"的数量const int iconMaxNum = 20;// 每隔100ms打印一次,这里同时每隔200ms会让进度加1const int Printinterval = 20000;// ****************************************************************for (int i = 0; i <= 100; ++i){// -------------- 打印进度条 --------------printf("\033[0;34m[\033[0m");int currentindex = i / Step;for (int j = 0; j < iconMaxNum; ++j){if (j < currentindex){printf("\033[0;34m%c\033[0m", Progressicon); // 打印进度条标志}else{printf(" "); // 未达进度则打印空格}}printf("\033[0;34m]\033[0m ");// ----------------------------------------// -------- 打印数字进度 ----------printf("\033[0;34m%3d%%\033[0m", i);fflush(stdout);// -------------------------------usleep(Printinterval);for (int j = 0; j < strlen(LastStr); ++j){printf("\b"); // 回删字符,让数字和进度条原地变化}fflush(stdout);}printf("\n");
}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 Clear_news() //清除输入框内容
{printf("\033[33;12H"); //将光标移动回到消息发送区printf("\033[K");      //清除发送框内容printf("\033[72C");printf("\033[0;34m|                 |\033[0m\n");printf("\033[33;12H \033[0;36m"); //将光标移动回到消息发送区printf("\033[36m");
}void Clear_hint() //清除系统提示信息
{printf("\033[31;4H"); //将光标系统提示信息头位置printf("\033[K");      //清除系统提示信息printf("\033[80C");printf("\033[0;34m|                 |\033[0m\n");  printf("\033[33;12H \033[0;36m"); //将光标移动回到消息发送区
}int print_msg(char *msg, int flag) //打印消息
{if (frame == 1){if ((*msg == '\0') || (*msg == '\n') ){return 0;}char i_msg[500];printf("\033[s"); //保存光标位置fflush(stdout);if (line == 8)printf(" \033[8;4H"); //消息打印位置else if (line == 10)printf(" \033[10;4H"); //消息打印位置else if (line == 12)printf(" \033[12;4H"); //消息打印位置else if (line == 14)printf(" \033[14;4H"); //消息打印位置else if (line == 16)printf(" \033[16;4H"); //消息打印位置else if (line == 18)printf(" \033[18;4H"); //消息打印位置else if (line == 20)printf(" \033[20;4H"); //消息打印位置else if (line == 22)printf(" \033[22;4H"); //消息打印位置else if (line == 24)printf(" \033[24;4H"); //消息打印位置else if (line == 26)printf(" \033[26;4H"); //消息打印位置else if (line == 28)printf(" \033[28;4H"); //消息打印位置else if (line == 30)printf(" \033[30;4H"); //消息打印位置else if (line == 32)       //行数到达最后一行{if ((Head.root == 1) && (Head.flag == 2)){Root_frame(&Head);}else{Chat_frame(&Head);}printf(" \033[8;4H"); //消息打印位置}if (flag == 1) //打印收到的消息{printf("%s", msg);//printf("\033[0;34m |\033[0m");}else if (flag == 2) //打印发送的消息{sprintf(i_msg, "\033[0;36m%s\033[0;33m:我\033[0m\n", msg);printf("%*s", 99, i_msg);}//printf("%*s",80,msg);line = line + 2;printf(" \033[u"); //恢复光标位置fflush(stdout);}else if (frame == 0){return 0;}
}void Chat_frame(struct client *head) //打印聊天框
{system("clear");printf("\033[1;1H");fflush(stdout);line = 8;frame = 1;char name[50];char to_name[50];printf("\033[34m +===================================================================================================+\033[0m\n");printf("\033[34m |                                                                                                   |\033[0m\n");printf("\033[34m |\033[35m*********************************************\033[33m\033[1m飞飞聊天室\033[0;35m********************************************\033[34m|\033[0m\n");printf("\033[34m |                                                                                         \033[35m版本:1.0\033[34m  |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------------------------|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------|                 |\033[0m\n");printf("\033[34m |                                                                                 |\033[33m\033[1m用户:            \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 | \033[33m\033[1mw!:\033[0;36m文件发送     \033[34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------|                 |\033[0m\n");printf("\033[34m |\033[33m消息输入:\033[0;36m                                                                        \033[34m|                 |\033[0m\n");printf("\033[34m |                                                                                 |\033[35m输入\033[31mq!\033[0;35m退出聊天框\033[34m |\033[0m\n");printf("\033[34m +===================================================================================================+\033[0m\n");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, "%s\n", head->name);}if (head->flag == 2){sprintf(to_name, "群聊\n");}else{sprintf(to_name, "%s\n", head->to_name);}printf("\033[6;40H");                          //将管标移到聊天对象打印位置printf("\033[33m\033[1m%s\033[0m\n", to_name); // 聊天对象printf(" \033[9;85H");                       //将管标移到用户名打印位置printf("\033[36m\033[1m%s\033[0m \n", name); // 用户名printf(" \033[33;12H \033[0;36m"); //将光标移动回到消息发送区
}void Root_frame(struct client *head) //打印管理员群聊框
{// printf("\n");system("clear");printf("\033[1;1H");fflush(stdout);line = 8;frame = 1;char name[50];char to_name[50];printf("\033[34m +===================================================================================================+\033[0m\n");printf("\033[34m |                                                                                                   |\033[0m\n");printf("\033[34m |\033[35m*********************************************\033[33m\033[1m飞飞聊天室\033[0;35m********************************************\033[34m|\033[0m\n");printf("\033[34m |                                                                                         \033[35m版本:1.0\033[34m  |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------------------------|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------|                 |\033[0m\n");printf("\033[34m |                                                                                 |\033[33m\033[1m用户:            \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 | \033[33m\033[1m管理员权限:    \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 | \033[36m\033[1m1!:禁言         \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 | \033[36m\033[1m2!:解除禁言     \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 | \033[36m\033[1m3!:踢人        \033[0;34m |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 | \033[35m输入关键字      \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 | \033[35m进入对应权限    \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 | \033[33m\033[1mw!:\033[0;36m文件发送     \033[34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------|                 |\033[0m\n");printf("\033[34m |\033[33m消息输入:\033[0;36m                                                                        \033[34m|                 |\033[0m\n");printf("\033[34m |                                                                                 |\033[35m输入\033[31mq!\033[0;35m退出聊天框\033[34m |\033[0m\n");printf("\033[34m +===================================================================================================+\033[0m\n");sprintf(name, "%s(管理员)\n", head->name);sprintf(to_name, "群聊\n");printf("\033[6;40H");                          //将管标移到聊天对象打印位置printf("\033[33m\033[1m%s\033[0m\n", to_name); // 聊天对象printf(" \033[9;85H");                       //将管标移到用户名打印位置printf("\033[36m\033[1m%s\033[0m \n", name); // 用户名printf(" \033[33;12H \033[0;36m"); //将光标移动回到消息发送区
}void Function_frame(struct client *head)  //打印功能选择界面
{system("clear");line = 8;frame = 0;char name[50];printf("\033[34m +===================================================================================================+\033[0m\n");printf("\033[34m |                                                                                                   |\033[0m\n");printf("\033[34m |\033[35m*********************************************\033[33m\033[1m飞飞聊天室\033[0;35m********************************************\033[34m|\033[0m\n");printf("\033[34m |                                                                                         \033[35m版本:1.0\033[34m  |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------------------------|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |\033[33m\033[1m用户:            \033[0;34m|\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                    \033[0;36m1:私聊功能\033[0;34m                                   |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                    \033[0;36m2:进入群聊\033[0;34m                                   |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                    \033[0;36m3:修改密码\033[0;34m                                   |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                   \033[0;31m0:退出聊天室\033[0;34m                                  |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n"); printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |                                                                                 |                 |\033[0m\n");printf("\033[34m |---------------------------------------------------------------------------------|                 |\033[0m\n");printf("\033[34m |\033[33m消息输入:\033[0;36m                                                                        \033[34m|                 |\033[0m\n");printf("\033[34m |                                                                                 |\033[0;35m输入\033[31mq!\033[0;35m返回本界面\033[0;34m |\n");printf("\033[34m +===================================================================================================+\033[0m\n");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, "%s\n", head->name);}printf(" \033[9;85H");                       //将管标移到用户名打印位置printf("\033[36m\033[1m%s\033[0m \n", name); // 用户名printf(" \033[33;12H \033[0;36m"); //将光标移动回到消息发送区
}void print_hint(char *msg) //打印系统提示消息
{Clear_hint(); //清除系统提示信息printf("\033[31;4H"); //将光标系统提示信息头位置printf("%s",msg);      //打印提示内容printf("\033[33;12H \033[0;36m"); //将光标移动回到消息发送区
}int fileread(char *massage) //读取文件
{int flag = 0;int i;FILE *fp;char past[20];char filename[20];char buffer[500];char buffer1[500];char msg[100];sprintf(msg,"\033[0;33m请输入文件路径:\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));scanf("%s", past);getchar();Clear_news(); //清除输入框内容sprintf(msg,"\033[0;33m请输入文件名:\033[0m\n");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,"\033[0;31m文件不存在!\033[35m(任意键继续,放弃请按1)\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));scanf("%d",&flag);Clear_news(); //清除输入框内容if(flag == 1){return 0;}}else{sprintf(msg,"\033[0;33m文件打开成功!\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));fread(buffer, 1, sizeof(buffer), fp);sleep(1);//printf("\033[0;33m文件内容:\033[0;36m%s\033[0m\n", buffer);}if (strlen(buffer) == 1){sprintf(msg,"\033[0;31m文件为空!\n是否往文件里写入信息.(写入请按1,其他任意键退出)\033[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, "%s\n%s", filename, buffer);//printf("buffer1:%s\n",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[500];char msg[100];while (1){if (massage[i] == '\n'){break;}len++;i++;}strncpy(filename1, massage, len);sprintf(msg,"\033[0;33m文件名:\033[0;36m%s\033[0m\n", filename1);print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);strcat(filename, filename1);strcpy(buffer, &massage[len + 1]);//printf("\033[0;33m文件内容:\033[0;36m%s\033[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], "\0");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[0m\n");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("\033[0;31m该用户不是管理员!\033[0m\n");printf("\033[0;33m是否继续以管理员身份登录?(任意键继续,按1返回初始界面!)\033[0m\n");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[0m\n");scanf("%s", head->password);getchar();write(clientfd2, head->password, 20);read(clientfd2, &flag, sizeof(flag));if (flag){printf("\033[0;34m正在登录中\033[0m");int vip;read(clientfd2, &vip, sizeof(vip)); //判断该用户是否为VIPif (vip == 1){head->root = 2;}progress_bar();return 1;}else if (flag == 0){printf("\33[0;31m用户名或密码输入错误!\033[0m\n");printf("\33[0;31m请重新输入!\033[0m\n");search(head);}}else if (flag1 != 0){printf("\33[0;31m该用户名已在其他客户端登录!\033[0m\n");printf("\33[0;31m请重新输入!\033[0m\n");search(head);}}else if (flag2 == 0){printf("\33[0;31m该用户不存在!\33[0m\n");printf("\33[0;31m请重新输入!\033[0m\n");search(head);}
}int sign_in(struct client *head) //用户帐号注册
{//获取注册用户名和密码while (1){int flag = -1;// printf("flag:%d\n", flag);printf("\33[0;34m请输入用户名:\33[0m\n");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[0m\n");scanf("%s", head->password);getchar();if (head->name[0] != '\0'){int ret = write(clientfd2, head->password, strlen(head->password));if (ret > 0){printf("\033[0;34m注册中\033[0m");progress_bar();printf("\033[0;34m注册成功!\033[0m\n");printf("\033[0;36m用户名:%s\033[0m\n", head->name);printf("\033[0;36m用户密码:%s\033[0m\n\n", head->password);break;}}}else if (flag > 0){printf("\033[0;31m用户名已被占用!\n请重新输入!\n\033[0m");}}
}void n_init() //初始化
{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("\033[0;31m无法连接到服务器\033[0m");exit(-1);}printf("\033[0;33m客户端启动成功\n\033[0m");
}void *recv_thread(void *p) //用于接收聊天信息
{char flag[20] = {};strcpy(flag, "flag==3");char buf[500] = {};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 == '\0'){str++;}printf("\033[s"); //保存光标位置fflush(stdout);//printf("\033[?25l");  //隐藏光标printf("\033[31;4H"); //将光标移动到系统提示信息头位置printf("\033[0;33m文件接收中\033[0m");progress_bar();memset(msg, 0, sizeof(msg));//printf("\033[33;12H \033[0;36m"); //将光标移动回到消息发送区//print_msg(buf, 3); //打印接收文件的消息sprintf(msg, "\033[0;35m文件接收成功!\033[0;36m");print_msg(msg,1);//打印信息sleep(1);memset(msg, 0, sizeof(msg));filewrite(str);  //写文件//printf(" \033[u"); //恢复光标位置fflush(stdout);//Clear_news(); //清除输入框内容printf("\033[36m");//printf("1\n");// Clear_news(); //清除输入框内容printf("\033[?25h"); //显示光标// printf("\033[12C \033[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,"\033[0;33m请输入发送对象 :\033[0m\n");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("\033[?25l");  //隐藏光标printf("\033[31;4H"); //将光标系统提示信息头位置printf("\033[0;33m文件发送中\033[0m");progress_bar();memset(msg, 0, sizeof(msg));printf("\033[33;12H \033[0;36m"); //将光标移动回到消息发送区printf("\033[?25h"); //显示光标sprintf(msg,"\033[0;33m文件发送成功!\033[0;35m(按任意键继续,退出请按1)\033[0;36m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));scanf("%d", &y);getchar();Clear_news(); //清除输入框内容//printf("1\n");// Clear_news(); //清除输入框内容printf("\033[36m");}else{sprintf(msg,"\033[0;31m文件为空,无法传送!\033[0m\n");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,"\033[0;35m请输入私聊对象:\033[0m");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg)); scanf("%s", head->to_name);getchar();Clear_news(); //清除输入框内容//printf("\033[0;35m已进入与%s的私聊界面!\033[0m\n", 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("\033[0;35m欢迎进入群聊!\033[0m\n");}Head = *head;//打印聊天框if ((head->root == 1) && (head->flag == 2)){Root_frame(head);}else{Chat_frame(head);}//消息输入while (1){char buf[500] = {};//printf("\033[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, "\033[0;33m\033[1m我:\033[0;36m%s\033[0m\n", head->msg);print_msg(buf, 1); //靠左边打印发送的消息*///靠右边打印发送的消息print_msg(head->msg, 2); //靠右边打印发送的消息if (head->flag == 2){if (head->root == 1){sprintf(buf, "\033[0;33m(管理员)\033[0;34m%s\033[0;36m:%s\033[0m\n", head->name, head->msg);}else{sprintf(buf, "\033[0;34m%s:\033[0;36m%s\033[0m\n", 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,"\033[0;33m请输入旧密码:\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));scanf("%s", pw);Clear_news(); //清除输入框内容if (strcmp(head->password, pw) == 0){sprintf(msg,"\033[0;33m请输入新密码:\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));memset(pw, 0, 20);scanf("%s",pw);getchar();Clear_news(); //清除输入框内容sprintf(msg,"\033[0;33m请确认密码:\033[0m\n");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("\033[31;4H"); //将光标移动到系统提示信息头位置printf("\033[0;33m服务器修改中\033[0m");progress_bar();memset(msg, 0, sizeof(msg));sprintf(msg,"\033[0;35m密码修改成功!\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);Clear_hint();break;}else{sprintf(msg,"\033[0;31m密码修改失败,请重新输入!\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);}}else{sprintf(msg,"\033[0;31m;两次密码输入有误!请重新输入!\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);}}else{int temp = 0;sprintf(msg,"\033[0;31m密码输入错误!是否继续输入\033[0;35m(任意键继续,放弃请按1)\033[0m\n");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,"\033[0;33m请输入禁言用户:\033[0m\n");//         print_hint(msg);//打印系统提示信息while (1){if (1 == flag){head->forbit = 1;sprintf(msg,"\033[0;33m请输入禁言用户:\033[0m\n");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,"\033[0;33m请输入解禁用户:\033[0m\n");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,"\033[0;33m请输入被踢用户:\033[0m\n");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,"\033[0;33m\033[1m是否继续:\033[0;33m(任意键继续,结束请按1)\033[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,"\033[0;34m请输入功能选择项:\033[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,"\033[0;33m退出成功!欢迎下次使用!\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));close(clientfd2);printf("\033[35,0H");exit(-1);break;default:printf("\033[0;31m选择有误!请重新输入!\033[0m\n");print_hint(msg);//打印系统提示信息memset(msg, 0, sizeof(msg));break;}}
}void log_in(struct client *head) //登录&注册
{int select;system("clear");printf("\033[0;34m+================================================================+\033[0m\n");printf("\033[0;34m|                                                                |\033[0m\n");printf("\033[0;34m|***********************\033[0;33m欢迎来到飞飞聊天室\033[0;34m***********************|\033[0m\n");printf("\033[0;34m|                                                      版本:1.0 |\033[0m\n");printf("\033[0;34m|----------------------------------------------------------------|\033[0m\n");printf("\033[0;34m|                                                               |\033[0m\n");printf("\033[0;34m|                         \033[0;36m1.用户登录                            \033[0;34m|\033[0m\n");printf("\033[0;34m|                                                               |\033[0m\n");printf("\033[0;34m|                         \033[0;36m2.帐号注册                             \033[0;34m|\033[0m\n");printf("\033[0;34m|                                                               |\033[0m\n");printf("\033[0;34m|                         \033[0;33m3.管理员登录                           \033[0;34m|\033[0m\n");printf("\033[0;34m|                                                               |\033[0m\n");printf("\033[0;34m|                        \033[0;31m 0:退出聊天室                           \033[0;34m|\033[0m\n");printf("\033[0;34m|                                                               |\033[0m\n");printf("\033[0;34m+================================================================+\033[0m\n");printf("\n");while (1){printf("\033[0;34m请选择:\033[0m\n");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("\033[0;33m退出成功!\033[0m\n");printf("\033[0;33m欢迎下次使用!\033[0m\n");exit(-1);break;}else{printf("\033[0;31m选择错误!请重新输入!\033[0m\n");}}
}int main()
{struct client head;n_init();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, "\033[0;33m%s\033[0;35m(管理员)\033[0;33m进入了聊天室!\033[0m\n", head.name);}else{sprintf(head.msg, "\033[0;33m%s进入了聊天室!\033[0m\n", 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;
}

聊天室(C语言)- 基于文件编程、网络通信、数据库实现相关推荐

  1. 视频教程-实战Go语言:多人聊天室-Go语言

    实战Go语言:多人聊天室 多年互联网从业经验: 有丰富的的企业网站.手游.APP开发经验: 曾担任上海益盟软件技术股份有限公司项目经理及产品经理: 参与项目有益盟私募工厂.睿妙影音家庭物联网设备.手游 ...

  2. 视频教程-网络聊天室Java基础版(Socket_Swing编程)仿QQ聊天-Java

    网络聊天室Java基础版(Socket_Swing编程)仿QQ聊天 IT行业资深从业者,7年资深Java高级开发,Java架构师.曾就职银行.电信等行业多家上市公司.担任项目负责人,软件架构师.有丰富 ...

  3. C语言多文件编程基本格式

    1.背景: 用一个丢骰子的简单案例熟悉了C语言多文件编程该咋写 2.格式 (1)主函数文件main.c //文件头part1:所有要使用的函数#include ;#include ;#include ...

  4. 【C语言】C语言基础重点08——C语言多文件编程分析

    原文链接 C语言多文件编程分析 一.C语言的可执行文件的构建机制 从人编写的.c文件(源文件)到CPU能看懂的可执行文件,需要经过很多步,不能直接生成. 预处理:.c文件经过预处理,决定了哪些编译,哪 ...

  5. c语言tcp多线程聊天,基于tcp和多线程的多人聊天室-C语言

    之前在学习关于网络tcp和多线程的编程,学了知识以后不用一下总绝对心虚,于是就编写了一个基于tcp和多线程的多人聊天室. 具体的实现过程: 服务器端:绑定socket对象->设置监听数-> ...

  6. java swing多人聊天室_使用java swing和socket编程实现简单的多人聊天室-Go语言中文社区...

    完成效果如下 客户端: 服务器端: 客户端功能: 输入服务器对应的端口,IP号,用户名(昵称),可以互相发送消息 服务器端功能: 输入端口号,启动,可以向所有客户端发送消息,IP地址自动获取. 下面是 ...

  7. 聊天室c语言程序,socket 多线程聊天室的实现(C语言)

    人生不止眼前的苟且,代码也不止数据的增删改查,也有有趣的网络编程.如何用C语言做一个简单的服务器和客户端,实现一个聊天室程序呢?这里就简单的写一下博主的实现. 一.程序需求 实现一个简单的服务器,包括 ...

  8. java web聊天室论文_基于Java网页版聊天室的设计与实现毕业论文含开题报告及文献综述(样例3)...

    <基于Java网页版聊天室的设计与实现毕业论文含开题报告及文献综述.doc>由会员分享,可免费在线阅读全文,更多与<基于Java网页版聊天室的设计与实现毕业论文含开题报告及文献综述& ...

  9. java web聊天室论文_基于Java网页版聊天室的设计与实现毕业论文含开报告及文献综述.doc...

    基于Java网页版聊天室的设计与实现毕业论文含开报告及文献综述 本科生毕业论文(设计) 题 目: 基于Java网页版聊天室的设计与实现 姓 名:学 院: 理学院 专 业: 信息与计算科学 班 级: 信 ...

最新文章

  1. 在一台Mac上不同平台同时使用多个Git账号
  2. python opencv 画米字形状
  3. 查询手机号段对应地区编码_想知道海关统计数据吗?这里有详细的查询教程
  4. [转:有种感觉叫佩服]一个程序员的奋斗历程
  5. mysql 5.5 免安装_mysql 5.5.56免安装版配置方法
  6. Hibernate READ_WRITE CacheConcurrencyStrategy如何工作
  7. 玩转算法面试-第四章查找值之leetcod相关笔记
  8. 牛客网SQL篇刷题篇(3-10)
  9. ggplot2之配对数据差异比较及结果可视化
  10. python语言常量_python---01.各类计算机语言,python历史,变量,常量,数据类型,if条件...
  11. linux 安装 wkhtmltox
  12. Bearcat pomelo game 实战 -- treasures
  13. Ribbon和Feign的对比-带简易例子
  14. Nginx在Linux下的安装部署
  15. 【高等数学】基本求导法则与导数公式
  16. 博客推广技巧:如何通过博客推广并实现营销
  17. SpringBoot笔记(五)Linux系统与项目部署
  18. PHP的strtolower()和strtoupper()函数在安装非中文系统的服务器下可能会将汉字转换为乱码
  19. 电子计算机按钮说明,电子计算器常用按键功能说明
  20. python入门教材带视频_Python全套,从入门到进阶。视频,电子书

热门文章

  1. (一)基于Flink电商用户画像项目:项目概述
  2. 天地图各级下的比例尺
  3. 地震勘探基础(十二)之地震偏移处理
  4. Spinning Wheels_usaco3.2_模拟
  5. 设为首页-加入收藏-联系我们的代码
  6. Linux查看防火墙,开放端口
  7. 服务型机器人的“立春”来临了吗?
  8. Wireshark对IMAP抓包分析
  9. pandas获取csv的指定行和列
  10. 为电子版微积分算一算经济账