网络编程项目:linux下基于C/S架构的聊天室

来源:互联网 发布:电商平台软件 编辑:程序博客网 时间:2024/05/18 00:53

一、项目要求:

1. 采用 Client/Server 架构 

2. Client A 登陆聊天服务器前,需要注册自己的 ID 和密码 
3. 注册成功后,Client A 就可以通过自己的 ID 和密码登陆聊天服务器 
4. 多个 Client X 可以同时登陆聊天服务器之后,与其他用户进行通讯聊天 
5. Client A 成功登陆后可以查看当前聊天室内其他在线用户 Client x 
6. Client A 可以选择发消息给某个特定的 Client X,即”悄悄话”功能 
7. Client A 可以选择发消息全部的在线用户,即”群发消息”功能 
8. Client A 在退出时需要保存聊天记录 

9. Server 端维护一个所有登陆用户的聊天会的记录文件,以便备查 

10.可以进行文件传输

二、附加功能:

1. Server 可以内建一个特殊权限的账号 root,即超级管理员,用于管理聊天室 
2. Admin 可以将某个 Client X “提出聊天室” 
3. Admin 可以将某个 Client X ”设为只能旁听,不能发言,即禁言功能


三、项目所需相关知识点:网络编程(C/S模型的架构),文件I/O编程(将用户信息和聊天记录保存在数据库文件中,文件传输),多进程或多线程编程(一个服务器可以连接多个客户端)


四、项目使用(测试)说明:将client.c, client.c和server.c分别置于不同目录下,编译命令:gcc  client.c  -o  client   -lpthread  -lsqlite3

运行命令:./client     (server.c同样编译运行)


五、项目代码

服务器:

