1.實驗專案名稱:聊天室的設計與實作
2.實驗目的:完成局域網內聊天功能,進一步掌味訓于socket的編程方法和執行緒創建方法.
3.實驗程序:
通過socket 建立用戶連接并傳送用戶輸入的資訊,分別來寫客戶端和服務器段,利用多執行緒來實作多用戶模式,服務器隨時準備接受客戶端發送的訊息,并判斷該訊息型別(私聊或群聊)來進行對應的轉發作業,客戶端隨時接受來自服務器端的訊息,從而實作訊息的同步,
(1)開啟服務器

(2)創建用戶

(3)群聊的實作

(4)私聊的實作

(5)離開聊天室
ser.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <semaphore.h> #include <pthread.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8024 int i=0; int aa; struct kehu { char name[10];//記錄用戶的姓名方便輸出及辨認 int haoma;//記錄用戶連接的sockfd值 } ct[100]; //定義發送(拼音)函式,將訊息通過結構體中存盤的sockfd值發送給所有服務器 void fasong(char *qq,int d){ int n=0; for(n;n<i;n++){ //如果是呼叫(發送函式)本身的客戶端直接跳過,不發送 if(ct[n].haoma==d){ continue; } write(ct[n].haoma,qq,strlen(qq)); } } //制作客戶端的執行緒 void *cli(void *bb) { int aa=(*(int *)bb); int sql; char bk[200]; char bj[200]; char bj1[100]; char bna[100]; char ini[200]={}; char s[10]={"@"}; char *p; char *pi; int q; //先讀一遍,接收賬戶名,發送給其他用戶歡迎的提示 read(aa,bna,sizeof(bna)); sprintf(bj1,"歡迎%s進入聊天室",bna); fasong(bj1,aa); //將名字存入結構體 strcpy(ct[i-1].name,bna); while(1) { q=read(aa,bk,sizeof(bk)); printf("%s\n",bk); //客戶端斷開連接時,歡送,釋放存盤空間 if(q==0){ sprintf(bj,"歡送%s離開聊天室",bna); fasong(bj,aa); int u=0; //回圈遍歷至斷開的客戶端位置 for(u;u<i;u++){ if(ct[u].haoma==aa); break; } //將斷開位置后的客戶端結構體依次向前存盤 for(u;u<i;u++){ strcpy(ct[i].name,ct[i+1].name); ct[i].haoma=ct[i+1].haoma; } //將指向創建結構體位置的 i 減一 i-=1; break; } else{ p=bk; sql=0; for(p;*p!='\0';p++){ //私聊功能,遍歷內容中是否有@來確定是否為私聊 if (*p==*s) { int l1=0; int l2=0; //以下內容 for(l1;l1<i;l1++){ pi=bk; for(pi;*pi!='\0';pi++){ if(*pi==*ct[l1].name){ sprintf(bj,"%s對你說%s",bna,bk); write(ct[l1].haoma,bj,sizeof(bj)); break; } } } sql=1; break; } } //正常聊天 if(sql==1){ continue; } sprintf(bj,"%s說:%s",bna,bk); fasong(bj,aa); } } } int main(){ //建立網路連接 int sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd==-1){ perror("failed socket"); exit(-1); } printf("網路連接建立成功\n"); //創建結構體 struct sockaddr_in addr; addr.sin_family=AF_INET; addr.sin_port=htons(PORT); inet_aton("127.0.0.1",&addr.sin_addr); printf("結構體創建成功\n"); //進行連接系結 int pp=bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)); if(pp==-1){ perror("failed bind"); exit(-1); } //建立監聽 listen(sockfd,100); printf("正在初始化\n"); //為連接做定義 struct sockaddr_in from; socklen_t len = sizeof(from); int tid[10]; printf("初始化成功\n"); printf("等待連接\n"); //回圈等待 while(1){ //等待連接 aa=accept(sockfd,(struct sockaddr *)&from,&len); //將用戶的編號存入結構體 ct[i].haoma=aa; pthread_t tid[i]; pthread_create(&tid[i],NULL,cli,&aa); printf("%d號客戶端連接成功\n",i); i+=1; } }
cli.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <pthread.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8024 int sockfd; //定義一個執行緒用于讀取服務器發來的資料 void *rr(void *bb){ while(1){ char ba[200]={0}; int aa=*(int *)bb; read(aa,ba,sizeof(ba)); printf("%s\n",ba); } } int main(){ sockfd=socket(AF_INET,SOCK_STREAM,0); if (sockfd==-1){ perror("failed socket"); exit(-1); } struct sockaddr_in addr; addr.sin_family=AF_INET; addr.sin_port=htons(PORT); inet_aton("127.0.0.1",&addr.sin_addr); connect(sockfd,(struct sockaddr *)&addr,sizeof(addr)); char bk[100]; char bl[100]; char bd[100]={}; printf("*********************\n"); printf("請輸入您的賬戶名\n"); scanf("%s",bk); printf("*********************\n"); //將用戶的賬戶名發送給服務器 write(sockfd,bk,sizeof(bk)); //通過復制來清空bk的內容 strcpy(bk,bd); //制作讀取執行緒 pthread_t tid; pthread_create(&tid,NULL,rr,&sockfd); //回圈讀取并發送給服務器 while(1){ scanf("%s",bl); write(sockfd,bl,sizeof(bl)); } }
4.心得體會
對socket定義及格式,select函式使用有了一定的了解,用socket實作多人聊天的思路,當服務器接收到來自某一客戶端的資料時,直接轉發給其他所有連接這的客戶端,即完成了多人聊天;但是實作聊天室的私聊有bug,當@某人時會將私聊發出的訊息再次群發給所有人,需要改進,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/285698.html
標籤:其他
上一篇:經典實驗--學生成績管理系統
下一篇:俄羅斯方塊的設計與實作
