利用TCP协议完成服务器与客户端之间文件传输

利用TCP文件传输协议模拟与服务器的文件上传、下载操作,以及进入各种目录和列举当前目录文件状态操作。ls为查看当前目录,cd+目录名进入该目录,cd …则返回上一级目录,dow加文件名则下载文件到本地代码所在的工作目录,snd加文件名则上传本地代码所在工作目录的文件到当前查看服务器所在的目录,quit则退出。

创建文件夹

首先在本地客户端源代码的工作目录下创建一个fil的目录文件,为文件上传和下载做准备,下面要上传的文件以及下载的文件都将存放在fil文件中。
利用TCP协议完成服务器与客户端之间文件传输_第1张图片

同时在服务器的服务端源代码的工作目录下创建一个文件名为file的目录文件,理由同上。
利用TCP协议完成服务器与客户端之间文件传输_第2张图片

服务端

首先是服务器存放的服务器源代码,由于在本地编译所以ip一致都使用的本地ip,注意端口号需要一致。

#include 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include "tools.h"

#define IP "192.168.43.201"

int show_list(int clifd,char* path)
{
	printf("path=%s\n",path);
	char snd[1024] = {};
	DIR* dp = opendir(path);
	if(NULL == dp)
	{
		perror("opendir");
		return -1;
	}
	for(struct dirent* de = readdir(dp);NULL!=de;de=readdir(dp))
	{
		if('.' == de->d_name[0]) continue;
		strcat(snd,de->d_name);
		snd[strlen(snd)] = ' ';		
	}
	snd[strlen(snd)] = '\0';
	write(clifd,snd,strlen(snd));
	write(clifd,NULL,0);
	printf("已反馈ls\n");
	closedir(dp);
	return 0;
}