#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <arpa/inet.h>#include <pthread.h>#include <sqlite3.h>#include <sys/stat.h>#include <fcntl.h>#define SIZE 20#define PORT  9999#define TRUE   1#define FALSE -1struct Usr{char name[SIZE];int socket;int flag;//用来判断该用户是否被禁言,没有被禁设为0,被禁设为1};struct Msg{struct Usr usr[SIZE];char msg[1024];char buf[1024];char name[SIZE];char fromname[SIZE];char toname[SIZE];char password[SIZE];int cmd;int filesize;int flag;    //用来判断用户权限   0代表普通用户,1代表超级用户};struct Usr usr[SIZE];int count;pthread_mutex_t mutex; //互斥锁,用来避免两个客户端同时访问全局变量//查看在线用户void see_online(int client_socket, struct Msg *msg){int i;for(i=0; i<20; i++){msg->usr[i] = usr[i];}write(client_socket, msg, sizeof(struct Msg));}//保存一条聊天记录void insert_record(struct Msg *msg){sqlite3 * database;int ret = sqlite3_open("allrecord.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return ;}//获取系统当前时间time_t timep;      char s[30];       time(&timep);      strcpy(s,ctime(&timep));int count = strlen(s);s[count-1] = '\0';char *errmsg = NULL;char *sql = "create table if not exists allrecord(time TEXT,fromname TEXT,toname TEXT,word TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("聊天记录表创建失败:%s\n", errmsg);return;}char buf[100];sprintf(buf, "insert into allrecord values('%s','%s','%s','%s')",s,msg->fromname, msg->toname,msg->msg);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("添加聊天记录失败:%s\n", errmsg);return ;}sqlite3_close(database);return ;}//群聊void chat_group(int client_socket, struct Msg *msg){int i = 0;for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(msg->fromname, usr[i].name) == 0){break;}}if(usr[i].flag == 0)     //判断该用户有没有被禁言{printf ("%s 发一次群消息\n", msg->fromname);insert_record(msg);for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(msg->fromname, usr[i].name) != 0){write (usr[i].socket, msg, sizeof(struct Msg));}}}else{msg->cmd = 1003;write (client_socket, msg, sizeof(struct Msg));}}//私聊void chat_private(int client_socket, struct Msg *msg){int i;for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(msg->fromname, usr[i].name) == 0){break;}}if(usr[i].flag == 0){printf("%s给%s发了一条消息\n", msg->fromname, msg->toname);insert_record(msg);for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(usr[i].name, msg->toname)==0){write (usr[i].socket, msg, sizeof(struct Msg));break;}}}else{msg->cmd = 1003;write (client_socket, msg, sizeof(struct Msg));}}//获取文件大小int file_size(char* filename)  {      struct stat statbuf;      stat(filename,&statbuf);      int size=statbuf.st_size;        return size;  }//上传文件void send_file(int client_socket, struct Msg *msg){printf("用户%s在聊天室内上传了一个文件%s\n",msg->fromname,msg->msg);int i;for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(usr[i].name, msg->fromname) != 0){write (usr[i].socket, msg, sizeof(struct Msg));break;}}int fd = open(msg->msg, O_RDWR | O_CREAT, 0777);if(fd == -1){perror("open");printf("文件传输失败\n");}int size = msg->filesize;char buf[65535];memset(buf, 0, 65535);int ret = read(client_socket, buf, size);if(ret == -1){perror("read");return;}write(fd, buf, ret);close(fd);}//从用户数据库删除一个用户void del_fromsql(struct Msg *msg){sqlite3 * database;int ret = sqlite3_open("usr.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return;}char *errmsg = NULL;char buf[100];sprintf(buf, "delete from usr where name = '%s'", msg->name);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("删除用户失败:%s\n", errmsg);return;}sqlite3_close(database);return;}//注销用户void logout(int client_socket,struct Msg *msg){int i,j;for(i=0; i<count; i++){if(strcmp(msg->name, usr[i].name) == 0)break;}for(j=i; j<count; j++){usr[j] = usr[j+1];}count--;printf("正在注销用户%s\n",msg->name);del_fromsql(msg);write(client_socket, msg, sizeof(struct Msg));return;}//用户下线void off_line(int client_socket,struct Msg *msg){pthread_mutex_lock(&mutex);    // 抢锁printf("用户%s下线\n",msg->name);int i,j;for(i=0; i<count; i++){if(strcmp(msg->name, usr[i].name) == 0)break;}for(j=i; j<count; j++){usr[j] = usr[j+1];}count--;pthread_mutex_unlock(&mutex);  // 解锁write(client_socket, msg, sizeof(struct Msg));return;}//用户下载文件void download_file(int client_socket,struct Msg *msg){printf("用户%s下载了文件%s\n", msg->name, msg->msg);int size = file_size(msg->msg);msg->filesize = size;write(client_socket, msg, sizeof(struct Msg));usleep(100000);int fd = open(msg->msg, O_RDONLY, 0777);if(fd == -1){perror("open");printf("文件下载失败\n");}char buf[65535];memset(buf, 0, 65535);int ret = read(fd, buf, size);if(ret == -1){perror("read");return;}    write(client_socket, buf, ret);close(fd);}//设置禁言void forbid_speak(int client_socket,struct Msg *msg){msg->cmd = 1003;printf("用户%s已被禁言\n",msg->msg);pthread_mutex_lock(&mutex);int i;for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg)==0){write (usr[i].socket, msg, sizeof(struct Msg));usr[i].flag = 1;break;}}pthread_mutex_unlock(&mutex);}//解除禁言void relieve_speak(int client_socket,struct Msg *msg){msg->cmd = 1004;printf("用户%s已被解除禁言\n",msg->msg);pthread_mutex_lock(&mutex);int i;for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg)==0){write (usr[i].socket, msg, sizeof(struct Msg));usr[i].flag = 0;break;}}pthread_mutex_unlock(&mutex);}//踢出聊天室void kickout_room(int client_socket,struct Msg *msg){msg->cmd = 1005;printf("用户%s已被踢出聊天室\n",msg->msg);pthread_mutex_lock(&mutex);int i;for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg) != 0){//给在线用户通知某某某被踢出聊天室write (usr[i].socket, msg, sizeof(struct Msg));}}pthread_mutex_unlock(&mutex);for (i = 0; i < SIZE; i++){if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg) == 0){break;}} msg->cmd = 1006;//strcpy(msg->name,msg->msg);//off_line(usr[i].socket,msg);write (usr[i].socket, msg, sizeof(struct Msg));}//超级用户void surperusr(int client_socket){struct Msg msg;while(1){int ret = read(client_socket, &msg, sizeof(msg));if (ret == -1){perror ("read");break;}if (ret == 0){printf ("客户端退出\n");break;}switch (msg.cmd){case 1:see_online(client_socket, &msg);break;case 2:chat_group(client_socket, &msg);break;case 3:chat_private(client_socket, &msg);break;case 6:forbid_speak(client_socket, &msg);  // 设置禁言break;                           case 7:                         relieve_speak(client_socket,&msg);  // 解除禁言break;                           case 8:  off_line(client_socket,&msg);return;case 9:  kickout_room(client_socket,&msg);   // 踢出聊天室break;}}}//普通用户void commonusr(int client_socket){struct Msg msg;while(1){int ret = read(client_socket, &msg, sizeof(msg));if (ret == -1){perror ("read");break;}if (ret == 0){printf ("客户端退出\n");break;}switch (msg.cmd){case 1:see_online(client_socket, &msg);break;case 2:chat_group(client_socket, &msg);break;case 3:chat_private(client_socket, &msg);break;case 6:send_file(client_socket, &msg);break;case 7:logout(client_socket,&msg);return;case 8:                  off_line(client_socket,&msg);return;case 9:download_file(client_socket,&msg);break;}}}//添加到在线用户列表void add_usr(struct Msg *msg, int client_socket){pthread_mutex_lock(&mutex);    // 抢锁strcpy(usr[count].name, msg->name);usr[count].socket = client_socket;count++;pthread_mutex_unlock(&mutex);  // 解锁}// 注册void reg(int client_socket, struct Msg *msg){//查找用户是否已经被注册printf("正在查找该用户是否被注册...\n");if(find_name(msg) == TRUE){printf("用户%s已经被注册\n",msg->name);msg->cmd = 0;}else{if(insert_sql(msg) == TRUE){msg->cmd = 1;printf("用户%s成功添加到数据库\n",msg->name);}}write(client_socket, msg, sizeof(struct Msg));}// 登陆void login(int client_socket, struct Msg *msg){int flag1 = 0; //用来判断该用户有没有成功登陆  1代表成功//检查该用户有没有注册printf("正在查找该用户有没有注册...\n");if(find_name(msg) == TRUE){if(find_np(msg) == TRUE){if(check_ifonline(msg) == TRUE){msg->cmd = 3;printf("用户%s已经登陆过了\n",msg->name);}else{msg->cmd = 0;printf("检查该用户权限\n");if(check_root(msg) == TRUE){printf("该用户是超级用户\n");msg->flag = 1;}else{printf("该用户是普通用户\n");msg->flag = 0;}printf("用户%s登陆成功\n",msg->name);flag1 = 1;add_usr(msg, client_socket);    //添加到在线用户列表}}else{msg->cmd = 1;printf("用户%s密码输入错误\n",msg->name);}}else{msg->cmd = 2;printf("用户%s还没有注册\n",msg->name);}write(client_socket, msg, sizeof(struct Msg));if(flag1 == 1){if(msg->flag == 1)surperusr(client_socket);if(msg->flag == 0)commonusr(client_socket);}}//查看用户权限int check_root(struct Msg *msg){if(strcmp(msg->name, "root") == 0)return TRUE;else return FALSE;}void* hanld_client(void* v){struct Msg msg;int client_socket = (int)v;while(1){// 从客户端读一个结构体数据int ret = read(client_socket, &msg, sizeof(msg));if (ret == -1){perror ("read");break;}// 代表客户端退出if (ret == 0){printf ("客户端退出\n");break;}printf("从客户端读到一个用户:%s, %s, %d\n", msg.name, msg.password, msg.cmd);switch (msg.cmd){case 1:reg(client_socket, &msg);break;case 2:login(client_socket, &msg);break;}}close (client_socket);}//检查该用户是否在线int check_ifonline(struct Msg *msg){int i;for(i=0; i<count; i++){if(strcmp(msg->name, usr[i].name) == 0)return TRUE;}if(i == count)return FALSE;}//查找用户名int find_name(struct Msg *msg){sqlite3 * database;int ret = sqlite3_open("usr.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return FALSE;}char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select name from usr";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("用户查找失败:%s\n", errmsg);return FALSE;}int i;for(i=0; i<nrow+1; i++){if(strcmp(resultp[i], msg->name) == 0)return TRUE;}return FALSE;}//查找用户名和密码int find_np(struct Msg *msg){sqlite3 * database;int ret = sqlite3_open("usr.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return FALSE;}char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from usr";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("用户查找失败:%s\n", errmsg);return FALSE;}int i;for(i=0; i<(nrow+1)*ncolumn; i++){if(strcmp(resultp[i], msg->name) == 0 &&strcmp(resultp[i+1], msg->password) == 0)return TRUE;}return FALSE;}//添加用户到数据库int insert_sql(struct Msg *msg){sqlite3 * database;int ret = sqlite3_open("usr.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return FALSE;}char *errmsg = NULL;char buf[100];sprintf(buf, "insert into usr values('%s','%s')", msg->name, msg->password);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("添加用户失败:%s\n", errmsg);return FALSE;}sqlite3_close(database);return TRUE;}//建立所有用户的聊天记录数据库void setup_record(){sqlite3 * database;int ret = sqlite3_open("allrecord.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return;}char *errmsg = NULL;char *sql = "create table if not exists allrecord(time TEXT,fromname TEXT,toname TEXT,word TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("聊天记录表创建失败:%s\n", errmsg);return;}sqlite3_close(database);return;}//建立用户数据库,并在里面添加超级用户int setup_sql(){sqlite3 * database;int ret = sqlite3_open("usr.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return FALSE;}char *errmsg = NULL;char *sql = "create table if not exists usr(name TEXT,password TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("用户表创建失败:%s\n", errmsg);return FALSE;}struct Msg msg;strcpy(msg.name, "root");strcpy(msg.password, "123");insert_sql(&msg);sqlite3_close(database);return TRUE;}// 初始化套接字,返回监听套接字int init_socket(){//1、创建socketint listen_socket = socket(AF_INET, SOCK_STREAM, 0);if (listen_socket == -1){perror ("socket");return -1;}// 2、命名套接字,绑定本地的ip地址和端口struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family  = AF_INET;     // 设置地址族addr.sin_port    = htons(PORT); // 设置本地端口addr.sin_addr.s_addr = htonl(INADDR_ANY);   // 使用本地的任意IP地址int  ret = bind(listen_socket,  (struct sockaddr *)&addr, sizeof(addr));if (ret == -1){perror ("bind");return -1;}// 3、监听本地套接字ret = listen(listen_socket, 5);if (ret == -1){perror ("listen");return -1;}printf ("等待客户端连接.......\n");return listen_socket;}// 处理客户端连接,返回与连接上的客户端通信的套接字int  MyAccept(int listen_socket){struct sockaddr_in client_addr; int len = sizeof(client_addr);int client_socket = accept(listen_socket, (struct sockaddr *)&client_addr,  &len);if (client_socket == -1){perror ("accept");return -1;}printf ("成功接收一个客户端: %s\n", inet_ntoa(client_addr.sin_addr));return client_socket;}int main(){setup_sql();setup_record();pthread_mutex_init(&mutex, NULL);int listen_socket = init_socket();while (1){// 获取与客户端连接的套接字int client_socket = MyAccept(listen_socket);// 创建一个线程去处理客户端的请求,主线程依然负责监听pthread_t id;pthread_create(&id, NULL, hanld_client,  (void *)client_socket);pthread_detach(id); }close (listen_socket);pthread_mutex_destroy(&mutex);return 0;}


