1. 程式人生 > 其它 >使用C語言在Liunx環境下作出簡易FTP伺服器

使用C語言在Liunx環境下作出簡易FTP伺服器

技術標籤:linuxsocket網路

基於Linux系統下設計的簡易FTP伺服器

*模仿Linux自帶的FTP伺服器

什麼是FTP伺服器

*來自百度定義
FTP伺服器(File Transfer Protocol Server)是在網際網路上提供檔案儲存和訪問服務的計算機,它們依照FTP協議提供服務。 FTP是File Transfer Protocol(檔案傳輸協議)。顧名思義,就是專門用來傳輸檔案的協議。簡單地說,支援FTP協議的伺服器就是FTP伺服器。


FTP伺服器的工作原理是什麼

既然我們想通過已有的FTP伺服器,自己進行簡單地模仿,那就得了解FTP伺服器它的大概的原理與步驟:

  1. FTP服務bai器執行FTPd守護程序,du等待使用者的FTP請求。
    2.用bai戶執行FTP命令du,請求FTP伺服器為zhi其服務。
    例:FTP 202.119.2.197
  2. FTPd守護進bai程收到使用者的FTP請求後,派生出子程序FTP與使用者程序FTP互動,建立檔案傳輸控制連線,使用TCP埠21。
  3. 使用者輸入FTP子命令,伺服器接收子命令,如果命令正確,雙方各派生一個數據傳輸程序FTP-DATA,建立資料連線,使用TCP埠20,進行資料傳輸。
  4. 本次子命令的資料傳輸完,拆除資料連線,結束FTP-DATA程序。
  5. 使用者繼續輸入FTP子命令,重複4、5的過程,直至使用者輸入quit命令,雙方拆除控制連線,結束檔案傳輸,結束FTP程序。

專案設想

想要用C語言完成一個簡易的FTP伺服器,一定要有伺服器客戶端這兩個介面,所要實現的操作無非就是伺服器和客戶端(即本地)的一個互動以及本地的檔案操作。大致功能如下所示(XXX表示檔名):
伺服器
獲取伺服器的檔案:get XXX
展示伺服器中的檔案:ls XXX
進入伺服器某個資料夾:cd XXX
上傳檔案到伺服器:put XXX

客戶端(即本地):
檢視客戶端本地檔案:lls XXX
進入客戶端裡的資料夾:lcd XXX


基礎的伺服器客戶端的流程圖

主要原理還是基於Socket伺服器和客戶端的開發,以及呼叫相關的API。

伺服器(TCP Server):

socket bind listen accept read1 write read2 close

客戶端TCP Client):

socket connect write read close

總體開發流程:

socket_Server bind listen accept read process_requst write read2 close socket_Client connect write2 read3 close2

最後只要呼叫相關的API和新增上相應的功能程式碼即可。


專案部分程式碼

伺服器程式碼:

int get_cmd_type(char *cmd)
{
	if(!strcmp("ls",cmd))     return LS;
	if(!strcmp("quit",cmd))     return QUIT;
	if(!strcmp("pwd",cmd))     return PWD;
	if(strcmp(cmd,"cd") != NULL)     return CD;
	if(strcmp(cmd,"get") != NULL)     return GET;
	if(strcmp(cmd,"put") != NULL)     return PUT;

	return 100;
}

char *getDesDir(char *cmsg)
{
	char *p;
	p = strtok(cmsg," ");
	p = strtok(NULL," ");
	return p;
}

void msg_handler(struct Msg msg,int fd)
{
	char dataBuf[1024] = {0};
	char *file = NULL;
	int fdfile;

	printf("cmd:%s\n",msg.data);
	int ret = get_cmd_type(msg.data);


	switch(ret){
		case LS:
		case PWD:
			msg.type = 0;
			FILE *r = popen(msg.data,"r");
			fread(msg.data,sizeof(msg.data),1,r);
			write(fd,&msg,sizeof(msg));

			break;

		case CD:
			msg.type = 1;
			char *dir = getDesDir(msg.data);
			printf("dir:%s\n",dir);
			chdir(dir);
			break;
		
		case GET:
			file = getDesDir(msg.data);
			
		if(access(file,F_OK) == -1){
			  strcpy(msg.data,"沒有這個檔案!");
			  write(fd,&msg,sizeof(msg));
			}else{
		 	    msg.type = DOFILE;

				fdfile = open(file,O_RDWR);
				read(fdfile,dataBuf,sizeof(dataBuf));
				close(fdfile);

				strcpy(msg.data,dataBuf);
				write(fd,&msg,sizeof(msg));
			}
			break;

		case PUT:
			fdfile = open(getDesDir(msg.data),O_RDWR | O_CREAT);
			write(fdfile,msg.secondBuf,strlen(msg.secondBuf));
			close(fdfile);
			break;

		case QUIT:
			printf("客戶端退出!\n");
			exit(-1);
	}
}

