linux下自创网络编程聊天室(3)

来源:互联网 发布:推淘宝店铺的微博 编辑:程序博客网 时间:2024/06/12 21:47

总体设计

本聊天室系统采用了c/s形式。服务器主要是处理客户输入信息。首先要存储客户的个人资料,相当于注册。再有,在客户的聊天信息时,也要记录下客户的聊天记录,已备查看聊天记录所用。当然,服务器还有自己的动态数据处理。客户状态分为链接客户和非连接客户,我采用结构体存储链接客户信息,以链表来记录链接客户。而链接客户又分为登陆客户和未登陆客户,这就通过修改链表上客户的名字。当客户一链接客户服务端时,就给客户一个账号,也就是相当于注册信息,同时并发服务器一直在为连接用户创建线程。


本聊天室登陆界面

1.登录

2.注册

3.退出

登录

登录的账号必须注册,帐号密码必须匹配都对,登录的账号必须不在线

注册

注册账号id必须和已有的不同,注册时填写账号,密码以及昵称

退出

退出程序


登录成功后可以使用功能

你可以使用的功能如下\
1.查找好友

2.显示好友

3.删除好友

4.添加好友(需要先发送申请好友请求,对方同意才能添加,也有回复信息提示)

5.查看或修改个人信息

6.与好友发送私信              (不在线也能发)(消息都会有发送时候的时间显示)

7.群聊                                       (能查看当前参与群聊的有多少人,账号和昵称分别是多少)

8.与好友聊天窗口(屏幕分区输入输出)                               (屏幕上半部输出双方聊天信息,下方输入信息,类似QQ界面)

9.在线查看聊天记录

10.从服务器下载本人聊天记录

open.查看是否有新消息                                   (类似好友请求,好友私信等,如不看会保存,下次上线再次提示)

11.退出






4.添加好友功能,如果对方在线,发送消息,如果对方不在线,保存消息,对方上线再发送,直到对方选择添加或拒绝添加消息才清除


账号8与789之间演示




发送之后对方需要输入open查看信息,(也可以不看,但消息一直在)

下图是789账号的界面



同意或者不同意

对方账号能收到反馈




6.与好友发送私信

此人必须为好友





发送陈功

发送结束时要输入over

对方会显示收到新消息,打开open查看



对方收到后





没有消息时候open







7.群聊功能

都能加入。都能收到,也可以查看当前在线人数以及他们的账号和昵称

进入聊天室会有音乐声音提示

输入的信息在别人那里会有前缀账号信息和发送时间





可以输入look查看  上图所示



下图所示,当退出时其他客户端会收到某某某退出





10.在线从服务器查看聊天记录,曾私人聊天的记录




输入11,也可以从服务器下载此聊天记录文件到本地







!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

但是一般的实时聊天,如果一个客户端正在输入,没有发送,,,此时另一个客户端发送消息过来,会把正在输入的消息一起顺带打印出去,这是由于光标和缓冲区的问题,在没有用界面软件,如qt,需要用到如下的东西

这暂时需要好友在线,并也打开chat双人聊天,否则可以发私信先沟通






这样的上面输出,下面输入,就需要用到特殊的控制光标方法,界面有待美化。



客户端代码如下:


