聊天室项目
1、簡述:
采用了cs結(jié)構(gòu)
服務(wù)器端四步走:socket,bind,listen,accept
客戶端兩步走:socket,connect
簡單模型:客戶端a消息發(fā)送到服務(wù)器;服務(wù)器找到客戶端b的socket,在把消息發(fā)送給客戶端b;
設(shè)計(jì)數(shù)據(jù)庫操作存儲(chǔ)的一些操作比較簡單這里不提
2、看代碼:
(1)、服務(wù)器端代碼
(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]; // (姓名)消息內(nèi)容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、注冊(cè)\n");printf ("\t\t 2、登錄\n");}void interface3() {//printf ("\t\t 1、注冊(cè)\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、注銷當(dāng)前用戶\n");printf ("\t\t 5、顯示好友列表\n");printf ("\t\t 6、查看本地聊天記錄\n"); }//讀寫分離的讀線程的回調(diào)函數(shù);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;}}}//注冊(cè)函數(shù) int reg(int socketfd) {struct Msg msg;//msg.cmd = 1;while(1){msg.cmd = 1;//memset(&msg,0,sizeof(msg));//清零printf ("請(qǐng)輸入用戶名:\n");scanf("%s",&(msg.msg));printf ("請(qǐng)輸入用戶的密碼 :\n");scanf("%d",&(msg.password));write (socketfd, &msg, sizeof(msg));//來自服務(wù)器端的回復(fù):到底有沒有注冊(cè)成功,成功了返回一個(gè)標(biāo)號(hào),要是失敗了,那么為什么失敗,返回另外一個(gè)標(biāo)號(hào),最后失敗的需要全部重新注冊(cè)read (socketfd, &msg, sizeof(msg));if (msg.cmd == 1001){printf ("你已注冊(cè)成功,歡迎加入聊天室\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;}//用戶已經(jīng)被注冊(cè)過了,重新注冊(cè)if (msg.cmd == -2){printf ("該用戶名已經(jīng)被注冊(cè),請(qǐng)重新進(jìn)行注冊(cè)\n");memset(&msg,0,sizeof(msg));//清零continue;}//聊天室的登錄人數(shù)上限已經(jīng)到達(dá)注冊(cè)失敗;else if(msg.cmd == -1){printf ("聊天室的上限已經(jīng)到達(dá),請(qǐng)重新進(jìn)行注冊(cè)\n");continue;}}sleep(1);}//群聊函數(shù), void chat(int socketfd) {struct Msg msg;msg.cmd = 2;printf ("請(qǐng)輸入要發(fā)送的內(nèi)容: \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 ("請(qǐng)輸入要發(fā)送的對(duì)象名稱: \n");//fgets(msg.toname, 20, stdin);scanf("%s",msg.toname);printf ("請(qǐng)輸入要發(fā)送的內(nèi)容: \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));//調(diào)用存儲(chǔ)聊天記錄的函數(shù);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)); } //登錄函數(shù); void enter(int socketfd) {struct Msg msg;msg.cmd = 4;printf("請(qǐng)輸入用戶名 :\n");scanf("%s",&(msg.msg));printf("請(qǐng)輸入用戶密碼:\n");scanf("%d",&(msg.password));write (socketfd, &msg, sizeof(msg)); //消息寫過去,等待回復(fù),例如什么用戶不存在,密碼錯(cuò)誤;if(msg.cmd == 5){}read(socketfd ,&msg ,sizeof(msg)); //讀取來自服務(wù)器的返回的消息,進(jìn)行客戶端屏幕打印;//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("數(shù)據(jù)庫打開成功\n"); //下邊一大段是創(chuàng)建一個(gè)表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語句的函數(shù);if(ret != SQLITE_OK){printf("數(shù)據(jù)庫創(chuàng)建操作失敗 %s\n",errmsg);return -1;}//往表插入數(shù)據(jù);//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 );//設(shè)置主鍵之后,如果插入的id已經(jīng)存在那么就會(huì)返回錯(cuò)誤,一班來說返回的錯(cuò)誤都是插入重復(fù)這里有點(diǎn)取巧;ret = sqlite3_exec(database ,buf, NULL ,NULL ,&errmsg);if (ret != SQLITE_OK){printf ("數(shù)據(jù)庫插入操作失敗:%s\n", errmsg);return -1;}printf("聊天記錄成功保存到本地?cái)?shù)據(jù)庫*_*\n");sqlite3_close(database);return 1; }//查看本地聊天記錄; int look_msg(int socketfd) {//本地創(chuàng)建一張數(shù)據(jù)庫表,存放本地的消息;//查看不就簡單了,直接打開本地的數(shù)據(jù)庫就好了;sqlite3 *database;int ret = sqlite3_open( "msg.db", &database );if(ret != SQLITE_OK){perror("sqlite3_open");return -1;}printf("數(shù)據(jù)庫打開成功\n"); char ** resultp;char *errmsg = NULL;int nrow;int ncolumn;//進(jìn)行什么操作;char *sql = "select * from msg";ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg );if(ret != SQLITE_OK){printf("數(shù)據(jù)庫操作失敗 %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 (數(shù)組)等于malloc了一個(gè)空間來儲(chǔ)存數(shù)據(jù)庫的數(shù)據(jù);所以需要釋放;sqlite3_close(database); }//傳送文件; send_file(int socketfd) {}// 客戶端向服務(wù)器發(fā)送數(shù)據(jù)處理函數(shù); 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: //注冊(cè);reg(socketfd);printf("注冊(cè)注冊(cè)注冊(cè)\n");break;case 2: //登錄;enter(socketfd);break;}system("clear");}} }int main() { // 創(chuàng)建與服務(wù)器通信的套接字int socketfd = socket(AF_INET, SOCK_STREAM, 0);if (socketfd == -1){perror ("socket");return -1;}// 連接服務(wù)器struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET; // 設(shè)置地址族addr.sin_port = htons(PORT); // 設(shè)置本地端口inet_aton("127.0.0.1",&(addr.sin_addr));// 連接服務(wù)器,如果成功,返回0,如果失敗,返回-1// 成功的情況下,可以通過socketfd與服務(wù)器進(jìn)行通信int ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));if (ret == -1){perror ("connect");return -1;}printf ("成功連上服務(wù)器\n");//開始進(jìn)行操作;ask_server(socketfd);// 關(guān)閉套接字close(socketfd);return 0; }總結(jié)
- 上一篇: 拿到6个重磅offer的大神,超详细面试
- 下一篇: 迅雷软件一直出现崩溃问题的常见解决方法