日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

聊天室项目

發(fā)布時間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊天室项目 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、簡述:
采用了cs結構
服務器端四步走:socket,bind,listen,accept
客戶端兩步走:socket,connect
簡單模型:客戶端a消息發(fā)送到服務器;服務器找到客戶端b的socket,在把消息發(fā)送給客戶端b;
設計數據庫操作存儲的一些操作比較簡單這里不提
2、看代碼:
(1)、服務器端代碼

#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>#define PORT 9999struct User {char name[20]; // 用戶名int socket; // 和用戶通信的socket };// 協議 struct Msg {char information[1024]; //發(fā)送消息char msg[1024]; // (姓名)消息內容int id;char sex[20];int age;int password;char toname[20];char fromname[20];int cmd; // 消息類型char online_name[20]; //在線姓名; };struct User user[2000] = {0};//打開數據庫函數,客戶端用戶登錄數據傳遞過來,服務器端打開數據庫比較查詢是否匹配此登錄用戶信息; int Database_enter(int client_socket,struct Msg *msg) {sqlite3 *database;int ret = sqlite3_open( "information.db", &database );if(ret != SQLITE_OK){perror("sqlite3_open");return -1;}printf("數據庫打開成功\n");// 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....};// 4、nrow: 多少行數據// 5、ncolumn: 多少列數據char ** resultp;char *errmsg = NULL;int nrow;int ncolumn;//進行什么操作;char *sql = "select * from information";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg );if(ret != SQLITE_OK){printf("數據庫操作失敗 %s\n",errmsg);return -1;}//查詢之后,把客戶端傳遞過來的數據和這張表里的數據想比較,先比較唯一的name;再比較password;int i;for(i = 0 ;i < (nrow+1)*ncolumn;i++){if (strncmp (resultp[i],msg->msg,strlen(msg->msg)) == 0)//if (strncmp (resultp[i],msg->msg,strlen(msg->msg)-1) == 0){if(atoi(resultp[i+1]) == msg->password) //強制轉換;{printf("服務器端已經通過登錄驗證\n");msg->cmd = 1002;int k;for(k = 0 ;k < 2000; k++){if(user[k].socket == 0){strcpy(user[k].name, msg->msg);user[k].socket = client_socket;printf("名字:%s\n",user[k].name);break;}}break;}else{printf("服務器端發(fā)現你的密碼不正確哦!\n");msg->cmd = -1;break;}}}if(i == (nrow+1)*ncolumn){msg->cmd = -2;printf("服務器端沒找到該用戶\n");}sqlite3_free_table(resultp); //之前的char**resultp (數組)等于malloc了一個空間來儲存數據庫的數據;所以需要釋放;sqlite3_close(database);printf("%d,%d\n",msg->cmd,sizeof(msg)); }//私聊數據庫函數 int Database_chat2(int client_socket,struct Msg *msg) {sqlite3 *database;int ret = sqlite3_open( "information.db", &database );if(ret != SQLITE_OK){perror("sqlite3_open");return -1;}printf("數據庫打開成功\n");// 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....};// 4、nrow: 多少行數據// 5、ncolumn: 多少列數據char ** resultp = NULL;char *errmsg = NULL;int nrow;int ncolumn;//進行什么操作;char *sql = "select * from information";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg );if(ret != SQLITE_OK){printf("數據庫操作失敗 %s\n",errmsg);return -1;}//查詢之后,把客戶端傳遞過來的數據和這張表里的數據想比較,先比較唯一的name;再比較password;int i;for(i = 0 ;i < (nrow+1)*ncolumn;i++){if (strncmp (resultp[i],msg->toname,strlen(msg->toname)) == 0)// if (strncmp (resultp[i],msg->toname,strlen(msg->toname)-1) == 0){printf("222222\n");msg->cmd = 11;return 11;}}if(i == (nrow+1)*ncolumn){msg->cmd = -5;return -5;}sqlite3_free_table(resultp); //之前的char**resultp (數組)等于malloc了一個空間來儲存數據庫的數據;所以需要釋放;sqlite3_close(database);}//數據庫寫入注冊數據函數 int Database_insert( struct Msg *msg) {sqlite3 *database;//打開數據庫,數據庫指針指向你要打開的數據庫,studtet.db是你創(chuàng)建的數據庫文件int ret = sqlite3_open( "information.db", &database );if(ret != SQLITE_OK){perror("sqlite3_open");return -1;}printf("數據庫打開成功\n");//下邊一大段是創(chuàng)建一個表char *errmsg = NULL;//sql語句char *sql = "create table if not exists information (name TEXT, PASSWORD INTEGER, primary key(name))"; //執(zhí)行sql語句的函數;ret = sqlite3_exec(database ,sql, NULL ,NULL ,&errmsg);if(ret != SQLITE_OK){printf("數據庫創(chuàng)建操作失敗 %s\n",errmsg);return -1;}//往表里邊插入數據;//insert into student values(1, 'Zhang', 'M', 18);char buf[100];sprintf(buf," insert into information values('%s',%d)",msg->msg ,msg->password );//設置主鍵之后,如果插入的id已經存在那么就會返回錯誤,一班來說返回的錯誤都是插入重復這里有點取巧;ret = sqlite3_exec(database ,buf, NULL ,NULL ,&errmsg);if (ret != SQLITE_OK){printf ("數據庫插入操作失敗:%s\n", errmsg);return -2;}printf("數據庫已經成功插入了你的注冊信息\n");sqlite3_close(database);return 1; }// 初始化套接字,返回監(jiān)聽套接字 int init_socket() {//1、創(chuàng)建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、監(jiān)聽本地套接字ret = listen(listen_socket, 5);if (ret == -1){perror ("listen");return -1;}printf ("等待客戶端連接.......\n");return listen_socket; }// 處理客戶端連接,返回與連接上的客戶端通信的套接字 int MyAccept(int listen_socket) {// 4、接收連接// 監(jiān)聽套接字不能用來與客戶端進行通信,它的職責是監(jiān)聽客戶端的連接// accpet 處理客戶端的連接,如果成功接收,會返回一個新的套接字,用來與客戶端進行通信// accept的第三個參數 是一個傳入傳出參數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 reg(int client_socket, struct Msg *msg) {printf ("%s 正在進行注冊\n", msg->msg);//下邊的代碼就是應該把注冊的消息寫進數據庫;int ret = Database_insert( msg );if(ret == -2){printf("該用戶id已經被注冊 \n");msg->cmd = -2;}else{msg->cmd = 1001;}//msg->cmd = 1001;/* {// 將用戶套接字進行保存,保存在數組里int i = 0;for (i = 0; i < 20; i++){if (user[i].socket == 0){strcpy (user[i].name , msg->msg);user[i].socket = client_socket;break;}}if (i == 2000){msg->cmd = -1;}else{msg->cmd = 1001;}}*/write (client_socket, msg, sizeof(struct Msg)); }//登錄驗證函數;void enter(int client_socket, struct Msg *msg){Database_enter(client_socket,msg );write(client_socket, msg ,sizeof(struct Msg));printf("11111111\n");}// 群發(fā) void chat(int client_socket, struct Msg *msg) {printf ("%s 發(fā)一次群消息\n", msg->msg);// 將用戶進行保存int i = 0;for (i = 0; i < 2000; i++){if (user[i].socket != 0){write (user[i].socket, msg, sizeof(struct Msg)); }} }// 私聊 void chat2(int client_socket, struct Msg *msg) {printf ("%s 要 給 %s 發(fā)一條消息\n", msg->fromname, msg->toname);//判斷數據庫是夠有此用戶int ret = Database_chat2(client_socket,msg);if(ret == -5){printf("此用戶不存在\n");write(client_socket,msg,sizeof(struct Msg));}//用戶才在于數據庫,查找是否在線?else if(ret == 11){printf("數據庫存在此用戶,正在判斷該用戶是否在線\n");//判斷用戶是否在線;int i = 0;for (i = 0; i < 2000; i++){if (user[i].socket != 0 && strncmp(user[i].name, msg->toname,strlen(msg->toname))==0)// if (user[i].socket != 0 && strncmp(user[i].name, msg->toname,strlen(msg->toname)-1)==0){printf("11111111\n");msg->cmd = 10;write(client_socket,msg,sizeof(struct Msg));printf("服務器判斷出該用戶在線,正在給該用戶發(fā)送消息....\n");msg->cmd = 3;write (user[i].socket, msg, sizeof(struct Msg));printf("1111111111\n");break;}}if(i == 2000){printf("服務器顯示該用戶不在線\n");msg->cmd = -6;write(client_socket,msg,sizeof(struct Msg));}}}//顯示在線好友列表; void display(int client_socket,struct Msg *msg) {//服務器端把user[]里邊的在線遍歷出來發(fā)送給客戶端;int i;msg->cmd = 4;for(i = 0;i < 2000;i++){if(user[i].socket != 0){//遍歷一次寫給客戶端一次;strncpy(msg->online_name ,user[i].name,strlen(user[i].name));write(client_socket, msg ,sizeof(struct Msg));printf("44444444\n");}}}//注銷用戶信息 int logout(int client_socket, struct Msg *msg) {sqlite3 * database;// 打開數據庫int ret = sqlite3_open("information.db", &database);if (ret != SQLITE_OK){printf ("打開數據庫失敗\n");return -1;}printf ("打開數據庫成功\n");char *errmsg = NULL;char buf[100];sprintf (buf, "delete from information where name = '%s'", msg->msg);ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);if (ret != SQLITE_OK){printf ("數據庫操作失敗:%s\n", errmsg);return -1;}printf("注銷成功\n");write(client_socket,msg, sizeof(struct Msg));printf("msg.cmd %d\n",msg->cmd);sqlite3_close(database); }// 把 負責處理客戶端通信的函數改成線程的工作函數 void* hanld_client(void* v) {int client_socket = (int)v;struct Msg msg;while(1){// 從客戶端讀一個結構體數據int ret = read(client_socket, &msg, sizeof(msg));//printf("2:%d,%d\n",msg.cmd,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 : // 客戶端進行群聊天chat(client_socket, &msg);break;case 3 : // 客戶端進行私聊chat2(client_socket, &msg);break;case 4: //客戶進行登錄;enter(client_socket,&msg);break;case 5: //顯示在線好友列表;display(client_socket,&msg);break;case 6: //注銷用戶;logout(client_socket,&msg);break;}}close (client_socket); }int main() {// 初始化套接字int listen_socket = init_socket();while (1){// 獲取與客戶端連接的套接字int client_socket = MyAccept(listen_socket);// 創(chuàng)建一個線程去處理客戶端的請求,主線程依然負責監(jiān)聽pthread_t id;pthread_create(&id, NULL, hanld_client, (void *)client_socket);pthread_detach(id); // 線程分離}close (listen_socket);return 0; }