客户端:

#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <arpa/inet.h>#include <time.h>#include <stdlib.h>#include <sqlite3.h>#include <sys/stat.h>#include <fcntl.h>#define PORT  9999#define SIZE 20int flag = 0;int offline = 0; //用来判断该用户有没有被踢  0代表没有,1代表被踢struct Usr{char name[SIZE];int socket;int flag;//用来判断该用户是否被禁言,没有被禁设为0,被禁设为1};struct Msg{struct Usr usr[SIZE];char msg[1024];char buf[1024];char name[SIZE];char fromname[SIZE];char toname[SIZE];char password[SIZE];int cmd;int filesize;int flag;    //用来判断用户权限   0代表普通用户,1代表超级用户};struct Msg tmp;//显示空行void display_line(){system("clear");printf("\n");printf("\n");printf("\n");printf("\n");printf("\n");printf("\n");}//显示主界面void maininterface(){display_line();printf("\t\t1、    注册\n");printf("\n");printf("\t\t2、    登陆\n");printf("\n");printf("\t\t3、    退出\n");printf("\n");printf("\n");printf("\n");printf("\n");printf("\n");printf("\n");printf("\t\t请输入指令:\n");}//显示普通用户界面void cominterface(struct Msg *msg){display_line();printf("\t\t-.- %s:\n", msg->name);printf("\n");printf("\t\t1、    查看在线用户\n");printf("\n");printf("\t\t2、    群发消息\n");printf("\n");printf("\t\t3、    发送悄悄话\n");printf("\n");printf("\t\t4、    删除聊天记录\n");printf("\n");printf("\t\t5、    查看聊天记录\n");printf("\n");printf("\t\t6、    上传文件\n");printf("\n");printf("\t\t7、    注销当前用户\n");printf("\n");printf("\t\t8、    退出当前账号\n");printf("\n");printf("\t\t9、    下载文件\n");printf("\n");printf("\t\t请输入指令:");}//显示超级用户界面void supinterface(struct Msg *msg){display_line();printf("\t\t-.- %s:\n", msg->name);printf("\n");printf("\t\t1、    查看在线用户\n");printf("\n");printf("\t\t2、    群发消息\n");printf("\n");printf("\t\t3、    发送悄悄话\n");printf("\n");printf("\t\t4、    删除聊天记录\n");printf("\n");printf("\t\t5、    查看聊天记录\n");printf("\n");printf("\t\t6、    设置禁言\n");printf("\n");printf("\t\t7、    解除禁言\n");printf("\n");printf("\t\t8、    退出当前账号\n");printf("\n");printf("\t\t9、    踢出聊天室\n");printf("\n");printf("\t\t请输入指令:");}//查看在线用户void see_online(int socketfd, struct Msg *msg){msg->cmd = 1;write(socketfd, msg, sizeof(struct Msg));//struct Usr usr[SIZE];//read(socketfd, usr, sizeof(usr));return;}//保存一条聊天记录void insert_mysql(struct Msg *msg){sqlite3 * database;int ret = sqlite3_open("person.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return ;}//获取系统当前时间time_t timep;      char s[30];       time(&timep);      strcpy(s,ctime(&timep));int count = strlen(s);s[count-1] = '\0';char *errmsg = NULL;char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("用户表创建失败:%s\n", errmsg);return;}char buf[100];sprintf(buf, "insert into person values('%s','%s','%s','%s')",s,msg->fromname, msg->toname,msg->msg);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("添加聊天记录失败:%s\n", errmsg);return ;}sqlite3_close(database);return ;}//群聊void chat_group(int socketfd, struct Msg *msg){msg->cmd = 2;printf ("\t\t请输入要发送的内容: ");//scanf("%s",msg->msg);//while(getchar() != '\n');fgets(msg->msg, 1024, stdin);int len = strlen(msg->msg);msg->msg[len-1] = '\0';strcpy(msg->fromname, msg->name);strcpy(msg->toname, "all");insert_mysql(msg);write (socketfd, msg, sizeof(struct Msg));//printf("aaa\n");}//发送悄悄话void  chat_private(int socketfd, struct Msg *msg){msg->cmd = 3;printf ("\t\t请输入要发送的对象名称: \n");fgets(msg->toname, SIZE, stdin);int len = strlen(msg->toname);msg->toname[len-1] = '\0';printf ("\t\t请输入要发送的内容: \n");fgets(msg->msg, 1024, stdin);len = strlen(msg->msg);msg->msg[len-1] = '\0';strcpy(msg->fromname, msg->name);insert_mysql(msg);write(socketfd, msg, sizeof(struct Msg));}//删除聊天记录void del_personsql(){sqlite3 * database;int ret = sqlite3_open("person.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return;}char *errmsg = NULL;char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("用户表创建失败:%s\n", errmsg);return;}char *buf = "drop table person";ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("删除聊天记录失败:%s\n", errmsg);return;}sqlite3_close(database);display_line();printf("\t\t删除成功\n");printf("\t\t按ENTER键返回\n");while(getchar() != '\n');return;}//查看聊天记录void see_record(){sqlite3 * database;int ret = sqlite3_open("person.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return ;}char *errmsg = NULL;char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("用户表创建失败:%s\n", errmsg);return;}char **resultp = NULL;int nrow, ncolumn;char *buf = "select * from person";ret = sqlite3_get_table(database, buf, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("数据库操作失败:%s\n", errmsg);return;}//判断表是否为空if(nrow == 0){display_line();printf("\t\t没有聊天记录\n");}else{int i;system("clear");printf ("nrow = %d, ncolumn = %d\n", nrow, ncolumn);printf("\t\t%s",resultp[0]);printf("\t%12s",resultp[1]);printf("\t%8s",resultp[2]);printf("\t%s",resultp[3]);for (i = 4; i < (nrow+1)*ncolumn; i++){if (i % ncolumn == 0)printf ("\n");printf ("%12s", resultp[i]);}printf ("\n");}printf("\t\t按ENTER键返回\n");while(getchar() != '\n');sqlite3_free_table(resultp);sqlite3_close(database);return;}//获取文件大小int file_size(char* filename)  {      struct stat statbuf;      stat(filename,&statbuf);      int size=statbuf.st_size;        return size;  }//上传文件void send_file(int socketfd, struct Msg *msg){msg->cmd = 6;strcpy(msg->fromname, msg->name);printf("\t\t请输入要发送的文件名:\n");fgets(msg->msg, 1024, stdin);int len = strlen(msg->msg);msg->msg[len-1] = '\0';int size = file_size(msg->msg);msg->filesize = size;write(socketfd, msg, sizeof(struct Msg));printf("\t\t正在上传,请稍后...\n");sleep(1);int fd = open(msg->msg, O_RDONLY, 0777);if(fd == -1){perror("open");printf("文件传输失败\n");}char buf[65535];memset(buf, 0, 65535);int ret = read(fd, buf, size);if(ret == -1){perror("read");return;}write(socketfd, buf, ret);close(fd);printf("\t\t上传文件成功\n");sleep(1);}//注销当前用户void logout(int socketfd, struct Msg *msg){msg->cmd = 7;write(socketfd, msg, sizeof(struct Msg));display_line();printf("\t\t正在注销...\n");}//设置禁言void forbid_speak(int socketfd, struct Msg *msg){msg->cmd = 6;printf("\t\t请输入要禁言的对象:\n");fgets(msg->msg, SIZE, stdin);int len = strlen(msg->msg);msg->msg[len-1] = '\0';write(socketfd, msg, sizeof(struct Msg));}//解除禁言void relieve_speak(int socketfd, struct Msg *msg){msg->cmd = 7;printf("\t\t请输入要解除禁言的对象:\n");fgets(msg->msg, SIZE, stdin);int len = strlen(msg->msg);msg->msg[len-1] = '\0';write(socketfd, msg, sizeof(struct Msg));}//踢出聊天室void kickout_room(int socketfd, struct Msg *msg){msg->cmd = 9;printf("\t\t请输入要踢除的对象:\n");fgets(msg->msg, SIZE, stdin);int len = strlen(msg->msg);msg->msg[len-1] = '\0';write(socketfd, msg, sizeof(struct Msg));}//下线void off_line(int socketfd, struct Msg *msg){msg->cmd = 8;write(socketfd, msg, sizeof(struct Msg));display_line();printf("\t\t正在退出...\n");}//超级用户void superusr(int socketfd, struct Msg *msg){char ch[SIZE];int x;while (1){supinterface(msg);scanf("%s",ch);while(getchar() != '\n');switch(ch[0]){case '1':                   // 查看在线用户see_online(socketfd, msg);break;case '2':                   // 群发消息chat_group(socketfd, msg);break;case '3':                   // 发送悄悄话chat_private(socketfd, msg);break;case '4':                   // 删除聊天记录del_personsql();break;case '5':                   // 查看聊天记录see_record();break;case '6':                   // 设置禁言forbid_speak(socketfd, msg);break;case '7':                   // 解除禁言relieve_speak(socketfd, msg);break;case '8':                   // 退出当前账号off_line(socketfd, msg);return;case '9':                   // 踢出聊天室kickout_room(socketfd, msg);break;default: display_line();printf("错误指令,请重新输入\n");sleep(1);break;}}}//下载文件void download_file(int socketfd, struct Msg *msg){msg->cmd = 9;printf("\t\t请输入要下载的文件名:\n");fgets(msg->msg, 1024, stdin);int len = strlen(msg->msg);msg->msg[len-1] = '\0';write(socketfd, msg, sizeof(struct Msg));}void fun(int socketfd, struct Msg *msg){printf("\n\t\t正在下载,请稍后...\n");int fd = open(msg->msg, O_RDWR | O_CREAT, 0777);if(fd == -1){perror("open");printf("文件下载失败了\n");}int size = msg->filesize;char buf[65535];memset(buf, 0, 65535);int ret = read(socketfd, buf, size);if(ret == -1){perror("read");return;}write(fd, buf, ret);close(fd);sleep(1);printf("\t\t文件下载完成\n");}//普通用户void commonusr(int socketfd, struct Msg *msg){char ch[SIZE];int x;while (1){cominterface(msg);//scanf("%s",ch);//while(getchar() != '\n');fgets(ch, SIZE, stdin);if(offline == 1){display_line();printf("\n\t\t请稍后...\n");off_line(socketfd, msg);return;}else{switch(ch[0]){case '1':                   // 查看在线用户see_online(socketfd, msg);break;case '2':                   // 群发消息chat_group(socketfd, msg);break;case '3':                   // 发送悄悄话chat_private(socketfd, msg);break;case '4':                   // 删除聊天记录del_personsql();break;case '5':                   // 查看聊天记录see_record();break;case '6':                   // 上传文件send_file(socketfd, msg);break;case '7':                   // 注销当前用户logout(socketfd, msg);return;case '8':                   // 退出当前账号off_line(socketfd, msg);return;case '9':download_file(socketfd, msg); // 下载文件break;default: display_line();printf("错误指令,请重新输入\n");sleep(1);break;}}}}//创建聊天记录数据库void set_mysql(){sqlite3 * database;int ret = sqlite3_open("person.db", &database);if (ret != SQLITE_OK){printf ("打开数据库失败\n");return;}char *errmsg = NULL;char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("聊天记录表创建失败:%s\n", errmsg);return;}sqlite3_close(database);return;}//注册函数void reg(int socketfd){struct Msg msg;memset(&msg, 0, sizeof(msg));msg.cmd = 1;display_line();printf("\t\t请输入姓名:");scanf("%s",msg.name);while(getchar() != '\n');printf("\n");printf("\t\t请输入密码:");scanf("%s",msg.password);while(getchar() != '\n');write(socketfd, &msg, sizeof(msg));read(socketfd, &msg, sizeof(msg));if(msg.cmd == 0){display_line();printf("\t\t注册失败,该用户已被注册\n");}if(msg.cmd == 1){display_line();set_mysql();      //注册成功就建立一个用户自己的数据库存放在本地,存放聊天记录printf("\t\t注册成功\n");}sleep(2);return;}//专门用来读取服务器void * readMsg(void *v) { int socketfd = (int)v; struct Msg msg; while (1) { int i;  int ret = read (socketfd, &msg, sizeof(msg)); switch (msg.cmd) { case 1:  //显示在线用户printf("\n\t\t当前在线户:\n");printf("\t\t");for(i=0; i<SIZE; i++){printf("%-5s",msg.usr[i].name);}printf("\n");break; case 2:   // 接受一条群聊信息printf ("\n%s给大家发了一条消息: %s\n", msg.name, msg.msg);insert_mysql(&msg);       //保存聊天信息break; case 3:   // 接受一条私聊信息printf ("\n%s给你发一条消息:%s\n", msg.fromname, msg.msg);insert_mysql(&msg);break; case 6:   // 收到有人上传文件的消息printf("\n用户%s上传了一份文件%s\n", msg.fromname, msg.msg);break; case 7:return; case 8:return; case 9:fun(socketfd, &msg);break; case 1003://display_line();printf("\n\t\t您已被管理员禁言\n");//sleep(1);    break; case 1004:printf("\n您已被管理员解除禁言\n");    break; case 1005:printf("\n用户%s已被管理员踢出聊天室\n",msg.msg);    break; case 1006:    display_line();printf("\n\t\t您已被管理员踢出聊天室\n");printf("\t\t按ENTER键返回主菜单\n");offline = 1;//off_line(socketfd, msg);    break; }  } } //登陆函数void login(int socketfd){struct Msg msg;memset(&msg, 0, sizeof(msg));msg.cmd = 2;display_line();printf("\t\t请输入姓名:");scanf("%s",msg.name);while(getchar() != '\n');printf("\n");printf("\t\t请输入密码:");scanf("%s",msg.password);while(getchar() != '\n');write(socketfd, &msg, sizeof(msg));read(socketfd, &msg, sizeof(msg));printf("%d\n",msg.cmd);if(msg.cmd == 0){display_line();printf("\t\t正在登陆...\n");sleep(2);display_line();printf("\t\t登陆成功\n");//启动一个线程专门用来接受聊天信息pthread_t id;pthread_create(&id, NULL, readMsg,  (void *)socketfd);pthread_detach(id);                 offline = 0;if(msg.flag == 1)                   //超级用户superusr(socketfd, &msg);if(msg.flag == 0)                   //普通用户commonusr(socketfd, &msg);}if(msg.cmd == 1){display_line();printf("\t\t正在登陆...\n");sleep(2);display_line();printf("\t\t密码错误\n");}if(msg.cmd == 2){display_line();printf("\t\t正在登陆...\n");sleep(2);display_line();printf("\t\t用户不存在,请先注册\n");}if(msg.cmd == 3){display_line();printf("\t\t正在登陆...\n");sleep(2);display_line();printf("\t\t该用户已经登陆,请勿重复登陆\n");}sleep(1);return;}// 客户端向服务器发送数据void ask_server(int socketfd){int x;while (1){maininterface();scanf("%d",&x);while(getchar() != '\n');switch(x){case 1:     // 注册reg(socketfd);break;case 2:     // 登陆login(socketfd);break;case 3:     // 退出system("clear");return;default: display_line();printf("\t\t错误指令,请重新输入\n");sleep(1);break;}}}int main(){// 创建与服务器通信的套接字int socketfd = socket(AF_INET, SOCK_STREAM, 0);if (socketfd == -1){perror ("socket");return -1;}// 连接服务器struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family  = AF_INET;     addr.sin_port    = htons(PORT); inet_aton("127.0.0.1",&(addr.sin_addr));// 成功的情况下,可以通过socketfd与服务器进行通信int ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));if (ret == -1){perror ("connect");return -1;}printf ("成功连上服务器\n");ask_server(socketfd);close(socketfd);return 0;}


六、项目演示效果









原创粉丝点击