您的位置:首页 > 理论基础 > 计算机网络

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

2017-08-18 17:14 197 查看


总体设计

本聊天室系统采用了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);
else
printf("%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; //控制线程shou1
leap1=1; //控制线程fa
memset(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);
}
}
}
else
puts(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]; //每个账号唯一id
char passwd[20]; //账号密码
char name[50]; //账号昵称
char hy[100][20]; //好友列表,最大100个
int hys; //好友数量
int online; //0不在线,1在线
int fd; //存放客户端成功连接后accept产生的新的套接字,不在线为-5
int chatroom; //存放是否打开了双人聊天室,打开为1,没打开为0
struct xinxi *next; //下一条链表的首地址
}X;

typedef struct message //结构体message,别名M
{
char id[20]; //收信息的人
char id1[20]; //发信息的人
int type; //信息类型,好友请求1,私聊信息2,好友请求回复信息3
char 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"); //保存在当前运行服务器文件下123
if(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"); //保存在当前运行服务器文件下1234
if(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; //每次服务器新连接字置-5
p->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,不是返回0
int 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++; //全局变量账号数量+1
baocun(); //保存一次账号信息在文件
}
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; //在线返回1
p=p->next;
}
return 0;
}
char a[100][20]; //放聊天室人的id
int 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++; //每进入一个人,长度加1
int 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;
}


谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息