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 }