int main(int argc,char **argv)
{
	int s_fd;
	int c_fd;
	int s_read;
	char readBuf[128];

	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;
	struct Msg msg;

	if(argc != 3){
		printf("引數不太好!\n");
		exit(-1);
	}

	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	//1.socket()
	s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1){
		perror("socket");
		exit(-1);
	
	}

	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&s_addr.sin_addr);

	//2.bind()
	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

	//3.listen()
	listen(s_fd,10);

	//4.accept()
	int clen = sizeof(struct sockaddr_in);
	while(1){
		c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
		if(c_fd == -1){
			perror("accept");
		
		}

		printf("取得連線:%s\n",inet_ntoa(c_addr.sin_addr));
	
		if(fork() == 0){
			while(1){
			 	memset(msg.data,0,sizeof(msg.data));
				s_read = read(c_fd,&msg,sizeof(msg));
				if(s_read == 0){
	 				printf("客戶端退出!\n");
					break;
				}else if(s_read > 0){
					msg_handler(msg,c_fd);
				}
			
			}
		
		
		}
	
	
	}
	close(c_fd);
	close(s_fd);
	return 0;




}


客戶端程式碼:

char *getdir(char *cmd)
{
	char *p;

	p = strtok(cmd," ");
	p = strtok(NULL," ");

	return p;
}

int get_cmd_type(char *cmd)
{
	if(strstr(cmd,"lcd"))   return LCD;

	if(!strcmp("quit",cmd))  return QUIT;
	if(!strcmp("ls",cmd))  return LS;
	if(!strcmp("lls",cmd))  return LLS;
	if(!strcmp("pwd",cmd))  return LS;

	if(strstr(cmd,"cd"))   return CD;
	if(strstr(cmd,"get"))   return GET;
	if(strstr(cmd,"put"))   return PUT;

	return -1;
}

int cmd_handler(struct Msg msg,int fd)
{
	char *dir = NULL;
	char buf[32];
	int ret;
	int filefd;

	ret = get_cmd_type(msg.data);

	switch(ret){
		case LS:
		case CD:
		case PWD:
		   msg.type = 0;
	       write(fd,&msg,sizeof(msg));
	       break;
	    case GET:
		   msg.type = 2;
		   write(fd,&msg,sizeof(msg));
		   break;
	    case PUT:
		   strcpy(buf,msg.data);
		   dir = getdir(buf);
		   
		   if(access(dir,F_OK) == -1){
			printf("%s 不存在!\n",dir);
		   }else{
			filefd = open(dir,O_RDWR);
			read(filefd,msg.secondBuf,sizeof(msg.secondBuf));
			close(filefd);

			write(fd,&msg,sizeof(msg));
		   }
		   break;
		case LLS:
		   system("ls");
		   break;	
		case LCD:
		   dir = getdir(msg.data);
		   chdir(dir);
		   break;
		case QUIT:
		   strcpy(msg.data,"quit");
		   write(fd,&msg,sizeof(msg));
		   close(fd);
		   exit(-1);
	}
    return ret;	
}

void handler_server_message(int c_fd,struct Msg msg)
{
	int s_read;
	struct Msg msgget;
	int newfilefd;
	s_read = read(c_fd,&msgget,sizeof(msgget));

	if(s_read == 0){
		printf("伺服器退出!\n");
		exit(-1);
	}else if(msgget.type == DOFILE){
		char *p = getdir(msg.data);
		newfilefd = open(p,O_RDWR | O_CREAT,0600);
		write(newfilefd,msgget.data,strlen(msgget.data));
		putchar('>');
		fflush(stdout);
	}else{
		printf("-------------------------------\n");
		printf("\n%s\n",msgget.data);
		printf("-------------------------------\n");
	
		putchar('>');
		fflush(stdout);
	}
	
}

int main(int argc,char **argv)
{
	int c_fd;
	struct sockaddr_in c_addr;
	
	struct Msg msg;

	memset(&c_addr,0,sizeof(struct sockaddr_in));

	if(argc != 3){
		printf("引數不太好!\n");
		exit(-1);
	}

	//1.socket()
	c_fd = socket(AF_INET,SOCK_STREAM,0);
	if(c_fd == -1){
		perror("socket");
		exit(-1);
	}

	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&c_addr.sin_addr);

	//2.connect()
	if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
		perror("connect");
		exit(-1);
	
	}
	printf("連線中......\n");
	int mark = 0;
	while(1){
		memset(msg.data,0,sizeof(msg.data));
		if(mark == 0) printf(">");

		gets(msg.data);

		if(strlen(msg.data) == 0){
			if(mark == 1){
				printf(">");
			}
		continue;
		}
	mark = 1;
	
	int ret = cmd_handler(msg,c_fd);
	
	if(ret > IFGO){
		putchar('>');
		fflush(stdout);
		continue;
	}
	if(ret == -1){
		printf("command not!\n");
		printf(">");
		fflush(stdout);
		continue;
	}
	handler_server_message(c_fd,msg);
	}
	return 0;
}

最後別忘記配置你自己的標頭檔案哦!


總結

做完專案之後,你可以通過命令

ftp 127.0.0.1

(或者通過config指令進行IP地址的檢視),來對比Liunx自帶的FTP伺服器,你也可以新增你想到的功能,比如新增使用者名稱和密碼,新增多客戶端的接入等等,期待你的繼續發揮。
最後祝大家成功,同時也感謝陳立臣老師的細心指導!
在這裡插入圖片描述