1.實驗項目名稱:基於Linux的TCP網絡聊天室
2.實驗目的:通過TCP完成多用戶群聊和私聊功能。
3.實驗過程:
通過socket建立用戶連接並傳送用戶輸入的信息,分別來寫客戶端和服務器端,利用多線程來實現多用戶模式,服務器端隨時准備接收客戶端發送的消息,並判斷該消息類型(私聊或群聊)來進行對應的轉發工作,客戶端隨時接受來自服務器端的消息,從而實現消息的同步。
(1)開啟服務器。
(2)開啟客戶端,輸入用戶昵稱,客戶端開始與服務器建立連接。
(3)群聊功能,一名用戶發送消息,聊天室的其他成員均可收到此消息。
(4)私聊功能,一位用戶@其他用戶,則此消息只有被@的用戶可以收到。
用戶的信息借助結構體來存放,群聊功能通過遍歷結構體,把消息發送給每一名用戶,私聊功能通過對用戶所發消息進行判斷,若消息中存在@其他用戶則把該消息私發給指定用戶。
另外,私聊功能函數可以進行對各種@操作的判斷,如用戶所@的人不存在,則把此消息作為群消息發出,@其他用戶后沒有添加空格,則把此消息視為群發消息。
源碼:
頭文件
1 //header.h 2 #ifndef _A_H 3 #define _A_H 4 #include <stdio.h> 5 #include <pthread.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 #include <arpa/inet.h> 9 #include <stdlib.h> 10 #include <unistd.h> 11 #include <string.h> 12 #include <signal.h> 13 #include <pthread.h> 14 struct client{ 15 char name[30]; //客戶端名字 16 int fds; //客戶端socket描述符 17 }; 18 struct client c[100]={0};//接收客戶端100個 19 int in; //數組下標 20 char *IP ="127.0.0.1"; //ip 21 short PORT=10222; 22 char NAME[20]={}; //@的名字 23 int FLAG=0; //標記是群發還是私聊 24 #endif
服務器端
1 //tcp_s.c 2 #include "header.h" 3 int sockfd; //服務器socket 4 char tempName[20]={}; 5 //初始化服務器網絡 6 void init(){ 7 printf("start chat room..."); 8 sockfd = socket(PF_INET,SOCK_STREAM,0); 9 if(sockfd == -1){ 10 perror("create socket false\n"); 11 exit(-1); 12 } 13 struct sockaddr_in addr; //網絡通信地址結構 14 addr.sin_family = PF_INET; //協議簇 15 addr.sin_port = htons(PORT); //端口 16 addr.sin_addr.s_addr = inet_addr(IP); //IP地址 17 if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) == -1){ 18 perror("bind false\n"); 19 exit(-1); 20 } 21 if(listen(sockfd,100) == -1){ 22 perror("Setup listening failed\n"); 23 exit(-1); 24 } 25 printf("Server initialization succeeded!\n"); 26 }
分發消息
1 void SendMessage(char *msg){ 2 int flag=0; //標記@符號是否為私聊 3 int exit=0; //比較@的用戶是否存在 4 int i=0,j=0; 5 while(msg[i]!=':'){ 6 if(i>=strlen(msg)) 7 break; 8 i++; 9 } //找到@符號位置 10 if(msg[i+1] == '@'){ 11 i++; 12 while(msg[i]!=32){ 13 if(i>=strlen(msg)){ //@符號不是私聊作用 14 flag=1; 15 break; 16 } 17 NAME[j]=msg[i+1]; 18 i++; 19 j++; 20 } 21 if(flag){ //群發 22 for(i=0;i<in;i++){ 23 printf("send to %d\n",c[i].fds); 24 send(c[i].fds,msg,strlen(msg),0); 25 } 26 } 27 else{ //私發 28 for(i=0;i<in;i++){ 29 int k=0; 30 for(k=0;k<strlen(NAME)-1;k++){ 31 tempName[k]=NAME[k]; 32 } 33 if(strcmp(c[i].name,tempName)==0){ 34 send(c[i].fds,msg,strlen(msg),0); 35 exit=1; 36 break; 37 } 38 } 39 } 40 } 41 else{ 42 exit = 1; 43 for(i=0;i<in;i++){ 44 printf("send to %d\n",c[i].fds); 45 send(c[i].fds,msg,strlen(msg),0); 46 } 47 } 48 if(exit == 0){ 49 for(i=0;i<in;i++){ 50 printf("send to %d\n",c[i].fds); 51 send(c[i].fds,msg,strlen(msg),0); 52 } 53 } 54 }
線程函數中進行通信/接收客戶端消息,分發給所有客戶端
1 void *server_thread(void *p){ 2 char name[30]={}; 3 if(recv(c[in].fds,name,sizeof(name),0)>0){ 4 name[strlen(name)]='\0'; 5 strcpy(c[in].name,name); 6 } 7 in++; 8 char tip[100]={}; 9 sprintf(tip,"%s join in the chat room\n",c[in-1].name); 10 SendMessage(tip); 11 int fd = *(int*)p; 12 printf("pthread = %d\n",fd); 13 while(1){ 14 char buf[100]={}; 15 if(recv(fd,buf,sizeof(buf),0) == 0){ 16 //表示退出連接 17 printf("fd = %d left the chat room\n",fd); 18 int i,j; 19 char name[20]={}; 20 int flag = 1; 21 for(i=0;i<in;i++){ 22 if(c[i].fds == fd){ 23 strcpy(name,c[i].name); 24 i++; 25 flag = 0; 26 } 27 if(flag != 1){ 28 c[i-1].fds = c[i].fds; 29 strcpy(c[i-1].name,c[i].name); 30 } 31 } 32 c[i].fds = 0; 33 strcpy(c[i].name,""); 34 in--; 35 char msg[100]; 36 sprintf(msg,"%s left the chat room\n",name); 37 SendMessage(msg); 38 close(fd); 39 break; 40 } 41 SendMessage(buf); 42 } 43 }
等待客戶端連接,啟動服務器的服務
1 void server(){ 2 printf("Server starts service!\n"); 3 while(1){ 4 struct sockaddr_in fromaddr; //存儲客戶端通信地址 5 socklen_t len = sizeof(fromaddr); 6 int fd = accept(sockfd,(struct sockaddr*)&fromaddr,&len); 7 if(fd == -1){ 8 perror("Client connection failed!\n"); 9 continue; 10 } 11 c[in].fds = fd; 12 pthread_t pid; 13 pthread_create(&pid,0,server_thread,&fd); 14 } 15 } 16 void sig_close(){ 17 close(sockfd); 18 printf("Server close\n"); 19 exit(0); 20 } 21 int main() 22 { 23 signal(SIGINT,sig_close); 24 init(); 25 server(); 26 return 0; 27 }
客戶端
1 //tcp_c.c 2 #include "header.h" 3 char name[30]; //name 4 int sockfd; //客戶端socket 5 int mutax=0; //互斥變量 6 void init(){ 7 printf("Client starts\n"); 8 sockfd =socket(PF_INET,SOCK_STREAM,0); 9 struct sockaddr_in addr; 10 addr.sin_family =PF_INET; 11 addr.sin_port=htons(PORT); 12 addr.sin_addr.s_addr=inet_addr(IP); 13 if(connect(sockfd,(struct sockaddr*)&addr,sizeof (addr))==-1){ 14 perror("connect failed\n"); 15 exit(-1); 16 } 17 }
通信
1 void start(){ 2 pthread_t pid; 3 void* receive_thread(void*); 4 pthread_create(&pid,0,receive_thread,0); 5 while(1){ 6 char buf[100]={}; 7 gets(buf); 8 char msg[100]={}; 9 if(mutax){ 10 mutax=0; 11 continue; 12 } 13 sprintf(msg,"%s said:%s",name,buf); 14 send(sockfd,msg,strlen(msg),0); 15 } 16 17 } 18 void* receive_thread(void *p){ 19 while(1){ 20 char buf[100]={}; 21 if(recv(sockfd,buf,sizeof(buf),0)<=0){ 22 break; 23 } 24 printf("%s\n",buf); 25 } 26 } 27 void sig_close(){ 28 close(sockfd); 29 exit(0); 30 } 31 int main(){ 32 signal(SIGINT,sig_close); 33 printf("Please input your name:"); 34 scanf("%s",name); 35 mutax=1; 36 init(); 37 if(mutax) 38 send(sockfd,name,strlen(name),0); 39 start(); 40 return 0; 41 }