(2)、客戶端代碼

這里寫代碼片#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #include <sqlite3.h> #define PORT 9999 void enter( int );char myName[20]; char Sex[20];struct Msg {char information[1024]; //發(fā)送消息;char msg[1024]; // (姓名)消息內容int id;char sex[20];int age;int password;char toname[20]; //消息發(fā)給誰char fromname[20]; //消息從誰的那邊發(fā)出來int cmd; // 消息類型char online_name[20]; //在線姓名; };// 主界面; void interface() {printf ("\t\t 1、注冊\n");printf ("\t\t 2、登錄\n");}void interface3() {//printf ("\t\t 1、注冊\n");printf ("\t\t 2、登錄\n");}//登錄后的界面; void interface2() {printf ("\t\t 1、發(fā)送文件\n");printf ("\t\t 2、群聊\n");printf ("\t\t 3、私聊\n");printf ("\t\t 4、注銷當前用戶\n");printf ("\t\t 5、顯示好友列表\n");printf ("\t\t 6、查看本地聊天記錄\n"); }//讀寫分離的讀線程的回調函數;void * readMsg(void *v){int socketfd = (int)v;struct Msg msg;while (1){read(socketfd, &msg, sizeof(msg));//printf("test msg.cmd %d,%d\n",msg.cmd,sizeof(msg));switch (msg.cmd){case -5: //私聊用戶不存在;printf("你想私聊的用戶不存在\n");break;case 10: //用戶存在并且在線;printf("用戶存在并且在線\n");break;case -6: //用戶不在線;printf("用戶存在但是不在線\n");break;case 2: // 群聊printf ("收到一條消息: %s\n", msg.information);break;case 3: // 私聊printf ("%s 給你發(fā)一條消息:%s\n", msg.fromname, msg.information);break;case 4: // 顯示在線好友列表;printf("在線的好友有:%s\n",msg.online_name);printf("3333333\n");break;case 6: //用戶注銷;printf("注銷成功!\n");return;break;}}}//注冊函數 int reg(int socketfd) {struct Msg msg;//msg.cmd = 1;while(1){msg.cmd = 1;//memset(&msg,0,sizeof(msg));//清零printf ("請輸入用戶名:\n");scanf("%s",&(msg.msg));printf ("請輸入用戶的密碼 :\n");scanf("%d",&(msg.password));write (socketfd, &msg, sizeof(msg));//來自服務器端的回復:到底有沒有注冊成功,成功了返回一個標號,要是失敗了,那么為什么失敗,返回另外一個標號,最后失敗的需要全部重新注冊read (socketfd, &msg, sizeof(msg));if (msg.cmd == 1001){printf ("你已注冊成功,歡迎加入聊天室\n");/* int a;while (1){printf("\n");interface3();printf("\n");printf("\n");sleep(1);printf("\t\t*_*輸入功能:\n");scanf("%d",&a);printf("\n");while(getchar()!= '\n');switch(a){case 2: //登錄;enter(socketfd);break;}system("clear");}*/break;}//用戶已經被注冊過了,重新注冊if (msg.cmd == -2){printf ("該用戶名已經被注冊,請重新進行注冊\n");memset(&msg,0,sizeof(msg));//清零continue;}//聊天室的登錄人數上限已經到達注冊失敗;else if(msg.cmd == -1){printf ("聊天室的上限已經到達,請重新進行注冊\n");continue;}}sleep(1);}//群聊函數, void chat(int socketfd) {struct Msg msg;msg.cmd = 2;printf ("請輸入要發(fā)送的內容: \n");//fgets(msg.information, 1024, stdin);scanf("%s",msg.information);write (socketfd, &msg, sizeof(msg)); }//私聊; void chat2(int socketfd) {struct Msg msg;msg.cmd = 3;printf ("請輸入要發(fā)送的對象名稱: \n");//fgets(msg.toname, 20, stdin);scanf("%s",msg.toname);printf ("請輸入要發(fā)送的內容: \n");//fgets(msg.information, 1024, stdin);scanf("%s",msg.information);printf("輸入自己的名字 : \n");//fgets(msg.fromname, 20, stdin);scanf("%s",msg.fromname);write (socketfd, &msg, sizeof(msg));//調用存儲聊天記錄的函數;int ret = insert_msg( &msg );if(ret == 1){printf("成功保存本地記錄\n");} }//注銷用戶; void logout(int socketfd) {char ch[2];printf("是否注銷此用戶: y/n \n");scanf("%c",ch);if(ch[0] == 'y'){struct Msg msg;msg.cmd = 6;printf("輸入您的用戶名:\n");scanf("%s",msg.msg);write(socketfd, &msg,sizeof(struct Msg)); }else{return ;}}//顯示在線好友列表; void display(int socketfd) {struct Msg msg;printf("我想知道在線的好友列表\n");msg.cmd = 5;write(socketfd, &msg, sizeof(struct Msg)); } //登錄函數; void enter(int socketfd) {struct Msg msg;msg.cmd = 4;printf("請輸入用戶名 :\n");scanf("%s",&(msg.msg));printf("請輸入用戶密碼:\n");scanf("%d",&(msg.password));write (socketfd, &msg, sizeof(msg)); //消息寫過去,等待回復,例如什么用戶不存在,密碼錯誤;if(msg.cmd == 5){}read(socketfd ,&msg ,sizeof(msg)); //讀取來自服務器的返回的消息,進行客戶端屏幕打印;//printf("%d,%d\n",msg.cmd,sizeof(msg));if(msg.cmd == 1002){printf("恭喜你登錄成功\n");//登錄完成之后開始讀寫分離;sleep(1);pthread_t id;pthread_create(&id, NULL, readMsg, (void *)socketfd);pthread_detach(id); // 線程分離 int a;while (1){interface2();printf("\n");printf("\n");sleep(1);printf("\t\t*_*輸入功能:\n");scanf("%d",&a);printf("\n");while(getchar()!= '\n');switch(a){case 1: //發(fā)送文件send_file(socketfd);break;case 2: // 群聊天chat(socketfd);break;case 3: // 私聊chat2(socketfd);break;case 4 : //注銷用戶;logout(socketfd);return;case 5: //顯示在線好友列表;display(socketfd);sleep(1);break;case 6: //查看本地聊天記錄;look_msg(socketfd);sleep(1);break;}system("clear");}}else if(msg.cmd == -1){printf("密碼不正確\n");usleep(5*100000);}else if(msg.cmd == -2){printf("該用戶不存在\n");sleep(0.5);}}//聊天記錄存到本地; int insert_msg(struct Msg* msg) { sqlite3 *database;int ret = sqlite3_open( "msg.db", &database );if(ret != SQLITE_OK){perror("sqlite3_open");return -1;}printf("數據庫打開成功\n"); //下邊一大段是創(chuàng)建一個表char *errmsg = NULL;char *sql = "create table if not exists msg (fromname TEXT, information TEXT,toname TEXT)"; //sql語句 ret = sqlite3_exec(database ,sql, NULL ,NULL ,&errmsg);//執(zhí)行sql語句的函數;if(ret != SQLITE_OK){printf("數據庫創(chuàng)建操作失敗 %s\n",errmsg);return -1;}//往表插入數據;//insert into student values(1, 'Zhang', 'M', 18);char buf[100];printf("%s\n",msg->fromname);sprintf(buf," insert into msg values('%s','%s' ,'%s')",msg->fromname ,msg->information ,msg->toname );//設置主鍵之后,如果插入的id已經存在那么就會返回錯誤,一班來說返回的錯誤都是插入重復這里有點取巧;ret = sqlite3_exec(database ,buf, NULL ,NULL ,&errmsg);if (ret != SQLITE_OK){printf ("數據庫插入操作失敗:%s\n", errmsg);return -1;}printf("聊天記錄成功保存到本地數據庫*_*\n");sqlite3_close(database);return 1; }//查看本地聊天記錄; int look_msg(int socketfd) {//本地創(chuàng)建一張數據庫表,存放本地的消息;//查看不就簡單了,直接打開本地的數據庫就好了;sqlite3 *database;int ret = sqlite3_open( "msg.db", &database );if(ret != SQLITE_OK){perror("sqlite3_open");return -1;}printf("數據庫打開成功\n"); char ** resultp;char *errmsg = NULL;int nrow;int ncolumn;//進行什么操作;char *sql = "select * from msg";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg );if(ret != SQLITE_OK){printf("數據庫操作失敗 %s\n",errmsg);return -1;}int i;printf("nrow = %d,ncolumn = %d",nrow, ncolumn);for(i = 0; i < (nrow +1)* ncolumn;i++){if(i % ncolumn == 0){printf("\n");}printf("%-18s" ,resultp[i]);}printf("\n");sqlite3_free_table(resultp); //之前的char**resultp (數組)等于malloc了一個空間來儲存數據庫的數據;所以需要釋放;sqlite3_close(database); }//傳送文件; send_file(int socketfd) {}// 客戶端向服務器發(fā)送數據處理函數; void ask_server(int socketfd) {int a ;while (1){//char ch[2];int a;while (1){printf("\n");interface();printf("\n");printf("\n");sleep(1);printf("\t\t*_*輸入功能:\n");scanf("%d",&a);printf("\n");while(getchar()!= '\n');switch(a){case 1: //注冊;reg(socketfd);printf("注冊注冊注冊\n");break;case 2: //登錄;enter(socketfd);break;}system("clear");}} }int main() { // 創(chuàng)建與服務器通信的套接字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));// 連接服務器,如果成功,返回0,如果失敗,返回-1// 成功的情況下,可以通過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; }

總結

以上是生活随笔為你收集整理的聊天室项目的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。