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

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

创建文件夹

首先在本地客户端源代码的工作目录下创建一个fil的目录文件,为文件上传和下载做准备,下面要上传的文件以及下载的文件都将存放在fil文件中。
在这里插入图片描述

同时在服务器的服务端源代码的工作目录下创建一个文件名为file的目录文件,理由同上。
在这里插入图片描述

服务端

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

#include <stdio.h>
#include <string.h> 
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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<strlen(buf);i++)
						{
							st0[k] = buf[i];
							st[j] = buf[i];
							k++;
							j++;
						}
						if(-1 == show_list(clifd,st0))
						{
							strcat(path,st);
							printf("st=%s\n",st);
							printf("path0=%s\n",path);
							if(-1 == show_list(clifd,path))
							{
								char st1[50] = "输入指令有误!\n";
								write(clifd,st1,strlen(st1)+1);
								int j;
								for(j=strlen(path)-1;path[j]!='/';j--)
								{
									path[j] = '\0';
								}
								path[j] = '\0';
							}
						}
						printf("buf:%s\n",buf);
						printf("path:%s\n",path);
					}
				}
				if(0==strcmp("dow",buf))
				{
					//文件下载
					char snf[50] = "请输入文件名:";
					char name[1024] = {};
					int res = write(clifd,snf,strlen(snf)+1);
					printf("res=%d",res);
					read(clifd,name,sizeof(name));
					printf("name=%s\n",name);
					path[strlen(path)] = '/';
					strcat(path,name);
					struct stat ato = {};
					stat(path,&ato);
					int size = (int)ato.st_size;
					printf("path=%s\n",path);
					int fd = open(path,O_RDONLY,0644);
					if(fd < 0)
					{
						perror("open");
						char prf[50] = "文件不存在!\n";
						write(clifd,prf,strlen(prf)+1);
					}
					else
					{
						char sd_fil[1024] = {};
						while(size>=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<strlen(path);i++)
					{
						str[i] = path[i];
					}
					printf("str=%s",str);
					char nam[1024] = {};
					int j=0;
					for(int i=3;i<strlen(buf);i++)
					{
						nam[j] = buf[i];
						j++;
					}
					str[strlen(str)] = '/';
					strcat(str,nam);
					printf("str=%s\n",str);
					struct stat ato = {};
					if(stat(str,&ato))
					{
						char snd[1024] = {};
						snd[0] = '0';
						write(clifd,snd,strlen(snd)+1);
						printf("不存在该文件消息已发送!\n");
						//不断接收文件并且写入
						int fdd = open(str,O_WRONLY|O_CREAT,0644);
						flc = 1;
						while(1 == flc)
						{
							read(clifd,buf,sizeof(buf));
							printf("文件传输中!\n");
						
							for(int i=0;i<strlen(buf);i++)
							{
								if(2 == buf[i])
								{
									flc=0;
									buf[i] = '\0';
									break;
								}
							}
							printf("flc=%d\n",flc);
							write(fdd,buf,strlen(buf));
							if(0 == flc)
							{
								break;
							}
							printf("aaaflc=%d\n",flc);
						}
						close(fdd);
						printf("path=%s\n",str);
						int j;
						for(j=strlen(str);str[j]!='/';j--) str[j]='\0';
						str[j] = '\0';
						for(int i=0;i<strlen(str);i++) path[i]=str[i];
						printf("文件传输完成!\n");
						//break;
					}
					else
					{	//为后续其他功能做准备,如替换文件,断点续传
						printf("消息发送中!\n");
						char snd[1024] = {};
						snd[0] = '1';
						printf("消息发送中!\n");
						char stt[1024] = {}; 
						char* siz = in_pass_ch(stt,(int)ato.st_size);
						int size = strlen(siz);
						printf("消息发送中!\n");
						char str1[50] = {};
						char* tim = file_time(ato.st_mtime,str1);
						printf("消息发送中!\n");
						snd[1] = '0'+ size;
						snd[2] = '0'+ strlen(tim);
						printf("消息发送中!\n");
						strcat(snd,siz);
						strcat(snd,tim);  
						write(clifd,snd,strlen(snd)+1);
						printf("文件属性已发送!\n");
					}
				}
			}
		}

	}

}

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

客户端

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

#include <stdio.h>
#include <string.h> 
#include <stdbool.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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 <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <getch.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//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<strlen(buf);i++)
		{
			if(2 == buf[i])
			{
				flag = 0;
				buf[i] = '\0';
				break;
			}
		}
		write(fdd,buf,strlen(buf));
		if(0 == flag) break;
	}
	close(fdd);
	printf("文件下载成功!\n");
	printf("path=%s",path);
	return true;
}

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

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值