1. 程式人生 > >基於TCP的TFTP(Trivial File Transfer Protocol,簡單檔案傳輸協議) 的c程式設計實現

基於TCP的TFTP(Trivial File Transfer Protocol,簡單檔案傳輸協議) 的c程式設計實現

我們或許都聽到過,TFTP(Trivial File Transfer Protocol,簡單檔案傳輸協議)是TCP/IP協議族中的一個用來在客戶機與伺服器之間進行簡單檔案傳輸的協議,提供不復雜、開銷不大的檔案傳輸服務。

本文就簡單的敘述下tftp的小檔案傳輸功能以及客戶端對伺服器的列表功能。

之前就一直很納悶,我們經常在網上下載什麼東西或者從別處傳輸一個檔案,具體是怎麼實現的呢?於是乎,翻查一些資料,加上自己對網路程式設計的逐步加深,所以功夫不負有心人,還算是大致的完成了下。

本例程實現的功能呢? 就是實現客戶端對伺服器端提供的檔案上傳與下載功能,還有對伺服器端目錄的顯示功能,好了,請閱讀下面的程式設計實現部分:

首先是伺服器端:


#include<head.h>
typedef struct 
{
	int type;
	int size;
	char mtxt[1024];
}MSG;

#define PUT_FILE   10 
#define GET_FILE   20 
#define LIST_DIR   30 
#define NEXIST_FILE 40 
#define YEXIST_FILE 50

void handler_signal(int signum)
{
	waitpid(-1,NULL,WNOHANG);
	printf("CHild process over!\n");
}

void do_put(int sockfd,MSG *pmsg)
{
	int fd;

	fd = open(pmsg->mtxt,O_WRONLY | O_CREAT | O_TRUNC,0666);
	if(fd < 0){
		fprintf(stderr,"Fail to open %s : %s\n",pmsg->mtxt,strerror(errno));
		exit(EXIT_FAILURE);
	}
	
	if(ftruncate(fd,pmsg->size) < 0)
	{
		perror("Fail to ftruncate");
		exit(EXIT_FAILURE);
	}

	while(1)
	{
		if(recv(sockfd,pmsg,sizeof(MSG),0) <= 0)
			break;
		
		if(pmsg->size == 0)
			break;

		write(fd,pmsg->mtxt,pmsg->size);
	}
	
	return ;
}

int do_get(int sockfd,MSG *pmsg)
{
	int fd;
	struct stat f_info;

	fd = open(pmsg->mtxt,O_RDONLY);
	if(fd < 0){
		fprintf(stderr,"Fail to open %s : %s\n",pmsg->mtxt,strerror(errno));	
		pmsg->type = NEXIST_FILE;
	}
	
	if(fstat(fd,&f_info) < 0)
	{
		perror("Fail to fstat");
		pmsg->type = NEXIST_FILE;
	}
	
	if(pmsg->type == NEXIST_FILE)
	{
		//告訴客戶端結果
		send(sockfd,pmsg,sizeof(MSG),0);
		return -1;

	}else{
		pmsg->type = YEXIST_FILE;
		pmsg->size = f_info.st_size;
		send(sockfd,pmsg,sizeof(MSG),0);
	}

	while(1)
	{
		pmsg->size = read(fd,pmsg->mtxt,sizeof(pmsg->mtxt));
		if(send(sockfd,pmsg,sizeof(MSG),0) < 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}

		if(pmsg->size == 0)
			break;
	}

	return 0;
}

int do_list(int sockfd)
{
	DIR *pdir;
	MSG msg;
	struct dirent *pdirent;

	pdir = opendir(".");
	if(pdir == NULL)
	{
		msg.type = NEXIST_FILE;
		send(sockfd,&msg,sizeof(MSG),0);
		return -1;
	}
	
	while ( pdirent = readdir(pdir) )
	{
		if(pdirent->d_name[0] == '.')	
			continue;
		
		msg.type = YEXIST_FILE;
		strcpy(msg.mtxt,pdirent->d_name);
		
		if(send(sockfd,&msg,sizeof(MSG),0) < 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}
	}

	msg.type = NEXIST_FILE;
	if(send(sockfd,&msg,sizeof(MSG),0) < 0)
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
	}
	
	return 0;
}

void do_client(int connect_fd)
{
	MSG msg;

	while(1)
	{
		if(recv(connect_fd,&msg,sizeof(MSG),0) <= 0)
		{
			perror("Fail to recv");
			exit(EXIT_FAILURE);
		}

		switch(msg.type)
		{
			case PUT_FILE:
				do_put(connect_fd,&msg);
				break;

			case GET_FILE:
				do_get(connect_fd,&msg);
				break;

			case LIST_DIR:
				do_list(connect_fd);
				break;
		}
	}

	exit(EXIT_SUCCESS);
}


