基于Linux下TCP/IP协议局域网聊天室

这两天写了聊天室,基于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;
}


猜你喜欢

转载自blog.csdn.net/zdw6868/article/details/81334607