一、實現目標
一個在Linux下可以使用的聊天軟件,要求至少實現如下功能:
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 端維護一個所有登陸用戶的聊天會的記錄文件,以便查看
可以選擇實現的附加功能:
1. Server 可以內建一個特殊權限的賬號admin,用于管理聊天室
2. Admin 可以將某個Client X“踢出聊天室”
3. Admin 可以將某個Client X“設為只能旁聽,不能發言”
4. Client 端發言增加表情符號,可以設置某些自定義的特殊組和來表達感情,如輸入:),則會自動發送“XXX向大家做了個笑臉”
5. Client 段增加某些常用話語,可以對其中某些部分進行“姓名替換”,例如,輸入/ClientA/welcome,則會自動發送“ClientA大俠,歡迎你來到咱們的聊天室”
Client.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 <stdlib.h>
#include <sqlite3.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>#define PORT 9999char myName[20]; // 保存用戶名
char msg1[1024]; // 保存聊天信息sqlite3 * database;int flag1 = 0; // 線程退出的判斷條件(不退出)
int flag2 = 0; // 文件傳輸確認信號(無接收)
int flag3 = 0; // 存在文件接收請求的判斷(不存在)
int flag4 = 0; // 本地存儲是否被禁言(未被禁言)// 協議
struct Msg
{char msg[1024]; // 消息內容int cmd; // 消息類型char filename[50]; // 保存文件名char toname[20]; // 接收者姓名char fromname[20]; // 發送者姓名int sig; // 用戶狀態(0:管理員、1:普通用戶、2:被禁言)
};struct Msg msg; // 全局變量兩個線程共享// 注冊/登錄界面
void interface1()
{system("clear");printf ("\t*************************** 網絡聊天室 *****************************\n");printf ("\t* *\n");printf ("\t* *\n");printf ("\t* 1、 注冊 *\n"); printf ("\t* 2、 登錄 *\n");printf ("\t* q、 退出 *\n");printf ("\t* *\n");printf ("\t* *\n");printf ("\t* BY szw *\n");printf ("\t********************************************************************\n\n");printf ("\t***** 請輸入命令: ");}// 普通用戶界面
void interface2()
{system("clear");printf ("\t*************************** 網絡聊天室 *****************************\n");printf ("\t* *\n");printf ("\t* *\n");printf ("\t* 1、 查看當前在線人數 *\n"); printf ("\t* 2、 進入群聊界面 *\n");printf ("\t* 3、 進入私聊界面 *\n");printf ("\t* 4、 查看聊天記錄 *\n");printf ("\t* 5、 文件傳輸 *\n");printf ("\t* 6、 更改密碼 *\n"); printf ("\t* 7、 在線注銷 *\n"); printf ("\t* Q、 退出聊天室 返回登錄界面 *\n");printf ("\t* *\n");printf ("\t* *\n");printf ("\t* BY szw *\n");printf ("\t********************************************************************\n\n");printf ("\t***** 請輸入命令: ");
}// 管理員界面
void interface3()
{system("clear");printf ("\t*************************** 網絡聊天室 *****************************\n");printf ("\t* *\n");printf ("\t* *\n");printf ("\t* 1、 查看當前在線人數 *\n"); printf ("\t* 2、 進入群聊界面 *\n");printf ("\t* 3、 進入私聊界面 *\n");printf ("\t* 4、 查看聊天記錄 *\n");printf ("\t* 5、 文件傳輸 *\n");printf ("\t* 6、 更改密碼 *\n"); printf ("\t* 7、 在線注銷 *\n"); printf ("\t* 8、 管理員界面 *\n"); printf ("\t* Q、 退出聊天室 返回登錄界面 *\n");printf ("\t* *\n");printf ("\t* *\n");printf ("\t* BY szw *\n");printf ("\t********************************************************************\n\n");printf ("\t***** 請輸入命令: ");
}// 用來保存收到的聊天信息
void keep_msg(char * msg1)
{// 打開數據庫int ret = sqlite3_open("Histroy.db", &database);if (ret != SQLITE_OK){printf ("\t打開數據庫失敗\n");return;}// 往histroy表中添加信息char buf[100];char *errmsg = NULL;sprintf (buf, "insert into histroy values('%s','%s','%s')",msg.fromname,msg.toname,msg1);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("\t數據庫操作失敗:%s\n", errmsg);return;}
}// 接收文件
void receive()
{printf ("\n\t正在接收文件.....\n");int fd2 = open(msg.filename, O_WRONLY|O_CREAT, 0777);if (fd2 == -1){perror ("open fd2");return;}write (fd2, msg.msg, 1023);close (fd2);
}// 用來監聽收到的信息
void *readMsg(void *v)
{int socketfd = (int)v;while(1){if (flag1 == 1) // 判斷線程的退出條件{flag1 = 0; // 重置線程退出條件pthread_exit(NULL);} read (socketfd, &msg, sizeof(msg));switch(msg.cmd){case 9001: // 群聊sprintf (msg1, "收到一條來自%s的群消息:\n\t%s", msg.fromname,msg.msg);printf ("\n\n\t%s\n", msg1);printf ("\n\t回車鍵返回 \n");keep_msg (msg1);break;case 9002: // 私聊printf ("\n\t%s 發來一條消息:\n\t%s\n", msg.fromname, msg.msg);sprintf (msg1,"%s 向 %s 發送一條信息:\n\t%s",msg.fromname, msg.toname, msg.msg);printf ("\n\t回車鍵返回 \n");keep_msg (msg1);break;case 9003: // 處理發送失敗sleep(3);printf ("\n\t用戶不在線或不存在,發送失敗\n");printf ("\n\t回車鍵返回 \n");break;case 9004: // 是否存在文件接收確認信號printf ("\n\n\t收到一條信息,輸入任一字符進行回復:");fflush(stdout);flag3 = 1;break; case 9005: // 文件傳輸請求被拒絕printf ("\n\t您發送的文件傳輸請求已被拒絕\n");printf ("\n\t回車鍵返回 \n");break;case 9006: // 文件傳輸請求已通過printf ("\n\t您發送的文件傳輸請求已通過,請打開文件傳輸界面進行文件傳輸\n");printf ("\n\t回車鍵返回 \n");break;case 9007: // 接收文件if (flag2 != 1)printf ("\n\t已成功攔截 %s 給您發送的文件\n", msg.fromname);elsereceive();break;case 9008: // 文件傳輸完成 printf ("\n\t%s 給您發送的文件已全部接收完畢,請及時查看\n", msg.fromname);printf ("\n\t回車鍵返回 \n");flag2 = 0; // 重置文件傳輸確認信號break;case 9009: // 密碼修改成功printf ("\n\t密碼修改成功!\n");sleep(1);break;case 9010: // 密碼輸入有誤printf ("\n\t密碼輸入有誤,修改失敗\n");usleep(1500000);break; case 9011: // 收到禁言信號printf ("\n\t您已被管理員禁言,將無法發送群聊信息\n");printf ("\n\t回車鍵返回 \n");flag4 = 1;break;case 9012: // 收到結除禁言信號printf ("\n\t您已被管理員解除禁言\n");printf ("\n\t回車鍵返回 \n");flag4 = 0;break;case 9013: // 收到被踢出信號printf ("\n\t您已被管理員踢出,即將退出聊天室....\n");//sleep (2);flag1 = 1; break;}usleep(400000);}
}// 查看當前在線人數
void display (int socketfd)
{msg.cmd = 1;write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求usleep(100000);printf ("\n\t當前在線人數為:%d\n", msg.cmd);printf ("\n\t回車鍵返回 \n");getchar();
}// 退出聊天室,返回登錄界面
void quit_chatroom (int socketfd)
{msg.cmd = 10;strcpy (msg.fromname, myName);write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送退出信號
}// 進入群聊界面
void chat1(int socketfd)
{if (flag4 == 1){printf ("\n\t您已被管理員禁言,無法發送群聊信息...\n");return;}msg.cmd = 2;strcpy (msg.fromname, myName);strcpy(msg.toname, "all");printf ("\n\t請輸入您要發送的內容:\n\t");scanf ("%s",msg.msg);getchar();write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求printf ("\n\t發送完成,等待處理結果.....\n");
// usleep (500000);
}// 進入私聊界面
void chat2(int socketfd)
{ msg.cmd = 3;strcpy (msg.fromname, myName);printf ("\n\t請輸入您要發送的對象:\n\t");scanf ("%s",msg.toname);getchar();printf ("\t請輸入您要發送的內容:\n\t");scanf ("%s",msg.msg);getchar();write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求printf ("\n\t發送完成,請稍候.....\n");usleep (500000);
}// 打印群聊歷史記錄
void chat1_hst()
{system("clear");printf ("\t*************************** 網絡聊天室 *****************************\n\n\n");printf ("\t群聊歷史記錄: \n");// 打開數據庫int ret = sqlite3_open("Histroy.db", &database);if (ret != SQLITE_OK){printf ("\t打開數據庫失敗\n");return;}// 獲取histroy表中的信息char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from histroy";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}int i;for (i = 1+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], "all") == 0){printf ("\n\t%s\n", resultp[i+1]);}}sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);
}// 打印私聊歷史記錄
void chat2_hst()
{system("clear");printf ("\t*************************** 網絡聊天室 *****************************\n\n\n");printf ("\t私聊歷史記錄:\n");// 打開數據庫int ret = sqlite3_open("Histroy.db", &database);if (ret != SQLITE_OK){printf ("\t打開數據庫失敗\n");return;}// 獲取histroy表中的信息char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from histroy";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}int i;for (i = 1+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], "all") != 0){printf ("\n\t%s\n", resultp[i+1]);}}sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);
}// 查看聊天記錄
void dis_histroy(int socketfd)
{printf ("\n\t a、查看群聊記錄\n\t b、查看個人聊天記錄\n\t");printf ("\n\t***** 請選擇: ");switch (getchar()){case 'a':chat1_hst();break;case 'b':chat2_hst();break;}printf ("\n\t回車鍵返回 ");getchar();
}// 確認傳輸對象
void convey_confirm(int socketfd)
{msg.cmd = 5;strcpy (msg.fromname, myName);printf ("\n\t請輸入文件的傳輸對象:\n\t");scanf ("%s",msg.toname);getchar();printf ("\n\t傳輸請求發送完成,請稍等.....\n");write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求
}// 文件傳輸過程
void convey_chose(int socketfd)
{msg.cmd = 9007;strcpy (msg.toname, msg.fromname);strcpy (msg.fromname, myName);printf ("\n\t當前目錄下的文件有: \n\n\t");fflush(stdout);system ("ls");printf ("\n\t請輸入您要傳送的文件名: ");printf ("\n\t");scanf ("%s",msg.filename);getchar();// 打開要讀的文件int fd1 = open(msg.filename, O_RDONLY);if (fd1 == -1){perror ("open fd1");return;}int ret = 0;flag1 = 1; // 關閉線程while (ret = read (fd1, msg.msg, 1023)){if (ret == -1){perror("read");break;}printf ("\n\t正在傳輸文件,請稍候......\n");write (socketfd, &msg, sizeof(struct Msg));sleep(1);}msg.cmd = 9008;write (socketfd, &msg, sizeof(struct Msg));printf ("\n\t文件傳輸完成\n");close (fd1);// 重新開啟一個線程pthread_t id;pthread_create(&id, NULL, readMsg, (void*)socketfd);pthread_detach(id); // getchar();
}// 文件傳輸界面
void convey(int socketfd)
{system("clear");printf ("\t*************************** 網絡聊天室 *****************************\n\n\n");printf ("\t 1、傳輸對象確認\n");printf ("\t 2、選擇文件\n");printf ("\n\t***** 請選擇: ");char ch[2];fgets(ch, 2, stdin);while (getchar()!= '\n');switch (ch[0]){case '1': // 確認傳輸對象convey_confirm(socketfd);break;case '2': // 文件選擇convey_chose(socketfd);break;}printf ("\n\t回車鍵返回 ");getchar();
}// 更改密碼
void change_pass(int socketfd)
{char ch[2];msg.cmd = 6;printf ("\n\t您是否確定需要修改密碼?(y/n): ");fgets(ch, 2, stdin);while (getchar()!= '\n');if (ch[0] != 'y'){printf ("\n\t請稍候.....\n");usleep(700000);return;}printf ("\t請輸入舊密碼: ");scanf ("%s", msg.msg);getchar();printf ("\t請輸入新密碼: ");scanf ("%s", msg.filename);getchar();strcpy (msg.fromname, myName);write (socketfd, &msg, sizeof(struct Msg)); // 向服務器發送注冊信息printf ("\n\t正在校驗數據,請稍候......\n");sleep(1);
}// 在線注銷
void delete_user(int socketfd)
{msg.cmd = 8;printf ("\n\t正在處理注銷操作......\n");write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求sleep(1);printf ("\t注銷完成!\n");
}// 普通用戶操作
void user_do (int socketfd)
{ char ch[2];while(1){interface2();if (flag3 == 1){printf ("\n\n\t%s 請求傳輸文件,是否接收?(y/n):", msg.fromname);fflush(stdout);fgets(ch, 2, stdin);while (getchar()!= '\n');if (ch[0] != 'y'){printf ("\n\t您已拒絕接受文件傳輸\n");printf ("\n\t回車鍵返回 \n");}else {printf ("\n\t您已接受文件傳輸請求\n");printf ("\n\t回車鍵返回 \n");flag2 = 1;}if (flag2 == 0){msg.cmd = 9005; // 不接受文件strcpy (msg.toname,msg.fromname); // 修改信號發送對象strcpy (msg.fromname, myName);write (socketfd, &msg, sizeof(struct Msg)); }else if (flag2 == 1){msg.cmd = 9006; // 接受文件strcpy (msg.toname,msg.fromname); // 修改信號發送對象strcpy (msg.fromname, myName);write (socketfd, &msg, sizeof(struct Msg)); }flag3 = 0; // 重置是否存在文件接收請求的判斷flag2 = 0;}fgets(ch, 2, stdin);while (getchar()!= '\n'); switch(ch[0]){case '1': // 查看當前在線人數display(socketfd);break;case '2': // 進入群聊界面 chat1(socketfd);printf ("\n\t回車鍵返回 \n");getchar();break;case '3': // 進入私聊界面 chat2(socketfd);printf ("\n\t回車鍵返回 \n");getchar();break;case '4': // 查看聊天記錄dis_histroy(socketfd);getchar();break;case '5': // 文件傳輸convey(socketfd);break;case '6': // 更改密碼change_pass(socketfd);break;case '7': // 在線注銷printf ("\n\t是否確認注銷?(y/n): ");fgets(ch, 2, stdin);while (getchar()!= '\n');if (ch[0] != 'y'){printf ("\n\t請稍候.....\n");usleep(700000);break;}delete_user(socketfd);case 'q': // 退出聊天室 返回登錄界面flag1 = 1;quit_chatroom(socketfd);printf ("\n\t正在退出,請稍候......\n");break;}if (flag1 == 1) // 判斷退出條件{break;}system("clear");}
}// 將成員禁言
void silent (int socketfd)
{msg.cmd = 9011;strcpy (msg.fromname, myName);printf ("\n\t請輸入您要禁言的用戶:\n\t");scanf ("%s",msg.toname);getchar();write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求printf ("\n\t操作完成,請稍候.....\n");usleep (500000);
}// 將成員解除禁言
void silent_del (int socketfd)
{msg.cmd = 9012;strcpy (msg.fromname, myName);printf ("\n\t請輸入您要解除禁言的用戶:\n\t");scanf ("%s",msg.toname);getchar();write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求printf ("\n\t操作完成,請稍候.....\n");usleep (500000);
}// 將成員踢出聊天室
void kickout (int socketfd)
{msg.cmd = 9013;strcpy (msg.fromname, myName);printf ("\n\t請輸入您要踢出的用戶:\n\t");scanf ("%s",msg.toname);getchar();write (socketfd, &msg, sizeof(struct Msg)); //向服務器發送請求printf ("\n\t操作完成,請稍候.....\n");usleep (500000);
}// 管理員權限
void supuser (int socketfd)
{system("clear");printf ("\t*************************** 網絡聊天室 *****************************\n\n\n");printf ("\t 1、將成員禁言\n");printf ("\t 2、將成員解除禁言\n");printf ("\t 3、將成員踢出聊天室\n");printf ("\n\t***** 請選擇: ");char ch[2];fgets(ch, 2, stdin);while (getchar()!= '\n');switch (ch[0]){case '1': // 將成員禁言silent(socketfd);break;case '2': // 將成員解除禁言silent_del(socketfd);break; case '3': // 將成員踢出聊天室kickout(socketfd);break;}printf ("\n\t回車鍵返回 ");getchar();
}// 管理員操作
void supuser_do (int socketfd)
{ char ch[2];while(1){interface3();if (flag3 == 1){printf ("\n\n\t%s 請求傳輸文件,是否接收?(y/n):", msg.fromname);fflush(stdout);fgets(ch, 2, stdin);while (getchar()!= '\n');if (ch[0] != 'y'){printf ("\n\t您已拒絕接受文件傳輸\n");printf ("\n\t回車鍵返回 \n");}else {printf ("\n\t您已接受文件傳輸請求\n");printf ("\n\t回車鍵返回 \n");flag2 = 1;}if (flag2 == 0){msg.cmd = 9005; // 不接受文件strcpy (msg.toname,msg.fromname); // 修改信號發送對象strcpy (msg.fromname, myName);write (socketfd, &msg, sizeof(struct Msg)); }else if (flag2 == 1){msg.cmd = 9006; // 接受文件strcpy (msg.toname,msg.fromname); // 修改信號發送對象strcpy (msg.fromname, myName);write (socketfd, &msg, sizeof(struct Msg)); }flag3 = 0; // 重置是否存在文件接收請求的判斷flag2 = 0;}fgets(ch, 2, stdin);while (getchar()!= '\n'); switch(ch[0]){case '1': // 查看當前在線人數display(socketfd);break;case '2': // 進入群聊界面 chat1(socketfd);printf ("\n\t回車鍵返回 \n");getchar();break;case '3': // 進入私聊界面 chat2(socketfd);printf ("\n\t回車鍵返回 \n");getchar();break;case '4': // 查看聊天記錄dis_histroy(socketfd);getchar();break;case '5': // 文件傳輸convey(socketfd);break;case '6': // 更改密碼change_pass(socketfd);break;case '8': // 管理員權限操作supuser (socketfd);break;case '7': // 在線注銷printf ("\n\t是否確認注銷?(y/n): ");fgets(ch, 2, stdin);while (getchar()!= '\n');if (ch[0] != 'y'){printf ("\n\t請稍候.....\n");usleep(700000);break;}delete_user(socketfd);case 'q': // 退出聊天室 返回登錄界面flag1 = 1;quit_chatroom(socketfd);printf ("\n\t正在退出,請稍候......\n");break;}if (flag1 == 1) // 判斷退出條件{break;}system("clear");}
}// 登錄
void log_in(int socketfd)
{char password[20];msg.cmd = 2;printf ("\n\t用戶登錄:\n");printf ("\t請輸入用戶名: ");scanf ("%s", myName);getchar();printf ("\t請輸入密碼: ");scanf ("%s", password);getchar();strcpy (msg.fromname, myName);strcpy (msg.msg, password);write (socketfd, &msg, sizeof(struct Msg)); // 向服務器發送登錄請求read (socketfd, &msg, sizeof(struct Msg)); // 讀取服務器的登錄回應printf ("\n\t正在校驗數據......\n");sleep(1);if (msg.cmd == 1002){printf ("\n\t驗證通過,正在登錄......\n");usleep(1500000);flag4 = msg.sig; // 更新禁言狀態// 線程分離,用來監聽服務器返回信息pthread_t id;pthread_create(&id, NULL, readMsg, (void*)socketfd);pthread_detach(id); user_do (socketfd);}else if (msg.cmd == 1003){printf ("\n\t驗證通過,正在登錄......\n");usleep(1500000);// 線程分離,用來監聽服務器返回信息pthread_t id;pthread_create(&id, NULL, readMsg, (void*)socketfd);pthread_detach(id); supuser_do (socketfd);} else if (msg.cmd == -4){printf ("\n\t此賬號已在別處登錄\n");}else if (msg.cmd == -3){printf ("\n\t驗證失敗,請您確認信息后重新登錄\n");}else if (msg.cmd == -2){printf ("\t驗證失敗,數據庫打開失敗\n");}else if (msg.cmd == -1){printf ("\t數據庫操作失敗\n");}usleep(1500000);
}// 注冊(可注冊管理員)
void reg(int socketfd)
{msg.cmd = 1;printf ("\t用戶注冊:\n");printf ("\t請輸入用戶名: ");scanf ("%s", myName);getchar();printf ("\t請輸入密碼: ");scanf ("%s", msg.msg);getchar();printf ("\t管理員: ");scanf ("%d", &msg.sig);getchar(); strcpy (msg.fromname, myName);write (socketfd, &msg, sizeof(struct Msg)); // 向服務器發送注冊信息read (socketfd, &msg, sizeof(struct Msg)); // 讀取服務器的注冊回應printf ("\n\t正在校驗數據......\n");sleep(1);if (msg.cmd == 1001){printf ("\n\t注冊成功!\n\t請稍候......\n");}else if (msg.cmd == -1){printf ("\t注冊失敗,該用戶名已被注冊\n");}else if (msg.cmd == -2){printf ("\t注冊失敗,數據庫打開失敗\n");}usleep(1500000);
}// 向服務器發送請求
void ask_server(int socketfd)
{char ch[2];while (1){interface1();fgets(ch, 2, stdin);while (getchar()!= '\n');switch(ch[0]){case '1': // 注冊reg(socketfd);break;case '2': // 登錄log_in(socketfd);break;case 'q': // 退出exit(1);}system("clear");}
}int main(int argc, char **argv)
{// 打開數據庫Histroy.dbint ret = sqlite3_open("Histroy.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return -1;}// 創建 histroy 表char *errmsg = NULL;char *sql = "create table if not exists histroy(fromname TEXT,toname TEXT,msg TEXT)";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return -1;}// 關閉數據庫sqlite3_close(database);// 創建與服務器通信的套接字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(argv[1],&(addr.sin_addr));// 連接服務器,如果成功,返回0,如果失敗,返回-1// 成功的情況下,可以通過socketfd與服務器進行通信ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));if (ret == -1){perror ("connect");return -1;}printf ("成功連上服務器\n");ask_server(socketfd);// 關閉套接字close(socketfd);return 0;
}
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 <stdlib.h>
#include <sqlite3.h> #define PORT 9999sqlite3 * database;// 協議
struct Msg
{char msg[1024]; // 消息內容int cmd; // 消息類型char filename[50]; // 保存文件名char toname[20]; // 接收者姓名char fromname[20]; // 發送者姓名int sig; // 用戶狀態(0:管理員、1:普通用戶、2:被禁言)
};// 初始化套接字,返回監聽套接字
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)
{// 4、接收連接struct sockaddr_in client_addr; // 用來保存客戶端的ip和端口信息int len = sizeof(client_addr);int client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &len);if (client_socket == -1){perror ("accept");}printf ("成功接收一個客戶端: %s\n", inet_ntoa(client_addr.sin_addr));return client_socket;
}// 查看當前在線人數
void display (int client_socket, struct Msg *msg)
{printf ("查看當前在線人數\n");// 確認flag參數// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 與User表中信息進行比對char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}int count = 0;int i;for (i = 3+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], "1") == 0){count++;}}// 返回在線人數msg->cmd = count;printf ("當前在線人數為:%d\n", msg->cmd);write (client_socket, msg, sizeof(struct Msg));sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);printf ("操作完成,已關閉數據庫\n");
}// 退出聊天室,返回登錄界面
void quit_chatroom (int client_socket, struct Msg *msg)
{printf ("%s 退出聊天室\n", msg->fromname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return;}char buf[100];char *errmsg = NULL;errmsg = NULL;sprintf (buf, "update user set flag = 0 where name = '%s'",msg->fromname);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}sqlite3_close(database);printf ("登錄狀態修改完畢,已關閉數據庫\n");write (client_socket, msg, sizeof(struct Msg));
}// 客戶端發送群聊消息
void chat1 (int client_socket, struct Msg *msg)
{printf ("%s 發了一條群消息\n",msg->fromname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取數據庫中的flag參數信息char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}int i;for (i = 3+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){// 查詢所有在線的用戶if(strcmp(resultp[i], "1") == 0){msg->cmd = 9001; write (atoi(resultp[i-1]), msg, sizeof(struct Msg)); }}printf ("群消息已全部發送完成\n");
}// 客戶端發送私聊消息
void chat2 (int client_socket, struct Msg *msg)
{printf ("%s 向 %s 發了一條消息\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取數據庫中的flag參數信息,判斷是否在線char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){msg->cmd = 9002;write (atoi(resultp[i+2]), msg, sizeof(struct Msg));return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));
}// 處理確認文件傳輸對象
void convey_confirm (int client_socket, struct Msg *msg)
{printf ("%s 向 %s 發送文件傳輸請求\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return;}// 獲取數據庫中的flag參數信息char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){msg->cmd = 9004;write (atoi(resultp[i+2]), msg, sizeof(struct Msg));return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));
}// 用戶不接受文件
void refuse (int client_socket, struct Msg *msg)
{printf ("%s 拒絕了 %s 的文件傳輸請求\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return;}// 獲取數據庫中 toname 的套接字char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){write (atoi(resultp[i+2]), msg, sizeof(struct Msg));return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));
}// 用戶接受文件
void accept_ (int client_socket, struct Msg *msg)
{printf ("%s 通過了 %s 的文件傳輸請求\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return;}// 獲取數據庫中 toname 的套接字char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){write (atoi(resultp[i+2]), msg, sizeof(struct Msg));return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));
}// 處理文件傳輸
void convey_chose (int client_socket, struct Msg *msg)
{printf ("%s正在向%s傳輸文件......\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取數據庫中的 flag 參數信息,判斷是否在線char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取toname的套接字int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){write (atoi(resultp[i+2]), msg, sizeof(struct Msg));return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));
}// 文件傳輸完成
void convey_complete (int client_socket, struct Msg *msg)
{printf ("文件傳輸結束\n");// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取數據庫中的 flag 參數信息,判斷是否在線char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取toname的套接字int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){write (atoi(resultp[i+2]), msg, sizeof(struct Msg));return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));
}// 更改密碼
void change_pass (int client_socket, struct Msg *msg)
{printf ("%s請求修改密碼\n", msg->fromname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 與User表中信息進行比對char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from user";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->fromname)==0 && strcmp(resultp[i+1], msg->msg)==0){// 返回確認信息msg->cmd = 9009;printf ("%s 驗證通過\n", msg->fromname);write (client_socket, msg, sizeof(struct Msg));// 修改密碼char buf[100];errmsg = NULL;sprintf (buf, "update user set password = '%s' where name = '%s'",msg->filename,msg->fromname);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);printf ("密碼修改完成,已關閉數據庫\n");return;}} printf ("%s 驗證不通過,密碼輸入有誤\n", msg->fromname);msg->cmd = 9010;write (client_socket, msg, sizeof(struct Msg));sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);printf ("操作完成,已關閉數據庫\n"); }// 客戶端請求在線注銷
void delete_user (int client_socket, struct Msg *msg)
{printf ("即將處理用戶注銷\n");// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return;}// 刪除 user 表中的信息char buf[100];char *errmsg = NULL;sprintf (buf, "delete from user where name = '%s'", msg->fromname);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}// 關閉數據庫sqlite3_close(database);printf ("刪除成功,已關閉數據庫\n");
}// 處理禁言請求
void silent (int client_socket, struct Msg *msg)
{printf ("正在處理管理員 %s 對成員 %s 的禁言請求\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取數據庫中的 flag 參數信息,判斷是否在線char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取toname的套接字int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){msg->cmd = 9011;write (atoi(resultp[i+2]), msg, sizeof(struct Msg));char buf[100];errmsg = NULL;sprintf (buf, "update user set sig = 2 where name = '%s'",msg->toname);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}sqlite3_close(database);printf ("禁言狀態修改完畢,已關閉數據庫\n");return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));sqlite3_close(database);printf ("用戶不在線,修改失敗,已關閉數據庫\n");
}// 處理解除禁言請求
void silent_del (int client_socket, struct Msg *msg)
{printf ("正在處理管理員 %s 對成員 %s 的解除禁言請求\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取數據庫中的 flag 參數信息,判斷是否在線char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取toname的套接字int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){msg->cmd = 9012;write (atoi(resultp[i+2]), msg, sizeof(struct Msg));char buf[100];errmsg = NULL;sprintf (buf, "update user set sig = 1 where name = '%s'",msg->toname);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}sqlite3_close(database);printf ("禁言狀態修改完畢,已關閉數據庫\n");return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));sqlite3_close(database);printf ("用戶不在線,修改失敗,已關閉數據庫\n"); }// 處理踢出成員
void kickout (int client_socket, struct Msg *msg)
{printf ("正在處理管理員 %s 對成員 %s 的踢出請求\n",msg->fromname,msg->toname);// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 獲取數據庫中的 flag 參數信息,判斷是否在線char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from User";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}// 獲取toname的套接字int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->toname)==0 && strcmp(resultp[i+3], "1")== 0){msg->cmd = 9013;write (atoi(resultp[i+2]), msg, sizeof(struct Msg));char buf[100];errmsg = NULL;sprintf (buf, "update user set flag = 0 where name = '%s'",msg->toname);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return;}sqlite3_close(database);printf ("踢出完畢,已關閉數據庫\n");return;}}msg->cmd = 9003;write (client_socket, msg, sizeof(struct Msg));sqlite3_close(database);printf ("用戶不在線,修改失敗,已關閉數據庫\n");
}// 處理用戶操作請求函數
void user_do (int client_socket)
{struct Msg msg;int sig = 0;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 10: // 退出聊天室,返回登錄界面quit_chatroom(client_socket, &msg);sig = 1;break;case 1 : // 查看當前在線人數display (client_socket, &msg);break;case 2 : // 處理群聊消息 chat1 (client_socket, &msg);break;case 3 : // 處理私聊消息 chat2 (client_socket, &msg);break; case 5 : // 處理確認文件傳輸對象convey_confirm (client_socket, &msg);break;case 6 : // 更改密碼change_pass (client_socket, &msg);break;case 8 : // 處理在線注銷delete_user (client_socket, &msg);break;case 9005 : // 用戶不接受文件refuse (client_socket, &msg);break;case 9006 : // 用戶接受文件accept_ (client_socket, &msg);break;case 9007 : // 處理文件傳輸convey_chose (client_socket, &msg);break; case 9008 : // 文件傳輸完成convey_complete (client_socket, &msg);break;case 9011: // 處理禁言請求silent (client_socket, &msg);break;case 9012: // 處理解除禁言請求silent_del (client_socket, &msg);break;case 9013: // 處理踢出成員kickout (client_socket, &msg);break;}if (sig == 1){printf("即將退出普通用戶操作請求函數\n");break;}}
}// 處理客戶端的登錄請求
void log_in(int client_socket, struct Msg *msg)
{printf ("%s 請求登錄\n", msg->fromname);// 將用戶信息進行比對// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 與User表中信息進行比對char *errmsg = NULL;char **resultp = NULL;int nrow, ncolumn;char *sql = "select * from user";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}int i;for (i = 0+ncolumn; i < (nrow+1)*ncolumn; i+=ncolumn){if(strcmp(resultp[i], msg->fromname)==0 && strcmp(resultp[i+1], msg->msg)==0){if (strcmp(resultp[i+3], "1") == 0){msg->cmd = -4;printf ("%s 已經在別處登錄\n", msg->fromname);write (client_socket, msg, sizeof(struct Msg));sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);printf ("操作完成,已關閉數據庫\n"); return;}if (strcmp(resultp[i+4], "0") != 0){// 普通用戶msg->cmd = 1002;msg->sig = atoi(resultp[i+4]);printf ("普通用戶 %s 驗證通過\n", msg->fromname);write (client_socket, msg, sizeof(struct Msg));}else {// 管理員msg->cmd = 1003;msg->sig = atoi(resultp[i+4]);printf ("管理員 %s 驗證通過\n", msg->fromname);write (client_socket, msg, sizeof(struct Msg));} // 修改在線狀態、更新套接字char buf[100];errmsg = NULL;sprintf (buf, "update user set socket = '%d',flag = 1 where name = '%s'",client_socket,msg->fromname);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);printf ("在線狀態已更新,已關閉數據庫\n");printf ("進入用戶操作請求處理功能\n");user_do (client_socket);return;}} printf ("%s 驗證不通過\n", msg->fromname);msg->cmd = -3;write (client_socket, msg, sizeof(struct Msg));sqlite3_free_table(resultp);// 關閉數據庫sqlite3_close(database);printf ("操作完成,已關閉數據庫\n");
}// 處理客戶端的注冊請求
void reg(int client_socket, struct Msg *msg)
{printf ("%s 進行注冊\n", msg->fromname);// 將用戶進行保存// 打開數據庫int ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");msg->cmd = -2;write (client_socket, msg, sizeof(struct Msg));return;}// 往User表中添加信息char buf[100];char *errmsg = NULL;sprintf (buf, "insert into user values('%s','%s',%d,%d,%d)",msg->fromname,msg->msg,client_socket,0,msg->sig);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);msg->cmd = -1;write (client_socket, msg, sizeof(struct Msg));return;}// 返回確認信息msg->cmd = 1001;printf ("%s 注冊成功\n", msg->fromname);write (client_socket, msg, sizeof(struct Msg));// 關閉數據庫sqlite3_close(database);printf ("操作完成,已關閉數據庫\n");
}// 線程的工作函數,即處理客戶端請求的函數
void* hanld_client(void* v)
{int client_socket = (int)v;struct Msg msg;while(1){printf("處理客戶端請求的函數函數已就緒\n");// 從客戶端讀一個結構體數據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 : // 客戶端進行注冊reg(client_socket, &msg);break;case 2 : // 客戶端進行登錄log_in(client_socket, &msg);break;}}close (client_socket);
}int main()
{// 打開數據庫User.dbint ret = sqlite3_open("User.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return -1;}// 創建 user 表char *errmsg = NULL;char *sql = "create table if not exists user(name TEXT,password TEXT,socket INTEGER,flag INTEGER,sig INTEGER,primary key(name))";ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return -1;}printf ("數據庫準備就緒......\n");// 關閉數據庫sqlite3_close(database);// 初始化套接字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);return 0;
}