int main()
{
	printf("服务器创建socket...\n");
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(0 > sockfd)
	{
		perror("socket");
		return -1;
	}

	printf("准备地址...\n");
	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(9040);
	addr.sin_addr.s_addr = inet_addr(IP);
	socklen_t len = sizeof(addr);

	printf("绑定socket与地址...\n");
	if(bind(sockfd,(struct sockaddr*)&addr,len))
	{
		perror("bind");
		return -1;
	}

	printf("设置监听...\n");
	if(listen(sockfd,5))
	{
		perror("listen");
		return -1;
	}

	printf("等待客户端连接...\n");
	for(;;)
	{
		int flc = 1;
		struct sockaddr_in addrcli = {};
		int clifd = accept(sockfd,(struct sockaddr*)&addrcli,&len);
		if(0 > clifd)
		{
			perror("accept");
			continue;
		}

		if(0 == fork())
		{
			char buf[1024] = {};
			char init_path[1024] = "file";
			char path[1024] = "file";
			for(;flc==1;)
			{
				read(clifd,buf,sizeof(buf));
				if(0 == strcmp("检测服务器!",buf))
				{
					printf("检测到一个服务器连接!\n");
					char snd[20] = IP;
					write(clifd,snd,strlen(snd)+1);
					continue;
				}
				printf("%s\n",buf);
				if(0 == strcmp(buf,"ls"))//list功能
				{
					if(0 == show_list(clifd,path))
					printf("成功反馈!\n");
				}
				if('c'==buf[0]&&'d'==buf[1])//CD功能
				{
					if('.'==buf[2]&&'.'==buf[3])
					{
						if(0!=strcmp(path,init_path))
						{
							int j;
							for(j=strlen(path)-1;path[j]!='/';j--)
							{
								path[j] = '\0';
							}
							path[j] = '\0';
						}
						show_list(clifd,path);
					}
					else
					{
						char st[1024] = "/";
						char st0[1024] = {};
						int k=0,j=1;
						for(int i=2;i=1024)
						{
							read(fd,sd_fil,1023);
							printf("%s",sd_fil);
							write(clifd,sd_fil,strlen(sd_fil));
							size = size-1023;
						}
						for(int i=0;i<1024;i++) sd_fil[i] = '\0';
						if(size < 1024)
						{
							read(fd,sd_fil,size);
							printf("%s",sd_fil);
							sd_fil[strlen(sd_fil)] = 2;
							write(clifd,sd_fil,strlen(sd_fil));
						}
						printf("文件下载完成\n");
						int j;
						for(j=strlen(path);path[j]!='/';j--) path[j]='\0';
						path[j] = '\0';
					}
				}
				if('n'==buf[0]&&'a'==buf[1]&&'m'==buf[2])//文件上传
				{
					char str[1024];
					for(int i=0;i

首先是定义了一个宏为ip地址,还有封装了一个显示当前服务器目录并且把消息传给本地的文件。然后主函数按照创建socket文件准备地址绑定监听等,然后开始启动死循环,不断接收消息,接收到不同指令则执行不同的操作。

客户端

然后是本地存放的客户端源代码,由于在本地编译所以ip一致都使用的本地ip,注意端口号需要一致。

#include 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "tools.h"
#include "fil_snd.h"

//文件下载
bool fil_dow(int sockfd,struct sockaddr_in addr,socklen_t len)
{
	char name[1024] = {};
	read(sockfd,name,sizeof(name));
	printf("%s",name);
	//printf("请输入文件名:");
	scanf("%s",name);
	write(sockfd,name,strlen(name)+1);
	fil_dow_TCP(sockfd,addr,len,name);
	return true;
}

bool fil_snd(int sockfd,struct sockaddr_in addr,socklen_t len)
{
	int fd;
	char str[1024] = "fil/";
	char fil[1024] = {};
	while(true)
	{
		printf("请输入指定目录下要传输文件的文件名:");
		scanf("%s",fil);
		clear_stdin();
		strcat(str,fil);
		printf("%s\n",str);
		fd = open(str,O_RDONLY,0644);
		if(fd < 0)
		{	
			perror("open");
			printf("文件不存在,是否重新输入(y/n)");
			char c = getchar();
			if('y'==c||'Y'==c) 
			{
				continue;
			}
			else 
			{
				return false;
			}
		}
		else
		{
			break;
		}
	}	
	char name[1024] = "nam";
	strcat(name,fil);
	write(sockfd,name,strlen(name)+1);//将文件名发送过去
	struct stat ato = {};
	if(stat(str,&ato))
	{
		perror("stat");
		return false;
	}
	char str1[50] = {};
	char* tim = file_time(ato.st_mtime,str1);//文件时间
	printf("请选择传输协议(1、TCP,2、UDP):\n");
	if('1' == get_cmd('1','2'))
	{
		//TCP传输
		return fil_TCP(sockfd,addr,len,fd,tim,(int)ato.st_size);
	}
	else
	{
		printf("2\n");//UDP传输
		return fil_UDP(sockfd,addr,len,fd,tim,(int)ato.st_size);
	}
}

int main()
{
	printf("与服务器创建socket...\n");
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(0 > sockfd)
	{
		perror("socket");
		return -1;
	}

	printf("准备地址...\n");
	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(9040);//端口号要一致
	addr.sin_addr.s_addr = inet_addr("192.168.43.201");//服务器的公网ip地址47.96.239.112

	printf("连接服务器...\n");
	socklen_t len = sizeof(addr);
	if(connect(sockfd,(struct sockaddr*)&addr,len))
	{
		perror("bind");
		return -1;
	}


	printf("客户端连接...\n");
	printf("检测可用服务器中...\n");
		char buf[1024] = "检测服务器!";
		write(sockfd,buf,strlen(buf)+1);
		char rev[1024] = {};
		if(0 < read(sockfd,rev,sizeof(rev)))
		{
			printf("可用服务器ip:");
			printf("%s\n",rev);
		}

	char cm[50] = {};
	int flg = 1;
	while(flg==1)
	{
		while(printf("请输入指令:"))
		{
			scanf("%s",cm);
			clear_stdin();
			if(0==strcmp(cm,"quit"))//输入quit退出客户端
			{
				flg = 0;
				break;
			}
			write(sockfd,cm,strlen(cm)+1);
			if(0==strcmp(cm,"ls"))
			{
				//输入ls,实现list功能,查看当前目录
				char ls[1024] = "ls";
				read(sockfd,ls,sizeof(ls));
				printf("%s\n",ls);
			}
			else if('c'==cm[0]&&'d'==cm[1])
			{
				//输入cd,实现cd功能
				char cd[1024] = {};
				read(sockfd,cd,sizeof(cd));
				printf("%s\n",cd);
			}
			else if(0==strcmp(cm,"snd"))
			{	
				//输入snd进入文件传输功能
				if(fil_snd(sockfd,addr,len)) 
				{
					anykey_continue();
					break;
				}
			}
			else if(0==strcmp(cm,"dow"))
			{
				//输入dow进入文件下载功能
				fil_dow(sockfd,addr,len);
				printf("\n");
				clear_stdin();
			}
			else
			{	
				printf("未找到指令,请重新输入!\n");
				anykey_continue();
			}		
		}
	}

	close(sockfd);//断开连接
		
}

同样主函数按照创建socket文件准备地址绑定监听等,然后开始启动死循环,不断发送接收消息,发送不同指令则执行不同的操作。同时也封装了一些发送接收文件的分函数。分函数文件如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//TCP文件传输
bool fil_TCP(int sockfd,struct sockaddr_in addr,socklen_t len,int fd,char* tim,int size)
{
	char buf[1024] = {};
	read(sockfd,buf,sizeof(buf));
	printf("%s\n",buf);
	if('1' == buf[0])
	{
		printf("文件已存在\n");			
		return false;	
	}
	else
	{
		printf("1size = %d\n",size);
		while(size>=1024)
		{
			read(fd,buf,1023);
			printf("%s",buf);
			write(sockfd,buf,strlen(buf));
			size = size-1023;
		}
		for(int i=0;i<1024;i++) buf[i] = '\0';
		printf("2size = %d\n",size);
		if(size < 1024)
		{
			read(fd,buf,size);
			buf[strlen(buf)] = 2;
			//buf[strlen(buf)] = '\0';
			write(sockfd,buf,strlen(buf));
			printf("%s",buf);
		}
		printf("3size=%d,buf=%d\n",size,strlen(buf));
		printf("文件传输完成!\n");
		close(fd);
	}
	return true;
}

//UDP文件传输
bool fil_UDP(int sockfd,struct sockaddr_in addr,socklen_t len,int fd,char* tim,int size)
{
	printf("该功能暂未开放,敬请期待!\n");
	return true;
}


//TCP文件下载
bool fil_dow_TCP(int sockfd,struct sockaddr_in addr,socklen_t len,char* name)
{
	char buf[1024] = {};
	char path[1024] = "fil";
	path[strlen(path)] = '/';
	strcat(path,name);
	int fdd = open(path,O_WRONLY|O_CREAT,0644);
	int flag = 1;
	while(1==flag)
	{
		read(sockfd,buf,sizeof(buf));
		printf("文件传输中。。。\n");
		for(int i=0;i

最后需注意的是,由于能力有限,cd功能中间不要加空格。。。部分代码还需待改进!

你可能感兴趣的:(Linux操作系统)