#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/stat.h>#include <unistd.h>int n=0;       //全局变量n控制双人实时聊天输出在第几行int leap=0;//控制线程shou1运行int leap1=0;//控制线程fa运行//用于控制屏幕输入输出位置void handle(char str[1024],int people)   {char str1[1024];memset(str1,0,1024);n++;  //控制输出在第几行    printf("\033[0m\033[s");      //保存当前光标sprintf(str1,"\033[0m\033[%d;1H",n);printf(str1);               //光标跳到当前屏幕第n行 //   printf("\033[0m\033[1;1H");  //光标跳到第一行,第一格if(strcmp(str,"over")==0)//over就不打印并结束函数return;if(people==0)//people控制输出的本地用户输入信息还是对方信息printf("me say:%s\n",str);  elseprintf("%s\n",str);    printf("\033[0m\033[u");   //恢复光标    fflush(stdout);    //清空I/O缓存中的标准输出缓存使光标恢复到原点if(n>=9){system("clear");//清屏printf("\033[0m\033[20;1H");//光标跳到第二十行printf("Enter:\n");   //主进程输入printf("\033[0m\033[K"); //清空光标开始的当前行n=0;//输出超过9行行数归0}}//shou1线程接收双人实时聊天的信息void* shou1(void* newfd) {while(leap==1)           //全局变量控制线程进行{int fd=(int)newfd;char shou[1024];memset(shou,0,1024);recv(fd,shou,1024,0);if(strcmp(shou,"over")==0)   //收到后如果不是over就屏幕打印break;handle(shou,1);       //第一个参数是字符串,第二个如果是0本地输入,1外来输入   }}//shou线程接收服务器消息void* shou(void* newfd){int fd=(int)newfd;int ret=0,ret1=0;char shou[1024];char fa[1024];int people=0; //people等于0是本地消息,1为外来消息while(1){memset(shou,0,1024);if((ret=recv(fd,shou,1024,0))==-1)return;if(strcmp(shou,"xiazai")==0)//如果收到服务器发来“xiazai”,打开或创建本地文件接收内容{system("rm -f bendi.txt");//确保本地没有此文件int fp;//文件标识符fp=open("bendi.txt",O_WRONLY|O_CREAT|O_TRUNC);memset(shou,0,1024);//接收数据,如果“endend”表示接收结束while(1){recv(fd,&ret,sizeof(int),0);recv(fd,shou,ret,0);if(strncmp(shou,"endend",6)==0)break;write(fp,shou,ret);}close(fp); }//如果收到服务器发来“chat”,准备进行双人实时聊天室的建立else if(strcmp(shou,"chat")==0){leap=1;//控制线程shou1leap1=1;//控制线程famemset(shou,0,1024);recv(fd,shou,1024,0);puts(shou);//此句会从服务器接收输入你想聊天的好友id//输入要id memset(fa,0,1024);scanf("%s",&fa);send(fd,fa,strlen(fa),0); //进行判断memset(shou,0,1024);recv(fd,shou,1024,0);//由于不存在或不在线等原因,关闭对线程的控制并停止建立双人聊天室if(strcmp(shou,"此好友不存在")==0){leap=0;leap1=0;puts(shou);continue; }else if(strcmp(shou,"好友不在线或没开聊天,请发送私人消息")==0){leap=0;leap1=0;puts(shou);continue; }//连接成功清屏并按格式控制输出相关信息else if(strcmp(shou,"连接成功")==0){puts(shou);system("clear");pthread_t id3;pthread_create(&id3,NULL,shou1,(void*)fd);while(1){printf("\033[0m\033[20;1H");//光标跳到第20行printf("Enter:\n");   //主进程输入printf("\033[0m\033[K"); memset(fa,0,1024);scanf("%s",&fa);send(fd,fa,strlen(fa),0);if(strcmp(fa,"over")==0){leap=0;leap1=0;system("clear");printf("你可以使用的功能如下\n\t1.查找好友\n\t2.显示好友\n\t3.删除好友\n\t4.添加好友\n\t5.查看或修改个人信息\n\t6.与好友发送私信\n\t7.群聊\n\tchat.与好友聊天窗口(屏幕分区输入输出)\n\t10.在线查看聊天记录\n\t11.从服务器下载聊天记录\n\t8.退出\n\topen.查看消息");printf("\033[0m\033[20;1H");//光标跳到第20行break;}handle(fa,0);} }}elseputs(shou);if(ret==0){printf("服务器故障\n");exit(0);}}}//fa线程用来向服务器发送信息void* fa(void* newfd){int fd=(int)newfd;char fa[1024];while(1){if(leap1==0)//如果leap1==0,说明在建立双人聊天室,暂停本线程{memset(fa,0,1024);scanf("%s",&fa);if(send(fd,fa,strlen(fa),0)==-1)return;if(strcmp(fa,"chat")==0)//发送chat为要求建立双人聊天室{leap1=1;}if(strcmp(fa,"end")==0)//发送end结束客户端程序{printf("欢迎使用再见\n");close(fd);//关闭套接字exit(0);}}}}int main(){int sockfd=0;//定义并初始化int ret=0;int len=sizeof(struct sockaddr);struct sockaddr_in otheraddr;memset(&otheraddr,0,len);//tcp套接字连接if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){perror("sockfd");return -1;}else{printf("socket success...%d\n",sockfd);}//初始化结构体,把服务器ip地址和端口号otheraddr.sin_family = AF_INET;otheraddr.sin_port = htons(8889);otheraddr.sin_addr.s_addr=inet_addr("192.168.194.129");//连接服务器if(connect(sockfd,(struct sockaddr*)&otheraddr,len)==-1){perror("connect");return -1;}else{printf("connect success...client's fd=%d\n",sockfd);printf("--client ip=%s,port=%d\n",inet_ntoa(otheraddr.sin_addr),ntohs(otheraddr.sin_port));}//创建线程pthread_t id1,id2;    char recvbuf[1024];    char sendbuf[1024];memset(recvbuf,0,sizeof(recvbuf));memset(sendbuf,0,1024);//给服务器发送信息,握手判断是否建立连接strcpy(sendbuf,"hello,woshi cilent\n");if(send(sockfd,sendbuf,strlen(sendbuf),0)==-1){perror("send");return -1;}if(recv(sockfd,recvbuf,1024,0)==-1){perror("recv");return -1;}else{printf("sever say:%s\n",recvbuf);}//启动客户端线程的收发功能pthread_create(&id2,NULL,fa,(void*)sockfd);pthread_create(&id1,NULL,shou,(void*)sockfd);//等待发送线程结束,退出客户端pthread_join(id2,NULL);return 0;}







服务器主要代码如下:


