一、項目要求:
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;
}
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、創建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;
}
六、項目演示效果