这两天写了聊天室,基于Linux下TCP/IP协议局域网聊天,可实现聊天、上下线提醒、在线列表、文件传输。
服务器 server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#define SERV_PORT 9527 //服务器端口号
/**有关变量**/
pthread_mutex_t mutex; //定义锁
/*用来保存客户端发来的消息*/
typedef struct data_buff
{
int status; //0:注册 1:上线 2:下线 3:消息
char sendname[10];//发送者登录名 sendname给othername
char send_pass[10];//发送者密码
char recvname[10];//接受者登录名,通过name寻找cfd再发送消息
int dat_type; //消息类型;0:普通消息 1:文件
char file_name[20];//文件名
int file_size;//文件大小
char data[BUFSIZ];//BUFSIZ=8192;
}DATBUFF;
/*保存已经连上服务器的客户端信息,并且通过cfd给指定的客户端发送消息*/
typedef struct socbuf
{
char cliname[10];//客户端登陆的名字
//char cli_IP[16];//保存客户端IP
int soc_cfd; //连接成功的客户端套接字
struct socbuf *next;
}SOCBUF;
SOCBUF *Shead=NULL;
/*将注册的用户信息保存到文件中*/
typedef struct user
{
char user_name[10];//用户名
char password[10];//密码
struct user *next;
}USER;
//USER *Uhead=NULL;
/**上下线时服务器要发送给客户端的信息**/
typedef struct server
{
int type;//0:上线 1:下线 2:消息
char othername[10];//
int datatype;//0:消息 1:文件
char file_name[20];//文件名
int file_size;//文件大小
char otherbuff[BUFSIZ];
}SERBUF;
struct s_info {
struct sockaddr_in cliaddr;
int connfd;
};
/*注册新用户*/
void Login_user(DATBUFF buff,int cfd)
{
/***
typedef struct user
{
char user_name[10];//用户名
char password[10];//密码
struct user *next;
}USER;
****/
int ret;
USER *Uhead=NULL;
USER *p1=Uhead;
FILE *fp;
/*****从文件读取用户信息*****/
if((fp=fopen("cli.dat","rb"))==NULL)
{
perror("Login_user fopen1 cli.dat");
exit(1);
}
while(!feof(fp)) //将所有用户信息读给链表
{
USER *p2=(USER *)malloc(sizeof(USER));
if(fread(p2,sizeof(USER),1,fp)==1)
{
if(Uhead==NULL)
{
Uhead=p2;
p2->next=NULL;
p1=p2;
}
else
{
p1->next=p2;
p1=p2;
p2->next=NULL;
}
}
}
fclose(fp);
USER *p2=Uhead;
USER *p3=Uhead;
/*******添加新的注册用户************/
if(Uhead==NULL)
{
/*该客户端用户没注册过*/
/*p1指向最后一个注册用户,这时接着p1后添加用户*/
USER *p4=(USER *)malloc(sizeof(USER));
strcpy(p4->user_name,buff.sendname);
strcpy(p4->password,buff.send_pass);
Uhead=p4;
p4->next=NULL;
p1=p4;
char str2[]="注册成功\n";
ret=write(cfd,str2,sizeof(str2));
//printf("ret:%d\n",ret);
}
else
{
while(strcmp(p2->user_name,buff.sendname)!=0&&p2->next!=NULL)
{
p3=p2;
p2=p2->next;
}
if(strcmp(p2->user_name,buff.sendname)==0)
{
/*该客户端用户注册过*/
char str1[]="该用户名被注册\n";
write(cfd,str1,sizeof(str1));
}
else
{
p1=Uhead;
while(p1->next!=NULL)
{
p1=p1->next;
}
/*该客户端用户没注册过*/
USER *p4=(USER *)malloc(sizeof(USER));
strcpy(p4->user_name,buff.sendname);
strcpy(p4->password,buff.send_pass);
p1->next=p4;
p1=p4;
p4->next=NULL;
char str2[]="注册成功\n";
ret=write(cfd,str2,sizeof(str2));
}
}
/*****将新的用户信息更新到文件中*******/
if((fp=fopen("cli.dat","wb"))==NULL)
{
perror("Login_user fopen1 cli.dat");
exit(1);
}
p1=Uhead;
while(Uhead!=NULL && p1!=NULL)
{
if(fwrite(p1,sizeof(USER),1,fp)==1)
{
p1=p1->next;
}
else
{
perror("fwrite");
}
}
/***释放内存***/
p1=Uhead;
while(Uhead!=NULL)
{
Uhead=p1->next;
free(p1);
p1=Uhead;
}
fclose(fp);
}
/*上线通知*/
int Online_inform(DATBUFF buff,int cfd)
{
/***将上线的客户端登录名和cfd保存在
typedef struct socbuf
{
char cliname[10];//客户端登陆的名字
//char cli_IP[16];//保存客户端IP
int soc_cfd; //连接成功的客户端套接字
struct socbuf *next;
}SOCBUF;
SOCBUF *Shead=NULL;
***/
int ret;
USER *Uhead=NULL;
USER *p1=Uhead;
FILE *fp;
/*****从文件读取用户信息*****/
if((fp=fopen("cli.dat","rb"))==NULL)
{
perror("Login_user fopen1 cli.dat");
exit(1);
}
while(!feof(fp)) //将所有用户信息读给链表
{
USER *p2=(USER *)malloc(sizeof(USER));
if(fread(p2,sizeof(USER),1,fp)==1)
{
if(Uhead==NULL)
{
Uhead=p2;
p2->next=NULL;
p1=p2;
}
else
{
p1->next=p2;
p1=p2;
p2->next=NULL;
}
}
}
USER *p2=Uhead;
USER *p3=Uhead;
/*******判断登录用户是否注册过************/
if(Uhead==NULL)
{
char str2[]="没该用户!\n";
ret=write(cfd,str2,sizeof(str2));
fclose(fp);
/***释放内存***/
p1=Uhead;
while(Uhead!=NULL)
{
Uhead=p1->next;
free(p1);
p1=Uhead;
}
return 1;
}
else
{
while(strcmp(p2->user_name,buff.sendname)!=0&&p2->next!=NULL)
{
p3=p2;
p2=p2->next;
}
if(strcmp(p2->user_name,buff.sendname)==0)
{
}
else
{
char str2[]="没该用户!\n";
ret=write(cfd,str2,sizeof(str2));
fclose(fp);
/***释放内存***/
p1=Uhead;
while(Uhead!=NULL)
{
Uhead=p1->next;
free(p1);
p1=Uhead;
}
return 1;
}
}
fclose(fp);
/***释放内存***/
p1=Uhead;
while(Uhead!=NULL)
{
Uhead=p1->next;
free(p1);
p1=Uhead;
}
/*申请空间存储新上线的客户端信息;用户名和cfd*/
SOCBUF *ps2=(SOCBUF *)malloc(sizeof(SOCBUF));
strcpy(ps2->cliname,buff.sendname);
ps2->soc_cfd=cfd;
SOCBUF *ps1=Shead;
if(Shead == NULL)
{
Shead=ps2;
ps2->next=NULL;
ps1=ps2;
char str1[]="登录成功\n";
write(cfd,str1,sizeof(str1));
}
else
{
while(ps1->next!=NULL)
{
ps1=ps1->next;
}
ps1->next=ps2;
ps1=ps2;
ps2->next=NULL;
char str2[]="登录成功\n";
write(cfd,str2,sizeof(str2));
}
/**告诉其他客户端有新的人上线**/
SERBUF serbuff;
bzero(&serbuff,sizeof(serbuff));
ps1=Shead;
while(ps1!=NULL)
{
if(strcmp(ps1->cliname,buff.sendname)!=0)
{
serbuff.type=0;//上线
strcpy(serbuff.othername,buff.sendname);
serbuff.datatype=0;//消息
sprintf(serbuff.otherbuff,"%s%s\n",buff.sendname,"上线了!");//某某上线了
write(ps1->soc_cfd,&serbuff,sizeof(serbuff));
}
ps1=ps1->next;
}
/**给该客户端发送已经上线的成员**/
ps1=Shead;
while(ps1!=NULL)
{
serbuff.type=0;//上线
strcpy(serbuff.othername,ps1->cliname);//ps1所指向的客户端名字
serbuff.datatype=0;//消息
sprintf(serbuff.otherbuff,"%s%s\n",ps1->cliname,"在线");//某某上线了
write(cfd,&serbuff,sizeof(serbuff));
ps1=ps1->next;
}
return 0;
}
/**下线通知**/
void Downline_inform(DATBUFF buff,int cfd)
{
SOCBUF *ps3=Shead;
SOCBUF *ps2=Shead;
SOCBUF *ps1=Shead;
SERBUF serbuff;
bzero(&serbuff,sizeof(serbuff));
if(Shead==NULL)
{
write(cfd,"\t下线失败1\n",strlen("\t下线失败1\n"));
}
else
{
while(strcmp(ps1->cliname,buff.sendname)!=0&&ps1->next!=NULL)
{
ps2=ps1;
ps1=ps1->next;
}
if(strcmp(ps1->cliname,buff.sendname)==0)
{
if(ps1==Shead)
{
Shead=ps1->next;
free(ps1);
ps3=Shead;
while(ps3!=NULL)
{
serbuff.type=1;//下线
strcpy(serbuff.othername,buff.sendname);
serbuff.datatype=0;//消息
sprintf(serbuff.otherbuff,"%s%s\n",buff.sendname,"下线了!");//某某下线了
write(ps3->soc_cfd,&serbuff,sizeof(serbuff));
ps3=ps3->next;
}
}
else
{
ps2->next = ps1->next;
free(ps1);
ps3=Shead;
while(ps3!=NULL)
{
serbuff.type=1;//下线
strcpy(serbuff.othername,buff.sendname);
serbuff.datatype=0;//消息
sprintf(serbuff.otherbuff,"%s%s\n",buff.sendname,"下线了!");//某某下线了
write(ps3->soc_cfd,&serbuff,sizeof(serbuff));
ps3=ps3->next;
}
}
}
else
{
write(cfd,"\t下线失败2\n",strlen("\t下线失败2\n"));
}
}
}
/**服务器发送文件**/
void ser_send_file(DATBUFF buff,int cfd)
{
//read(cfd,&buff,sizeof(buff));
SOCBUF *ps2=Shead;
SOCBUF *ps1=Shead;
int filenum1=0;
int filenum2=0;
char ackflag[10];
int i;
int scfd;
while(strcmp(ps1->cliname,buff.recvname)!=0&&ps1->next!=NULL)
{
ps2=ps1;
ps1=ps1->next;
}
if(strcmp(ps1->cliname,buff.recvname)==0)
{
scfd=ps1->soc_cfd;
}
else
{
write(cfd,"对方离线,发送失败!\n",strlen("对方离线,发送失败!\n"));
}
SERBUF serbuff;//用来存储将要发送的文件信息
bzero(&serbuff,sizeof(serbuff));
filenum1=buff.file_size/BUFSIZ;
filenum2=buff.file_size%BUFSIZ;
/*设置type*/
serbuff.type=2;
/*设置发送者*/
strcpy(serbuff.othername,buff.sendname);
/*设置文件类型*/
serbuff.datatype=1;
/*设置文件名*/
strcpy(serbuff.file_name,buff.file_name);
/*设置文件大小*/
serbuff.file_size=buff.file_size;
/*先向客户端发送文件信息,后再发送内容*/
write(scfd,&serbuff,sizeof(serbuff));
//等待客户端应答
while(1)
{
read(scfd,ackflag,sizeof(ackflag));
if(strstr(ackflag,"ACK")!=NULL)
{
break;
}
}
char ackbuf[BUFSIZ];
int realen=0;
if(filenum1==0)//小于BUFSIZ
{
bzero(buff.data,sizeof(buff.data));
bzero(serbuff.otherbuff,sizeof(serbuff.otherbuff));
realen=read(cfd,buff.data,sizeof(buff.data));
if(1)
{
write(cfd,buff.data,realen);
}
strcpy(serbuff.otherbuff,buff.data);
write(scfd,serbuff.otherbuff,realen);
/*********************/
while(1)
{
read(scfd,ackbuf,realen);
if(strcmp(ackbuf,serbuff.otherbuff)==0)
{
break;
}
}
}
else if((filenum1>0)&&(filenum2==0))//BUFSIZ整数倍
{
for(i=0;i<filenum1;i++)
{
bzero(buff.data,sizeof(buff.data));
bzero(serbuff.otherbuff,sizeof(serbuff.otherbuff));
realen=read(cfd,buff.data,sizeof(buff.data));
strcpy(serbuff.otherbuff,buff.data);
write(scfd,serbuff.otherbuff,realen);
}
}
else if((filenum1>0)&&(filenum2!=0))//BUFSIZ整数倍+多余部分
{
for(i=0;i<filenum1+1;i++)
{
bzero(buff.data,sizeof(buff.data));
bzero(serbuff.otherbuff,sizeof(serbuff.otherbuff));
realen=read(cfd,buff.data,sizeof(buff.data));
if(1)
{
write(cfd,buff.data,realen);
}
strcpy(serbuff.otherbuff,buff.data);
write(scfd,serbuff.otherbuff,realen);
/*********************/
while(1)
{
read(scfd,ackbuf,realen);
if(strcmp(ackbuf,serbuff.otherbuff)==0)
{
break;
}
}
}
}
}
/**消息通知**/
void News_inform(DATBUFF buff,int cfd)
{
SOCBUF *ps2=Shead;
SOCBUF *ps1=Shead;
SERBUF serbuff;
bzero(&serbuff,sizeof(serbuff));
switch(buff.dat_type)
{
/**普通消息**/
case 0:
{
while(strcmp(ps1->cliname,buff.recvname)!=0&&ps1->next!=NULL)
{
ps2=ps1;
ps1=ps1->next;
}
if(strcmp(ps1->cliname,buff.recvname)==0)
{
serbuff.type=2;//消息
strcpy(serbuff.othername,buff.sendname);
serbuff.datatype=0;//消息
sprintf(serbuff.otherbuff,"%s:%s\n",buff.sendname,buff.data);//
write(ps1->soc_cfd,&serbuff,sizeof(serbuff));
//write(cfd,"对方离线,发送失败!",strlen("对方离线,发送失败!"));
}
else
{
write(cfd,"对方离线,发送失败!\n",strlen("对方离线,发送失败!\n"));
}
}
break;
/**文件处理**/
case 1:
{
ser_send_file(buff,cfd);
}
break;
default:
break;
}
}
/*线程处理函数*/
void *manage_clie(void *argc)
{
//struct s_info *ts = (struct s_info*)arg;
//int cfd=ts->connfd;
int cfd=(int)argc;
DATBUFF buff; //保存客户端发来的信息
fd_set recfd;
int ret,readlen;
/*监听客户端cfd是否有消息发过来*/
while(1)
{
FD_ZERO(&recfd); //每次循环都要清空集合,否则不能检测描述符变化
FD_SET(cfd,&recfd);
ret=select(cfd+1,&recfd,NULL,NULL,NULL);
pthread_mutex_lock(&mutex);//加锁
if(ret < 0)
{
perror("pthread select");
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);
}
if(FD_ISSET(cfd,&recfd)) //客户端有信息过来了
{
bzero(&buff,sizeof(buff)); //清空结构体
readlen=read(cfd,&buff,sizeof(buff));
if(readlen==0&&ret==1)
{
printf("客户端与服务器断开连接!\n");
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);
}
/*处理客户端信息*/
switch(buff.status)
{
/*注册新用户*/
case 0:
{
Login_user(buff,cfd);
close(cfd);
pthread_mutex_unlock(&mutex);//解锁
//每次客户端下线要"解锁"
pthread_exit(NULL);
}
break;
/*上线通知*/
case 1:
{
ret=Online_inform(buff,cfd);
if(ret==1)
{
close(cfd);
pthread_mutex_unlock(&mutex);//解锁
//每次客户端下线要"解锁"
pthread_exit(NULL);
}
}
break;
/*下线通知*/
case 2:
{
Downline_inform(buff,cfd);
close(cfd);
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);
}
break;
/*消息通知*/
case 3:
{
News_inform(buff,cfd);
}
break;
default:
break;
}
}
pthread_mutex_unlock(&mutex);//解锁
}
}
/*主函数*/
int main(int argc,char **argv)
{
int sfd, cfd;
struct sockaddr_in serv_addr, clie_addr;
struct s_info ts;
char cli_IP[16];
socklen_t clie_addr_len;
fd_set readfd;
int ret;
pthread_t tid;
//互斥锁动态初始化
pthread_mutex_init(&mutex,NULL);
/*创建套接字*/
sfd = socket(AF_INET, SOCK_STREAM, 0);
/*初始化一个地址结构*/
bzero(&serv_addr, sizeof(serv_addr)); //将整个结构体清零
serv_addr.sin_family = AF_INET; //选择协议IPv4
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有IP地址
serv_addr.sin_port = htons(SERV_PORT); //绑定端口号
/*绑定服务器地址结构*/
bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
/*设定监听数量*/
listen(sfd, 64);
printf("等待客户端连接.......\n");
while(1)
{
FD_ZERO(&readfd); //每次循环都要清空集合,否则不能检测描述符变化
FD_SET(sfd,&readfd);
ret=select(sfd+1,&readfd,NULL,NULL,NULL);
if(ret < 0)
{
perror("select err");
exit(1);
}
if(FD_ISSET(sfd,&readfd)) //有新的客户端连接请求
{
bzero(&clie_addr,sizeof(clie_addr));
clie_addr_len=sizeof(clie_addr);
/*监听客户端连接*/
cfd=accept(sfd,(struct sockaddr *)&clie_addr,&clie_addr_len);
printf("新客户端IP %s 端口号 %d\n",
inet_ntop(AF_INET, &clie_addr.sin_addr, cli_IP, sizeof(cli_IP)),
ntohs(clie_addr.sin_port));
//ts.clie_addr = clie_addr;
//ts.connfd = cfd;
// pthread_create(&tid, NULL, manage_clie, (void*)&ts);
/*创建线程管理客户端,将客户端套接字传入线程*/
pthread_create(&tid, NULL,manage_clie,(void*)cfd);
pthread_detach(tid); //线程分离
}
}
//pthread_mutex_lock(&mutex);//加锁
//pthread_mutex_unlock(&mutex);//解锁
pthread_mutex_destroy(&mutex);//销锁
/*关闭链接*/
close(sfd);
//close(cfd);
return 0;
}
客户端 client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#define SERV_IP "192.168.1.108"
#define SERV_PORT 9527
pthread_mutex_t mutex; //定义锁
char uname[10];
/*用来保存要发送的内容(文件)*/
typedef struct data_buff
{
int status; //0:注册 1:上线 2:下线 3:消息
char sendname[10];//发送者登录名
char send_pass[10];//发送者密码
char recvname[10];//接受者登录名,
int dat_type; //消息类型;0:普通消息 1:文件
char file_name[20];//文件名
int file_size;//文件大小
char data[BUFSIZ];//BUFSIZ=8192;
}DATBUFF;
/*保存已经连上服务器的客户端的信息*/
typedef struct socbuf
{
char cliname[10];//客户端登陆的名字
//int soc_cfd; //连接成功的客户端套接字
struct socbuf *next;
}SOCBUF;
SOCBUF *Shead=NULL;
/**从服务器获取的信息**/
typedef struct server
{
int type;//0:上线 1:下线 2:消息
char othername[10];//谁上下线了
int datatype;//0:消息 1:文件
char file_name[20];//文件名
int file_size;//文件大小
char otherbuff[BUFSIZ];
}SERBUF;
/**Login_user**/
void *Login_user(void *argc)
{
//int sfd=(int)argc;
int ret;
int sfd;
char ack[100];
struct sockaddr_in serv_addr;
/*创建一个socket*/
sfd = socket(AF_INET, SOCK_STREAM, 0);
/*初始化一个地址结构:*/
bzero(&serv_addr, sizeof(serv_addr)); //清零
serv_addr.sin_family = AF_INET; //IPv4
inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); //指定IP字符串类型转换为网络字节序
serv_addr.sin_port = htons(SERV_PORT);
ret=connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(ret!=0)
{
printf("连接失败!\n");
exit(1);
}
DATBUFF buf;
//printf("测试\n");
printf("注册用户名:");
scanf("%s",buf.sendname);
printf("注册密码:");
scanf("%s",buf.send_pass);
buf.status=0;
write(sfd,&buf,sizeof(buf));
while(1)
{
ret=read(sfd,ack,sizeof(ack));
if(ret>0)
{
printf("%s",ack);
close(sfd);
pthread_exit(NULL);
}
}
pthread_exit(NULL);
}
/**保存上线的成员**/
void Online_inform(SERBUF sbuf)
{
SOCBUF *p2=(SOCBUF *)malloc(sizeof(SOCBUF));
strcpy(p2->cliname,sbuf.othername);
SOCBUF *p1=Shead;
if(Shead==NULL)
{
Shead=p2;
p2->next=NULL;
p1=p2;
}
else
{
while(p1->next!=NULL)
{
p1=p1->next;
}
p1->next=p2;
p1=p2;
p2->next=NULL;
}
}
/**删除下线的成员**/
void Downline_inform(SERBUF sbuf)
{
SOCBUF *p3=Shead;
SOCBUF *p2=Shead;
SOCBUF *p1=Shead;
if(Shead==NULL)
{
}
else
{
while(strcmp(p1->cliname,sbuf.othername)!=0&&p1->next!=NULL)
{
p2=p1;
p1=p1->next;
}
if(strcmp(p1->cliname,sbuf.othername)==0)
{
if(p1==Shead)
{
Shead=p1->next;
free(p1);
}
else
{
p2->next=p1->next;
free(p1);
}
}
else
{
}
}
}
/**从服务器接收文件**/
void Receive_files(SERBUF sbuf,int sfd)
{
int filenum1=0;
int filenum2=0;
int writelen=0;
int realen=0;
int sum=0;
float percent=0;
int i;
/*
char file_name[20];//文件名
int file_size;//文件大小
char otherbuff[BUFSIZ];
*/
printf("正在接收%s发来的文件....\n",sbuf.othername);
int fd=open(sbuf.file_name,O_WRONLY | O_CREAT | O_TRUNC | O_APPEND);
if(fd<0)
{
perror("Receive_files open");
return;
}
filenum1=sbuf.file_size/BUFSIZ;
filenum2=sbuf.file_size%BUFSIZ;
//发送应答
write(sfd,"ACK",strlen("ACK"));
if(filenum1==0)//小于BUFSIZ
{
realen=read(sfd,sbuf.otherbuff,sizeof(sbuf.otherbuff));
writelen=write(fd,sbuf.otherbuff,realen);
bzero(sbuf.otherbuff,sizeof(sbuf.otherbuff));
sum=sum+writelen;
percent=(float)1.0*sum/sbuf.file_size;
percent=percent*100;
printf("完成度:%d/%d %0.2f%c\n",sum,sbuf.file_size,percent,'%');
}
else if((filenum1>0)&&(filenum2==0))//BUFSIZ整数倍
{
for(i=0;i<filenum1;i++)
{
realen=read(sfd,sbuf.otherbuff,sizeof(sbuf.otherbuff));
writelen=write(fd,sbuf.otherbuff,realen);
bzero(sbuf.otherbuff,sizeof(sbuf.otherbuff));
sum=sum+writelen;
percent=(float)1.0*sum/sbuf.file_size;
percent=percent*100;
printf("完成度:%d/%d %0.2f%c\n",sum,sbuf.file_size,percent,'%');
}
}
else if((filenum1>0)&&(filenum2!=0))//BUFSIZ整数倍+多余部分
{
for(i=0;i<filenum1+1;i++)
{
realen=read(sfd,sbuf.otherbuff,sizeof(sbuf.otherbuff));
writelen=write(fd,sbuf.otherbuff,realen);
bzero(sbuf.otherbuff,sizeof(sbuf.otherbuff));
sum=sum+writelen;
percent=(float)1.0*sum/sbuf.file_size;
percent=percent*100;
printf("完成度:%d/%d %0.2f%c\n",sum,sbuf.file_size,percent,'%');
}
}
printf("接收完成!\n");
close(fd);
}
/**读取服务器**/
void *Read_news(void *argc)
{
int sfd=(int)argc;
SERBUF sbuf;
fd_set recfd;
int ret,readlen;
while(1)
{
FD_ZERO(&recfd); //每次循环都要清空集合,否则不能检测描述符变化
FD_SET(sfd,&recfd);
ret=select(sfd+1,&recfd,NULL,NULL,NULL);
pthread_mutex_lock(&mutex);//加锁
if(ret < 0)
{
perror("clie select");
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);
}
if(FD_ISSET(sfd,&recfd))//有信息过来了
{
bzero(&sbuf,sizeof(sbuf)); //清空结构体
readlen=read(sfd,&sbuf,sizeof(sbuf));
if(readlen==0&&ret==1)
{
printf("服务器与客户端断开连接!\n");
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);
}
/**处理服务器来的信息**/
switch(sbuf.type)
{
/*上线通知*/
case 0:
{
printf("%s",sbuf.otherbuff);
Online_inform(sbuf);
}
break;
/*下线通知 */
case 1:
{
printf("%s",sbuf.otherbuff);
Downline_inform(sbuf);
}
break;
/*消息通知 */
case 2:
{
/*这里还要区分普通消息还是文件*/
if(sbuf.datatype==0)
{
printf("%s",sbuf.otherbuff);
}
else if(sbuf.datatype==1)
{
Receive_files(sbuf,sfd);
}
}
break;
default:
break;
}
}
pthread_mutex_unlock(&mutex);//解锁
}
}
/**发送消息**/
void Send_news(int sfd)
{
printf("*当前在线成员:\n");
int i=0;
SOCBUF *p1=Shead;
while(p1!=NULL)
{
i++;
printf(" %d)%s\n",i,p1->cliname);
p1=p1->next;
}
DATBUFF sendbuf;
printf("接收者:");
scanf("%s",sendbuf.recvname);
printf("内容:");
scanf("%s",sendbuf.data);
//gets(sendbuf.data);
//fgets(sendbuf.data,BUFSIZ,stdin);
sendbuf.status=3;
strcpy(sendbuf.sendname,uname);
sendbuf.dat_type=0;//普通消息
write(sfd,&sendbuf,sizeof(sendbuf));
printf("\t*****请按<Enter>输入消息*****\n");
}
/**退出客户端**/
void Quit_user(int sfd)
{
DATBUFF sbuf;
sbuf.status=2;
strcpy(sbuf.sendname,uname);
write(sfd,&sbuf,sizeof(sbuf));
}
/**发送文件**/
void Send_files(int sfd)
{
printf("*当前在线成员:\n");
int i=0;
SOCBUF *p1=Shead;
int sum=0;
while(p1!=NULL)
{
i++;
printf(" %d)%s\n",i,p1->cliname);
p1=p1->next;
}
int fd,ret,filelen;
DATBUFF file_buf;
char Revname[10];//接收者名字
char path[20];
char filename[20];
char pathfile[40];
printf("接收者:");
scanf("%s",Revname);
printf("路径:");
scanf("%s",path);
printf("文件名:");
scanf("%s",filename);
/*路径文件拼接*/
sprintf(pathfile,"%s%s",path,filename);
/*打开文件*/
fd=open(pathfile,O_RDONLY);
if(fd<0)
{
perror("Send_files open");
return;
}
struct stat buf_st;
ret=stat(pathfile,&buf_st);
if(ret < 0)
{
perror("Send_files stat");
return;
}
/**获取文件大小**/
filelen=buf_st.st_size;
file_buf.status=3;//消息类型
strcpy(file_buf.sendname,uname);//发送者
strcpy(file_buf.recvname,Revname);//接收者
file_buf.dat_type=1; //文件
strcpy(file_buf.file_name,filename);//文件名
file_buf.file_size=filelen;//大小
/*先不发内容,先发送文件信息,后再发送内容*/
write(sfd,&file_buf,sizeof(file_buf));
int readlen;
int writnum;
printf("正在发送....\n");
while(1)
{
readlen=read(fd,file_buf.data,BUFSIZ);//文件内容
writnum=write(sfd,file_buf.data,readlen);
bzero(file_buf.data,sizeof(file_buf.data));
//printf("readlen:%d\n",readlen);
//printf("writnum:%d\n",writnum);
//sum=sum+writnum;
if(readlen!=BUFSIZ)
{
printf("SUM:%d\n",sum);
break;
}
}
close(fd);
printf("发送完成!\n");
printf("\t*****请按<Enter>输入消息*****\n");
}
/**发送服务器**/
void *Write_news(void *argc)
{
int sfd=(int)argc;
int key,ret;
int choose;
char ch;
fd_set writefd;
key = open("/dev/tty",O_RDONLY | O_NONBLOCK);
while(1)
{
FD_ZERO(&writefd);
FD_SET(key,&writefd);
ret=select(key+1,&writefd,NULL,NULL,NULL);
pthread_mutex_lock(&mutex);//加锁
if(ret<0)
{
perror("Write_news select");
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);
}
if(FD_ISSET(key,&writefd))
{
read(key,&ch,1);
if(ch=='\n')
{
printf("****请选择****\n");
printf(" 1.发送消息\n");
printf(" 2.发送文件\n");
printf(" 3.退出客户端\n");
printf(" 4.返回\n");
printf("注:如输入错误按Enter\n");
scanf("%d",&choose);
switch(choose)
{
/*发送消息*/
case 1:
{
Send_news(sfd);
}
break;
/*发送文件*/
case 2:
{
Send_files(sfd);
}
break;
/*退出客户端*/
case 3:
{
Quit_user(sfd);
close(sfd);
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);
}
break;
case 4:
{
}
break;
default:
break;
}
}
}
pthread_mutex_unlock(&mutex);//解锁
}
}
int main(int argc,char **argv)
{
//互斥锁动态初始化
pthread_mutex_init(&mutex,NULL);
int sfd;
int ret;
int ch;
pthread_t tid;
pthread_t wid,rid;
struct sockaddr_in serv_addr;
/*创建一个socket*/
sfd = socket(AF_INET, SOCK_STREAM, 0);
/*初始化一个地址结构*/
bzero(&serv_addr, sizeof(serv_addr)); //清零
serv_addr.sin_family = AF_INET; //IPv4
inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); //指定IP字符串类型转换为网络字节序
serv_addr.sin_port = htons(SERV_PORT);
//connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
START:printf("\t*************\t\n");
printf("\t* 1.注册 *\n");
printf("\t* 2.登录 *\n");
printf("\t* 3.退出 *\n");
printf("\t*************\t\n");
printf("选择<1-3>:");
scanf("%d",&ch);
switch(ch)
{
case 1:
{
//Login_user();
//printf("测试11\n");
pthread_create(&tid, NULL,Login_user,NULL);
pthread_join(tid,NULL);
//close(sfd);
goto START;
}
break;
case 2:
{
SERBUF sbuf;//用来接收服务器发来的信息
DATBUFF dbuf;//要发给服务器的信息
char ack[100];
printf("请输入用户名:");
scanf("%s",dbuf.sendname);
printf("请输入密码:");
scanf("%s",dbuf.send_pass);
//保存用户名
strcpy(uname,dbuf.sendname);
ret=connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(ret!=0)
{
printf("登录失败!\n");
goto START;
}
dbuf.status=1;
write(sfd,&dbuf,sizeof(dbuf));
while(1)
{
ret=read(sfd,ack,sizeof(ack));
if(ret>0&&strstr(ack,"登录成功")!=NULL)
{
printf("%s",ack);
break;
}
}
printf("\t*****请按<Enter>输入消息*****\n");
/********************************************************/
/**创建读写线程**/
pthread_create(&rid, NULL,Read_news,(void *)sfd);
pthread_create(&wid, NULL,Write_news,(void *)sfd);
pthread_join(wid,NULL);
exit(1);
pthread_join(rid,NULL);
}
break;
case 3:
{
exit(1);
}
break;
default:
{
printf("输入错误,重新输入!\n");
//memset(&ch,0,sizeof(ch));
goto START;
}
break;
}
while(1);
/*关闭链接*/
close(sfd);
//pthread_mutex_lock(&mutex);//加锁
//pthread_mutex_unlock(&mutex);//解锁
pthread_mutex_destroy(&mutex);//销锁
return 0;
}