#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <netinet/in.h>#include <arpa/inet.h>#include <ctype.h>#include <memory.h>#include <netinet/ip.h>#include <netinet/tcp.h>#include <time.h>#include <sys/stat.h>#define SIZE sizeof(X)  //结构体X的大小typedef struct xinxi    //结构体xinxi,别名X{char id[20];         //每个账号唯一idchar passwd[20]; //账号密码char name[50]; //账号昵称char hy[100][20]; //好友列表,最大100个int hys;             //好友数量int online;          //0不在线,1在线int fd;              //存放客户端成功连接后accept产生的新的套接字,不在线为-5int chatroom; //存放是否打开了双人聊天室,打开为1,没打开为0struct xinxi *next; //下一条链表的首地址}X;typedef struct message    //结构体message,别名M{char id[20];          //收信息的人char id1[20];         //发信息的人int type;             //信息类型,好友请求1,私聊信息2,好友请求回复信息3char mess[1024];      //信息struct message * next;//下一条信息的首地址}M;//结构体头地址全局变量方便调用X* head=NULL;M* head1=NULL;//分别为账号个数和保存信息的个数int count=0;int count1=0;int zhzx(char id[20]);//函数声明//用来保存账号信息void baocun(){FILE *fp;fp=fopen("123","w");    //保存在当前运行服务器文件下123if(fp==NULL){printf("open error\n");return;}printf("count=%d",count);fwrite(&count,sizeof(int),1,fp);//先保存账号个数X* p;p=head;if(p->next==NULL) //如果账号列表为空,关闭文件并退出函数{fclose(fp);return ;}p=p->next;while(p)                //按结构体大小保存账号信息{fwrite(p,sizeof(X),1,fp);p=p->next;}printf("账号信息保存成功\n");fclose(fp);}//保存未发出去的信息链表进文件void baocun1(){FILE *fp;fp=fopen("1234","w");   //保存在当前运行服务器文件下1234if(fp==NULL){printf("open error\n");return;}printf("count1=%d\n",count1);fwrite(&count1,sizeof(int),1,fp);   //首先保存信息个数M* p;p=head1;if(p->next==NULL)//如果信息列表为空,关闭文件并退出函数{fclose(fp);return ;}p=p->next;   //信息链表不为空进入保存while(p){fwrite(p,sizeof(X),1,fp);  //按保存信息结构体大小p=p->next;}printf("信息保存成功\n");fclose(fp);}//服务器上当前客户端id.txt保存聊天记录void baocun2(char str[1024],char id[20]){FILE *fp;char ch[1024];sprintf(ch,"%s.txt",id);fp=fopen(ch,"a+");        if(fp==NULL)//打开错误关闭程序return ;memset(ch,0,1024);//puts(str);strcat(str,"\r\n");      //txt文档每行句末尾加特殊换行符strcpy(ch,str);//puts(ch);fwrite(ch,strlen(ch),1,fp);printf("聊天记录保存成功\n");fclose(fp);}//创建账号信息的头指针,并从文件里读取保存已注册的账号信息X * create(){X* x;x=(X*)malloc(SIZE);if(x==NULL){printf("create error\n");return NULL;}x->next=NULL;FILE* fp;fp=fopen("123","a+");//打开文件,读取记录if(fp==NULL){printf("open error\n");return NULL;}if(fgetc(fp)==EOF)  //如果文件为空,返回头指针{fclose(fp);return x;}rewind(fp);          //文件指针重返文件头int n;fread(&n,sizeof(int),1,fp);//先读取长度printf("n=%d\n",n);count=n;         //全局变量获取账号长度X t;X *p,*p1;int i;for(i=0;i<n;i++)//按账号结构体长度读取信息{fread(&t,sizeof(X),1,fp);p1=x;while(p1->next){p1=p1->next;}p=(X*)malloc(sizeof(X));     //每次创建一个新的账号结构体大小空间来存信息p->next=NULL;strcpy(p->id,t.id);//账号,昵称,密码,好友及好友数读取保存的strcpy(p->name,t.name);strcpy(p->passwd,t.passwd);p->hys=t.hys;int j;for(j=0;j<p->hys;j++)strcpy(p->hy[j],t.hy[j]);p->fd=-5;               //每次服务器新连接字置-5p->chatroom=0;//双人聊天室置零为没打开p->online=0;//在线状态置零为不在线p1->next=p;}fclose(fp);return x;}//创建缓存信息的头指针,并从文件里读取保存未发送的信息M * create1(){M * x;x=(M*)malloc(sizeof(M));          //创建头结点if(x==NULL){printf("create error\n");return NULL;}x->next=NULL;FILE* fp;              fp=fopen("1234","a+");//打开文件并读取信息if(fp==NULL){printf("open error\n");return NULL;}if(fgetc(fp)==EOF)                   //如果为空文件关闭文件直接返回头指针{fclose(fp);return x;}rewind(fp);                         //重置文件指针int n;fread(&n,sizeof(int),1,fp);printf("n=%d\n",n); //先读取缓存信息长度count1=n;M t;M *p,*p1;int i;for(i=0;i<n;i++)//把信息取出{fread(&t,sizeof(M),1,fp);p1=x;while(p1->next){p1=p1->next;}p=(M*)malloc(sizeof(M));p->next=NULL;strcpy(p->id,t.id);strcpy(p->id1,t.id1);p->type=t.type;strcpy(p->mess,t.mess);p1->next=p;}fclose(fp);return x;}//判断字符串是否全是数字,是返回1,不是返回0int isnum1(char s[20]){int i=0;while(s[i]){if(!isdigit(s[i]))return 0;i++;}return 1;}void add(int fd)//注册功能函数{X *p1,*p,*p2;int leap=0;            //标识符,账号是否能正确注册p=(X*)malloc(SIZE);//开辟一个账号信息结构体大小的空间if(p==NULL)return;char str[256];char str1[256];memset(str,0,256);memset(str1,0,256);strcpy(str,"请输入你想注册的账号");send(fd,str,strlen(str),0);memset(str,0,256);recv(fd,str,256,0);strcpy(str1,str);if(!isnum1(str))          //判断是否纯数字账号{memset(str,0,256);strcpy(str,"请输入纯数字账号!\n");send(fd,str,strlen(str),0);return;}p1=head;while(p1->next){if(strcmp(p1->next->id,str)==0){leap=1;break;}p1=p1->next;}if(leap==1){memset(str,0,256);strcpy(str,"账号重复\n");send(fd,str,strlen(str),0);return;}//注册账号信息并赋初值strcpy(p->id,str1);memset(str,0,256);strcpy(str,"请输入密码");send(fd,str,strlen(str),0);memset(str,0,256);recv(fd,str,256,0);strcpy(p->passwd,str);memset(str,0,256);strcpy(str,"请输入昵称");send(fd,str,strlen(str),0);memset(str,0,256);recv(fd,str,256,0);strcpy(p->name,str);p1=head;while(p1->next){p1=p1->next;}p1->next=p;p->hys=0;p->online=0;p->fd=-5;p->next=NULL;memset(str,0,256);strcpy(str,"注册成功\n");send(fd,str,strlen(str),0);count++;         //全局变量账号数量+1baocun();//保存一次账号信息在文件}int yzzh(char id[20])//验证id是否存在{X *p;int leap=0;//账号信息结构体头结点开始遍历if(head->next==NULL)return 0;p=head->next;while(p){if(strcmp(id,p->id)==0)return 1;p=p->next;}return 0;}int yzmm(char id[20],char passwd[20])//验证passwd是否存在{X *p;int leap=0;if(head->next==NULL)return 0;p=head->next;while(p){if(strcmp(id,p->id)==0&&strcmp(passwd,p->passwd)==0)return 1;p=p->next;}return 0;}void look(int fd,char id[20])//查找好友,只能是已加为好友的好友{X* p,*p1;p=head;char fa[1024];char shou[1024];char shouid[1024];memset(fa,0,1024);memset(shou,0,1024);memset(shouid,0,1024);strcpy(fa,"输入您要查找的号码");send(fd,fa,strlen(fa),0);recv(fd,shouid,1024,0);if(yzzh(shouid)==0)                 //先从账号链表里查询是否存在此账号{memset(fa,0,1024);strcpy(fa,"此号码不存在");send(fd,fa,strlen(fa),0);return;}p=p->next;while(p)//从好友列表里查询是否有此好友{if(strcmp(p->id,id)==0)break;p=p->next;}if(p->hys==0){memset(fa,0,1024);strcpy(fa,"您没有此好友");send(fd,fa,strlen(fa),0);return;}int i;//有次好友打印相关信息for(i=0;i<(p->hys);i++){if(strcmp((p->hy[i]),shouid)==0){p1=head->next;char str[20];memset(str,0,20);while(p1){if(strcmp(p1->id,shouid)==0){strcpy(str,p1->name);break;}p1=p1->next;}memset(fa,0,1024);sprintf(fa,"此好友账号%s,昵称%s\n",shouid,str);send(fd,fa,strlen(fa),0);return;}}memset(fa,0,1024);sprintf(fa,"您没有此好友");send(fd,fa,strlen(fa),0);}void display(int fd,char id[20])//列出好友信息{X * p,*p1;int i;char fa[1024];char shou[1024];memset(fa,0,1024);memset(shou,0,1024);p=head->next;while(p){if(strcmp(p->id,id)==0)break;p=p->next;}if(p->hys==0){strcpy(fa,"您的好友列表为空");send(fd,fa,strlen(fa),0);return;}for(i=0;i<p->hys;i++){memset(fa,0,1024);p1=head->next;char str[20];int leap=0;memset(str,0,20);while(p1){if(strcmp(p1->id,p->hy[i])==0){strcpy(str,p1->name);//printf("%s\n",p1->id);leap=1;break;}p1=p1->next;}if(leap==1){memset(fa,0,1024);sprintf(fa,"账号%s\t昵称%s\n",p1->id,p1->name);send(fd,fa,strlen(fa),0);return ;}}}void del(int fd,char id[20])//删除好友{X *p,*p1;int i,leap=0;char fa[1024];char shou[1024];memset(fa,0,1024);memset(shou,0,1024);p=head->next;while(p){if(strcmp(p->id,id)==0)break;p=p->next;}if(p->hys==0)                   //先判断好友列表{strcpy(fa,"您的好友列表为空");send(fd,fa,strlen(fa),0);return;}memset(fa,0,1024);strcpy(fa,"输入你想删除的好友账号");send(fd,fa,strlen(fa),0);recv(fd,shou,1024,0);for(i=0;i<(p->hys);i++){if(strcmp((p->hy[i]),shou)==0){while(i<(p->hys-1)){strcpy(p->hy[i],p->hy[i+1]);i++;}memset(p->hy[i],0,20);p->hys--;memset(fa,0,1024);sprintf(fa,"删除成功");send(fd,fa,strlen(fa),0);leap=1;baocun();break;}}if(1==leap)    //如果删除本地成功,在对方好友列表中也删除自己{p1=head->next;while(p1){if(strcmp(p1->id,shou)==0)break;p1=p1->next;}for(i=0;i<(p1->hys);i++){if(strcmp((p1->hy[i]),p->id)==0){while(i<(p1->hys-1)){strcpy(p1->hy[i],p1->hy[i+1]);i++;}memset(p1->hy[i],0,20);p1->hys--;memset(fa,0,1024);sprintf(fa,"对方那也删除成功");send(fd,fa,strlen(fa),0);baocun();break;}}}else{memset(fa,0,1024);sprintf(fa,"删除失败,您没有此好友");send(fd,fa,strlen(fa),0);}}//加好友void addhy(int fd,char id[20]){X *p,*p1;int i;char fa[1024];char shou[1024];memset(fa,0,1024);memset(shou,0,1024);p=head->next;while(p){if(strcmp(p->id,id)==0)break;p=p->next;}strcpy(fa,"输入你想添加的好友账号");send(fd,fa,strlen(fa),0);recv(fd,shou,1024,0);memset(fa,0,1024);strcpy(fa,"消息已发送");send(fd,fa,strlen(fa),0);if(yzzh(shou)==0)//判断是否有账号{memset(fa,0,1024);strcpy(fa,"没有此账号");send(fd,fa,strlen(fa),0);return;}if(strcmp(shou,p->id)==0)     //不能添加自己为好友{memset(fa,0,1024);strcpy(fa,"不能添加自己为好友");send(fd,fa,strlen(fa),0);return;}for(i=0;i<p->hys;i++)          //此好友已添加,无需再添加{if(strcmp(shou,p->hy[i])==0){memset(fa,0,1024);strcpy(fa,"此好友已添加,无需再添加");send(fd,fa,strlen(fa),0);return;}}p1=head->next;time_t timep;while(p1)//给对方发了好友信息,通知对方,如果对方不在线,上线再通知{if(strcmp(p1->id,shou)==0){M* m,*p3;m=(M*)malloc(sizeof(M));m->type=1;strcpy(m->id,shou);strcpy(m->id1,p->id);memset(fa,0,1024);time(&timep);sprintf(fa,"%s\n%s想添加您为好友,y同意,n不同意",ctime(&timep),p->id);strcpy(m->mess,fa);m->next=NULL;p3=head1;while(p3->next)p3=p3->next;p3->next=m;count1++;//如果对方在线,给对方发送有新消息if(zhzx(p1->id)==1){memset(fa,0,1024);strcpy(fa,"您有新消息,请输入open查看");send(p1->fd,fa,strlen(fa),0);}baocun1();     //保存信息}p1=p1->next;}}//修改个人信息void xgnc(int fd,char id[20]){X *p,*p1;int i;char fa[1024];char shou[1024];memset(fa,0,1024);memset(shou,0,1024);p=head->next;while(p){if(strcmp(p->id,id)==0)break;p=p->next;}sprintf(fa,"我的昵称:%s\n我的账号:%s我的密码:%s",p->name,p->id,p->passwd);send(fd,fa,strlen(fa),0);memset(fa,0,1024);strcpy(fa,"\t1.修改昵称\n\t2.修改密码");send(fd,fa,strlen(fa),0);recv(fd,shou,1024,0);if(strcmp(shou,"1")==0)//输入1修改昵称{memset(fa,0,1024);strcpy(fa,"请输入新的昵称");send(fd,fa,strlen(fa),0);memset(shou,0,1024);recv(fd,shou,1024,0);strcpy(p->name,shou);baocun();return ;}else if(strcmp(shou,"2")==0)//输入2修改密码{memset(fa,0,1024);strcpy(fa,"请输入新的密码");send(fd,fa,strlen(fa),0);memset(shou,0,1024);recv(fd,shou,1024,0);strcpy(p->passwd,shou);baocun();//保存一次return ;}else{memset(fa,0,1024);strcpy(fa,"输入非法");send(fd,fa,strlen(fa),0);return;}}//发送私聊邮件信息void * siliao(int fd,char id[20]){X *p,*p1;int i,leap=0;char fa[1024];char shou[1024];memset(fa,0,1024);memset(shou,0,1024);p=head->next;while(p){if(strcmp(p->id,id)==0)break;p=p->next;}strcpy(fa,"请输入你想私聊的好友账号");send(fd,fa,strlen(fa),0);recv(fd,shou,1024,0);if(strcmp(shou,p->id)==0)//不能与自己聊天{memset(fa,0,1024);strcpy(fa,"不能与自己聊天");send(fd,fa,strlen(fa),0);return;}if(p->hys==0)//判断是否有此好友{memset(fa,0,1024);strcpy(fa,"你没有此好友");send(fd,fa,strlen(fa),0);return;}//printf("phys:%d\n",p->hys);for(i=0;i<p->hys;i++){if(strcmp(p->hy[i],shou)==0){leap=1;p1=head->next;while(p1){if(strcmp(p1->id,shou)==0)break;p1=p1->next;}printf("p fd%d,p1 fd%d\n",p->fd,p1->fd);memset(fa,0,1024);strcpy(fa,"请输入你要发送给他的内容,输入over结束");send(fd,fa,strlen(fa),0);if(p1->fd==-5)//好友不在线提示,但依旧能发送{memset(fa,0,1024);strcpy(fa,"提示:好友不在线,可能无法及时回复");send(fd,fa,strlen(fa),0);}M* m,*p8;int kkk=0;time_t timep;while(1){memset(shou,0,1024);if(recv(fd,shou,1024,0)==-1)return;if(strcmp(shou,"over")==0)break;kkk=1;p8=head1;while(p8->next){p8=p8->next;}m=(M*)malloc(sizeof(M));m->type=2;strcpy(m->id,p1->id);strcpy(m->id1,p->id);memset(fa,0,1024);p8->next=m;m->next=NULL;time(&timep);sprintf(fa,"%s  %s say:%s\n",ctime(&timep),p->id,shou);strcpy(m->mess,fa);//////////////////////////////baocun2(fa,id);printf("%s对%s说%s\n",m->id1,m->id,m->mess);count1++;printf("count1=%d\n",count1);}if(zhzx(p1->id)==1&&kkk==1)//给对方发了信息,通知对方,如果对方不在线,上线再通知{memset(fa,0,1024);strcpy(fa,"您有新消息,请输入open查看");send(p1->fd,fa,strlen(fa),0);}baocun1();   //保存未被接收的信息return;}}memset(fa,0,1024);strcpy(fa,"你没有此好友");send(fd,fa,strlen(fa),0);}int zhzx(char id[20])//验证账号是否在线{X* p;p=head;while(p){if((strcmp(p->id,id)==0)&&p->online==1)return 1;//在线返回1p=p->next;}return 0;}char a[100][20];//放聊天室人的idint len=0;//聊天室人数//创建多人聊天室void duorenliao(int fd,char id[20]){system("play -q 11.wav  repeat 2");   //播放进入聊天室的提示音char fa[1024];char shou[1024];memset(fa,0,1024);memset(shou,0,1024);strcpy(fa,"您已进入聊天室,输入stop退出,输入look查看当前人");send(fd,fa,strlen(fa),0);strcpy(a[len],id);len++;//每进入一个人,长度加1int i;X* p;time_t timep;time(&timep);  //时间函数while(1)//建立聊天室基本信息{memset(shou,0,1024);recv(fd,shou,1024,0);if(strcmp(shou,"stop")==0)//stop退出聊天室{for(i=0;i<len;i++){if(strcmp(a[i],id)==0){while(i<len-1){strcpy(a[i],a[i+1]);i++;}}}len--;for(i=0;i<len;i++){p=head->next;while(p){if(strcmp(p->id,a[i])==0){memset(fa,0,1024);sprintf(fa,"%s退出聊天室",id);send(p->fd,fa,strlen(fa),0);break;}p=p->next;}}return;}if(strcmp(shou,"look")==0)          //look查看聊天室有多少人,并显示出他们的昵称以及账号{memset(fa,0,1024);sprintf(fa,"当前聊天室有%d人,他们是:",len);send(fd,fa,strlen(fa),0);for(i=0;i<len;i++){p=head->next;while(p){if(strcmp(p->id,a[i])==0){memset(fa,0,1024);sprintf(fa,"昵称是%s  账号是%s\n",p->name,p->id);send(fd,fa,strlen(fa),0);break;}p=p->next;}}continue;}for(i=0;i<len;i++)        //发的消息对聊天室里所有人发出{p=head->next;while(p){if(strcmp(p->id,a[i])==0&&strcmp(p->id,id)!=0){memset(fa,0,1024);time(&timep);sprintf(fa,"%s%s say: %s",ctime(&timep),id,shou);send(p->fd,fa,strlen(fa),0);break;}p=p->next;}}}}//在线从服务器查看聊天记录void jilu(int fd,char id[20]){int fp;      //文件标识符char str[1024];sprintf(str,"%s.txt",id); //打开相应人的聊天记录文本文件fp=open(str,O_RDONLY);    //只读方式打开if(fp==-1){memset(str,0,1024);strcpy(str,"您没有聊天记录");send(fd,str,strlen(str),0);return;}int ret;memset(str,0,1024);while(ret=read(fp,str,1024))//ret读的长度,为0退出while{write(fd,str,strlen(str));//读出来发送给客户端memset(str,0,1024);}close(fp);}//客户端从服务器下载聊天文件void xiazai(int fd,char id[20]){int fp;char str[1024];sprintf(str,"%s.txt",id);fp=open(str,O_RDONLY);if(fp==-1)//先判断有没有聊天记录文件{memset(str,0,1024);strcpy(str,"您没有聊天记录");send(fd,str,strlen(str),0);return;}int ret;memset(str,0,1024);strcpy(str,"xiazai");//发送“xiazai”,让客户端准备接收send(fd,str,strlen(str),0);memset(str,0,1024);while(ret=read(fp,str,1024)){send(fd,&ret,sizeof(int),0);//先发送内容字节数,再发送内容send(fd,str,ret,0);memset(str,0,1024);}memset(str,0,1024);strcpy(str,"endend");ret = strlen(str);//结束后发送endend告诉客户端发送完毕send(fd,&ret,sizeof(int),0);send(fd,str,strlen(str),0);close(fp);}//双人聊天室,输入输出分开void doublechat(int fd,char id[20]){X * p;p=head->next;while(p){if(strcmp(p->id,id)==0)break;p=p->next;}p->chatroom=1;char shou[1024];char fa[1024];int ret=0;memset(shou,0,1024);memset(fa,0,1024);strcpy(fa,"chat");send(fd,fa,strlen(fa),0);memset(fa,0,1024);strcpy(fa,"输入你想聊天的好友id");send(fd,fa,strlen(fa),0);recv(fd,shou,1024,0);X* p1;//先判断该人存不存在p1=head->next;while(p1){if(strcmp(p1->id,shou)==0)break;p1=p1->next;}if(p1==NULL){memset(fa,0,1024);strcpy(fa,"此好友不存在");send(fd,fa,strlen(fa),0);p->chatroom=0;return;}if(p1->fd==-5||p1->chatroom==0)//判断好友在不在线和有没有打开聊天窗口{memset(fa,0,1024);strcpy(fa,"好友不在线或没开聊天,请发送私人消息");send(fd,fa,1024,0);p->chatroom=0;return;}memset(fa,0,1024);strcpy(fa,"连接成功");send(fd,fa,1024,0);while(1)//进入聊天,输入over退出{memset(shou,0,1024);recv(fd,shou,1024,0);if(strcmp(shou,"over")==0){p->chatroom=0;memset(fa,0,1024);sprintf(fa,"%s 已退出,请您输入over退出",id);if(p1->chatroom==1)send(p1->fd,fa,1024,0);break;}memset(fa,0,1024);sprintf(fa,"%s say:%s",id,shou);send(p1->fd,fa,1024,0);baocun1();//保存聊天记录}}//并发服务器用线程来完成
void * handleclient(void * newfd){int fd=(int)newfd;          char recvbuf[1024];char recvbuf1[1024];char sendbuf[1024];memset(recvbuf,0,sizeof(recvbuf));memset(recvbuf1,0,sizeof(recvbuf1));memset(sendbuf,0,1024);if(recv(fd,recvbuf,1024,0)==-1){perror("recv");//错误提示return;}else{printf("cli say:%s\n",recvbuf);     //tcp三次握手规则,回复客户端}while(1){memset(sendbuf,0,1024);//登录后界面strcpy(sendbuf,"欢迎使用本服务,可使用功能如下\n\t1.登录帐号\n\t2.注册帐号\n\tend.退出\n");send(fd,sendbuf,strlen(sendbuf),0);memset(recvbuf,0,sizeof(recvbuf));recv(fd,recvbuf,1024,0);if(strcmp(recvbuf,"1")==0)     //接收到客户端输入为1,请输入登录账号{memset(sendbuf,0,1024);strcpy(sendbuf,"请输入登录账号");             //要求客户端输入账号密码if(send(fd,sendbuf,strlen(sendbuf),0)==-1){perror("send");return;}memset(recvbuf,0,sizeof(recvbuf));if(recv(fd,recvbuf,1024,0)==-1){perror("recv");return;}memset(sendbuf,0,sizeof(sendbuf));strcpy(sendbuf,"请输入登录密码");if(send(fd,sendbuf,strlen(sendbuf),0)==-1){perror("send");return;}memset(recvbuf1,0,sizeof(recvbuf1));if(recv(fd,recvbuf1,1024,0)==-1){perror("recv");return;}//匹配账号密码是否都存在且正确if(yzzh(recvbuf)==0||yzmm(recvbuf,recvbuf1)==0){memset(sendbuf,0,1024);strcpy(sendbuf,"输入账号或密码不正确");send(fd,sendbuf,strlen(sendbuf),0);}else if(zhzx(recvbuf)==1){memset(sendbuf,0,1024);strcpy(sendbuf,"此帐号在线");send(fd,sendbuf,strlen(sendbuf),0);}else{strcpy(recvbuf1,recvbuf);memset(sendbuf,0,1024);strcpy(sendbuf,"登陆成功\n");X*p;p=head;while(p->next){if(strcmp(p->next->id,recvbuf1)==0){p->next->online=1;p->next->fd=fd;break;}p=p->next;}send(fd,sendbuf,strlen(sendbuf),0);//登陆时判断是否有新消息,有新消息发送一个提示给客户端M* p9;p9=head1;int le=0;while(p9){if(strcmp(p9->id,recvbuf1)==0){le=1;memset(sendbuf,0,1024);strcpy(sendbuf,"您有新消息,请输入open查看\n");send(fd,sendbuf,strlen(sendbuf),0);break;}p9=p9->next;}//登录后可以使用的功能while(1){memset(sendbuf,0,1024);strcpy(sendbuf,"你可以使用的功能如下\n\t1.查找好友\n\t2.显示好友\n\t3.删除好友\n\t4.添加好友\n\t5.查看或修改个人信息\n\t6.与好友发送私信\n\t7.群聊\n\tchat.与好友聊天窗口(屏幕分区输入输出)\n\t10.在线查看聊天记录\n\t11.从服务器下载聊天记录\n\t8.退出\n\topen.查看消息");send(fd,sendbuf,strlen(sendbuf),0);memset(recvbuf,0,sizeof(recvbuf));if(recv(fd,recvbuf,1024,0)==-1){perror("recv");return;}if(strcmp(recvbuf,"1")==0)//查找好友{look(fd,recvbuf1);}else if(strcmp(recvbuf,"2")==0)//显示好友列表{display(fd,recvbuf1);}else if(strcmp(recvbuf,"3")==0)//删除好友{del(fd,recvbuf1);}else if(strcmp(recvbuf,"4")==0)//添加好友{addhy(fd,recvbuf1);}else if(strcmp(recvbuf,"5")==0)//查看或修改个人信息{xgnc(fd,recvbuf1);}else if(strcmp(recvbuf,"6")==0)//与好友发送私聊信息{siliao(fd,recvbuf1);}else if(strcmp(recvbuf,"7")==0)//进入多人聊天室{duorenliao(fd,recvbuf1);}else if(strcmp(recvbuf,"8")==0)//退出,并把在线信息置零,套接字值置为-5{p=head;while(p->next){if(strcmp(p->next->id,recvbuf1)==0){p->next->online=0;p->next->fd=-5;break;}p=p->next;}break;}else if(strcmp(recvbuf,"chat")==0) //双人实时聊天,{doublechat(fd,recvbuf1);}else if(strcmp(recvbuf,"10")==0)//发送聊天记录给客户端{jilu(fd,recvbuf1);}else if(strcmp(recvbuf,"11")==0)//客户端接受聊天记录文件{xiazai(fd,recvbuf1);}else if(strcmp(recvbuf,"open")==0)//打开消息{char fa[1024];char shou[1024];M* m;int lea=0;m=head1->next;printf("count1=%d\n",count1);while(m){if(strcmp(m->id,recvbuf1)==0){lea=1;//标识符,提示缓存消息列表里有没有此人消息if(m->type==1)//消息类型,1为好友请求消息{memset(fa,0,1024);strcpy(fa,m->mess);send(fd,fa,strlen(fa),0);while(1){memset(shou,0,1024);recv(fd,shou,1024,0);if(strcmp(shou,"y")==0)  //如果同意加为好友,并告诉对方{m->type=3;strcpy(m->id,m->id1);strcpy(m->id1,recvbuf1);memset(fa,0,1024);sprintf(fa,"%s已同意添加,添加成功",recvbuf1);strcpy(m->mess,fa);X* p,*p1;p=head->next;p1=head->next;while(p){if(strcmp(p->id,m->id)==0)break;p=p->next;}while(p1){if(strcmp(p1->id,m->id1)==0)break;p1=p1->next;}strcpy(p->hy[p->hys],p1->id);p->hys++;strcpy(p1->hy[p1->hys],p->id);p1->hys++;if(zhzx(m->id)==1){memset(fa,0,1024);strcpy(fa,"您有新消息,请输入open查看");send(p->fd,fa,strlen(fa),0);}baocun();baocun1();break;}else if(strcmp(shou,"n")==0)   //不同意并告诉对方{X* p;p=head->next;while(p){if(strcmp(p->id,m->id1)==0)break;p=p->next;}m->type=3;strcpy(m->id,m->id1);strcpy(m->id1,recvbuf1);memset(fa,0,1024);sprintf(fa,"%s已拒绝添加请求,添加失败",recvbuf1);strcpy(m->mess,fa);if(zhzx(m->id)==1){memset(fa,0,1024);strcpy(fa,"您有新消息,请输入open查看");send(p->fd,fa,strlen(fa),0);}baocun1();break;}}}else if(m->type==3)//处理普通消息,看完消息,链表中删除{lea=1;memset(fa,0,1024);strcpy(fa,m->mess);send(fd,fa,strlen(fa),0);M*m1,*m2;m1=head1;while(m1->next){if(strcmp(m1->next->id,recvbuf1)==0){m2=m1->next;m1->next=m2->next;free(m2);break;}m1=m1->next;}baocun1();count1--;if(m==NULL);break;}else if(m->type==2)//消息类型2,聊天消息{   lea=1;M* p7;p7=head1->next;while(p7){int ppp=0;   //标识符,有没有此人消息if((strcmp(p7->id,recvbuf1)==0)&&(p7->type==2)){memset(fa,0,1024);strcpy(fa,p7->mess);send(fd,fa,strlen(fa),0);///////////////////baocun2(fa,recvbuf1);M *m1,*m2;m1=head1;while(m1->next){if((strcmp(m1->next->id,recvbuf1)==0)&&(m1->next->type==2)){m2=m1->next;m1->next=m2->next;free(m2);ppp=1;break;}m1=m1->next;}}if(ppp==1){count1--;baocun1();p7=head1->next;continue;}p7=p7->next;}}}if(m==NULL)break;m=m->next;}if(lea==0){memset(fa,0,1024);strcpy(fa,"您没有任何未处理消息");send(fd,fa,strlen(fa),0);}}}}}else if(strcmp(recvbuf,"2")==0){add(fd);}else if(strcmp(recvbuf,"end")==0){memset(sendbuf,0,1024);strcpy(sendbuf,"欢迎使用,谢谢");send(fd,sendbuf,strlen(sendbuf),0);break;}else {continue;}}close(fd);}//判断客户端是否依旧在线void * panduan(void){X* p;char fa[1024];p=head;while(1)         //判断是否断开连接,如果断开将此账号置为不在线状态,监听套接字-5{p=head->next;while(p){if(p->online==1){struct tcp_info info;int len=sizeof(info);         getsockopt(p->fd,IPPROTO_TCP,TCP_INFO,&info,(socklen_t*)&len);if((info.tcpi_state!=TCP_ESTABLISHED)){p->online=0;p->fd=-5;}}p=p->next;}}}int main(){int sockfd=0,newfd=0;    //定义并初始化int chlid=0;int ret=0;int len=sizeof(struct sockaddr);struct sockaddr_in myaddr;struct sockaddr_in otheraddr;memset(&myaddr,0,len);memset(&otheraddr,0,len);//tcp套接字连接/*if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){perror("sockfd");return -1;}else {printf("socket success...%d\n",sockfd);}*///实现端口复用int reuse = 1;    sockfd = socket(AF_INET, SOCK_STREAM,0);    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)    {        perror("setsockopet error\n");        return -1;    }//初始化结构体myaddr.sin_family = AF_INET;myaddr.sin_port = htons(8889);myaddr.sin_addr.s_addr=inet_addr("192.168.194.129");//myaddr.sin_addr.s_addr=INADDR_ANY;,可以自动获取本机ip地址//绑定套接字if(-1==(bind(sockfd,(struct sockaddr*)&myaddr,len))){perror("bind");         //错误提示return -1;}else{printf("bind success...\n");}//开始侦听if((listen(sockfd,10))==-1){perror("listen");return -1;}else{printf("listen success...\n");}printf("server waiting...\n");pthread_t id1,id2;head=create();         //创建账号信息的头结点head1=create1();//创建缓存信息的头结点while(1){if((newfd=accept(sockfd,(struct sockaddr*)&otheraddr,&len))==-1){perror("accept");return -1;}else//连接成功输出客户端等信息{printf("accept success...client's fd=%d,sever's fd=%d\n",newfd,sockfd);printf("--client ip=%s,port=%d\n",inet_ntoa(otheraddr.sin_addr),ntohs(otheraddr.sin_port));}//线程客户端pthread_create(&id1,NULL,handleclient,(void*)newfd);//判断客户端是否依旧在线pthread_create(&id2,NULL,(void*)panduan,NULL);pthread_detach(id1);//分离线程,线程结束后资源系统自动回收pthread_detach(id2);}close(sockfd);return 0;}


谢谢




原创粉丝点击