int main(int argc, const char *argv[])
{
	int listen_fd;
	pid_t pid;
	int connect_fd;
	struct sockaddr_in server_addr;
	struct sockaddr_in peer_addr;
	socklen_t addrlen = sizeof(struct sockaddr_in);

	if(argc < 3)
	{
		fprintf(stderr,"Usage : %s ip port\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	if(signal(SIGCHLD,handler_signal) == SIG_ERR)
	{
		perror("Fail to signal");
		exit(EXIT_FAILURE);
	}

	listen_fd = socket(AF_INET,SOCK_STREAM,0);
	if(listen_fd < 0){
		perror("Fail to socket");
		exit(EXIT_FAILURE);
	}
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_port   = htons(atoi(argv[2]));
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) < 0)
	{
		perror("Fail to bind");
		exit(EXIT_FAILURE);
	}

	listen(listen_fd,128);
	
	printf("listen ...\n");
	while(1)
	{
		connect_fd = accept(listen_fd,(struct sockaddr *)&peer_addr,&addrlen);
		if(connect_fd < 0){
			perror("Fail to accept");
			exit(EXIT_FAILURE);
		}
		
		printf("------------------------------------------------\n");
		printf("connect_fd = %d\n",connect_fd);
		printf("Port : %d\n",ntohs(peer_addr.sin_port));
		printf("IP   : %s\n",inet_ntoa(peer_addr.sin_addr));
		printf("---------------------------------------------\n");
	
		pid = fork();
		if(pid < 0){
			perror("Fail to fork");
			exit(EXIT_FAILURE);
		}
		
		if(pid == 0)
		{
			close(listen_fd);
			do_client(connect_fd);
		}

		//避免檔案描述符號浪費
		close(connect_fd);
	}

	return 0;
}

再來看客戶端:
#include <head.h>

typedef struct 
{
	int type;
	int size;
	char mtxt[1024];
}MSG;

#define PUT_FILE 10 
#define GET_FILE 20 
#define LIST_DIR 30 
#define NEXIST_FILE 40 
#define YEXIST_FILE 50

int do_put(int sockfd,char *filename)
{
	int fd;
	MSG msg;
	struct stat f_info;

	fd = open(filename,O_RDONLY);
	if(fd < 0){
		fprintf(stderr,"Fail to open %s : %s\n",filename,strerror(errno));
		return -1;
	}
	
	if(fstat(fd,&f_info) < 0)
	{
		perror("Fail to fstat");
		return -1;
	}

	msg.type = PUT_FILE;
	msg.size = f_info.st_size;
	strcpy(msg.mtxt,filename);
	
	if(send(sockfd,&msg,sizeof(MSG),0) < 0)
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
	}
	
	while(1)
	{
		msg.size = read(fd,msg.mtxt,sizeof(msg.mtxt));
		if(send(sockfd,&msg,sizeof(MSG),0) < 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}

		if(msg.size == 0)
			break;
	}

	return 0;
}

int do_get(int sockfd,char *filename)
{
	int fd;
	MSG msg;

	msg.type = GET_FILE;
	strcpy(msg.mtxt,filename);
	
	if(send(sockfd,&msg,sizeof(MSG),0) < 0)
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
	}
	
	//等待伺服器端結果:檔案存放或不存在
	if(recv(sockfd,&msg,sizeof(MSG),0) <= 0)
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
	}

	if(msg.type == NEXIST_FILE)
	{
		printf("The get file:%s is not exist!\n",filename);
		return -1;
	}

	fd = open(msg.mtxt,O_WRONLY | O_CREAT | O_TRUNC,0666);
	if(fd < 0){
		fprintf(stderr,"Fail to open %s : %s\n",msg.mtxt,strerror(errno));
		exit(EXIT_FAILURE);
	}
	
	if(ftruncate(fd,msg.size) < 0)
	{
		perror("Fail to ftruncate");
		exit(EXIT_FAILURE);
	}
		
	while(1)
	{
		if(recv(sockfd,&msg,sizeof(MSG),0) <= 0)
			break;
		
		if(msg.size == 0)
			break;

		write(fd,msg.mtxt,msg.size);
	}
	
	return 0;
}

int do_list(int sockfd)
{
	MSG msg;

	msg.type = LIST_DIR;
	if(send(sockfd,&msg,sizeof(MSG),0) < 0)
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
	}

	while(1)
	{
		if(recv(sockfd,&msg,sizeof(MSG),0) <= 0)
			break; 
		
		if(msg.type == NEXIST_FILE)
			break;

		printf("%s\n",msg.mtxt);
	}

	return 0;
}

int do_task(int sockfd,char *pcmd,char *filename)
{
	if(strncmp(pcmd,"get",3) == 0)
	{
		do_get(sockfd,filename);
	
	}else if(strncmp(pcmd,"put",3) == 0){
		
		do_put(sockfd,filename);	
	
	}else if(strncmp(pcmd,"list",4) == 0){
		
		do_list(sockfd);
	
	}else{
		printf("Invalid cmd : %s\n",pcmd);
		return -1;
	}

	return 0;
}


int main(int argc, const char *argv[])
{
	char buf[1024];
	int sockfd;
	char *pcmd,*pname;
	int connect_fd;
	struct sockaddr_in server_addr;
	
	if(argc < 3)
	{
		fprintf(stderr,"Usage : %s ip port\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0){
		perror("Fail to socket");
		exit(EXIT_FAILURE);
	}
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_port   = htons(atoi(argv[2]));
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) < 0)
	{
		perror("Fail to bind");
		exit(EXIT_FAILURE);
	}
	
	while(1)
	{
		printf(">");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf) - 1] = '\0';
		
		if(strncmp(buf,"quit",4) == 0)
			break;
	
		pcmd  = strtok(buf," ");
		pname = strtok(NULL," ");
		
		do_task(sockfd,pcmd,pname);
	}

	return 0;
}

或許生活就是這個樣子,當你想要去做一件事情的時候,那就認真的去做吧,肯定會成功的,或許我們的現在看起來是多麼的狼狽不堪,但是我們都沒有嘲笑別人的權利,因為成功的人畢竟是少數,我們不能以常人的思維去判斷一個人的價值取向,如果真的想實現我們的夢想,那麼此時此刻,就是我們應該認真做事,好好做人的時候了,我堅信當你的事業到達一個程度的時候,你的人格魅力將會自動的提升上去。

O(∩_∩)O~於君共勉