类的加载时机
901 2023-04-03 04:27:40
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 8024int 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 8024int 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,当@某人时会将私聊发出的消息再次群发给所有人,需要改进。