网络编程项目:linux下基于C/S架构的聊天室
2017-09-18 09:50
429 查看
一、项目要求:
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同样编译运行)
五、项目代码
服务器:
客户端:
六、项目演示效果
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 -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 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; } swi 111bc tch (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、创建socket int 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 20 int 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; }
六、项目演示效果
相关文章推荐
- 基于Linux C的聊天室客户端(四)网络编程
- Linux下 基于TCP协议下的C/S架构的网络聊天室 C程序
- linux网络编程之socket(十四):基于UDP协议的网络程序
- Java网络编程 - 基于UDP协议 实现简单的聊天室程序
- Linux网络编程之[基于socket通信的tcp协议的编程模型]
- 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器
- Linux Socket 网络编程 基于GTK+ 的多线程实现的局域网通信软件
- Linux 下基于socket的简单网络聊天室(服务器与客户端)
- Linux&C网络编程————“聊天室”
- linux网络编程之用epoll实现io复用(基于tcp)
- Linux网络编程:基于TCP的程序开发回顾篇
- 基于Socket的网络聊天室编程(第一版)
- Linux网络编程--使用epoll,共享内存技术实现高性能的聊天室程序
- 基于Linux下C/C++的网络编程之进程
- linux网络编程之socket(十四):基于UDP协议的网络程序
- 基于Linux的消息队列及多线程编程实现的聊天室(一)
- Linux进程间通信-基于socket,过渡到网络编程
- 【Linux网络编程】基于TCP多进程(fork)版本客户端/服务器
- 网络编程项目(聊天室项目)
- linux网络编程——UDP局域网聊天室