linux C 实现简易 ftp(内含客户端及服务端代码及个人理解)

1.基本功能

  • get + xxx 下载服务器的某个文件到客户端。

  • put + xxx 上传客户端的某个文件到服务器。

  • cd  切换服务器的当前目录

  • ls 列出服务器当前路径下的所有文件

  • lcd 切换客户端的当前目录

  • lls 列出客户端当前路径下的所有文件

  • pwd 展示服务器的当前路径

  • quit 关闭当前客户端的连接

2.编程步骤

双端通讯流程图

Socket 服务端步骤:

  • socket() 创建一个 Socket
  • bind() 绑定ip和端口号
  • listen() 监听
  • accept() 接收连接的请求
  • write() 和 read() 进行会话
  • close() 关闭 Socket

Socket 客户端步骤:

  • socket() 创建一个 Socket
  • connect() 与服务器连接
  • write() 和 read() 进行会话
  • close() 关闭 Socket

3.客户端和服务端共用代码

//config.h

#define LS  1
#define PWD  2
#define GET 3
#define IFGO  4
#define CD   5
#define PUT  6
#define LLS  7
#define LCD  8
#define QUIT 9

//采用结构体存储指令信息
struct Message {
        int fileFlag; //处理get指令的文件判断标志
        char cmd[1024];//指令
        char contentBuf[4096];//文件内容

};

服务端代码

//头文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "config.h"
#include <fcntl.h>

main函数:建立客户端与服务端的连接,接收客户端的指令



//argc:命令行参数个数  argv:字符串数组

int main(int argc,char **argv){
    //变量定义
    int s_fd;
    int c_fd;
    int n_read;
    char readBuf[128];

    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;
    struct Message msg;

  
    //参数个数判断
    if(argc!=3){
        
        printf("error:params expected 3 but %d given\n",argc);
        exit(-1);
    }    


    //memset()函数 初始化内存
    memset(&s_addr,0,sizeof(struct sockaddr_in));
    memset(&c_addr,0,sizeof(struct sockaddr_in));

    //socket() 创建一个套接字
    // int socket(int domain, int type, int protocol); 
    s_fd=socket(AF_INET,SOCK_STREAM,0);

    //ip and port
    s_addr.sin_family=AF_INET;

    //htons() 函数:将整型变量从主机字节顺序转变成网络字节顺序
    s_addr.sin_port=htons(atoi(argv[2]));
    //inet_aton()函数:字符串IP地址转换为一个32位的网络序列IP地址
    inet_aton(argv[1],&s_addr.sin_addr);

    //bind()函数:绑定ip和端口
    // int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

    //listen 监听连接
    listen(s_fd,8);
    int clen=sizeof(struct sockaddr_in);


    while(1){

   //accept 队列中提取客户端连接
    // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);  

        //主进程不断获取多个客户端连接,因此用while
        c_fd=accept(s_fd,(struct sockaddr*)&c_addr,&clen);
        //客户端连接失败
        if(c_fd==-1){
            perror("create client socket failed!\n");
        }
        //客户端ip
        char *cip=inet_ntoa(c_addr.sin_addr);
        //已连接客户端(交互信息)
        printf("a new connection [%s] has been established...\n",cip);
        
        //创建子进程处理
        if(0==fork()){

             //进入此说明当前是在子进程中
            while(1){

             //子进程不断处理当前客户端的请求,因此用while
                memset(msg.cmd,0,sizeof(msg.cmd));
                //ssize_t read(int fd, void *buf, size_t count);
                n_read=read(c_fd,&msg,sizeof(msg));
                //TCP 协议中,当一方关闭了连接,另一方通过 read() 函数从套接字中读取数据时,会返回一个值为 0 的结果,表示当前没有数据可以读取

                //如果读取到的结果是0个字节,说明客户端退出了连接
                if(n_read==0){
                    puts("client disconnected...");
                    break;
                }

                 //有效的指令,读取到的字节数>0

                else if(n_read>0){

                //messageHandler函数处理客户端发来的信息
                    messageHandler(msg,c_fd,cip);
                }        
            }
        }
    }
    return 0;
}

​

getCommandType函数:将字符串指令转换为整形,供messageHandler函数中switch分支单独处理

int getCommandType(char *cmd){

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

/*char *strstr(const char *str1, const char *str2)

返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回NULL指针。 */
        if(strstr(cmd,"cd")!=NULL)
                return CD;
        if(strstr(cmd,"get")!=NULL)
                return GET;
        if(strstr(cmd,"put")!=NULL)
                return PUT;

        //所有指令都不匹配,即未知指令,返回-1

        return  -1;
}

 getDestDir函数:获取文件名或路径

char *getDestDir(char *cmd){
        char *p;
        //char *strtok(char *str, const char *delim);
        p=strtok(cmd," ");
        p=strtok(NULL," ");
        return p;
}

messageHandler函数:处理客户端指令的业务代码

void messageHandler(struct Message msg,int fd,char *cip){

        char *filePath=NULL;
        char dataBuf[4096]={0};
        int file_fd;
        FILE *fp;
        char *dir;

     /*cmdType是指令的类型(如ls、cd、get)
       封装getCommandType()函数获取指令类别
       配合switch函数处理相应的功能*/
        int cmdType=getCommandType(msg.cmd);

        //调试信息,方便查询问题
        printf("-----------------------\ncommand:%s   cmdType: %d\n",msg.cmd,cmdType);
        if(cmdType==-1){
                puts("command not found");
        }

        switch(cmdType){
                case LS:
                case PWD:
                         //FILE *popen(const char *command, const char *type);
                        fp=popen(msg.cmd,"r");
                         //1:执行ls 或pwd 指令 2:返回执行结果对应的文件指针fp

                        //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
                        fread(msg.cmd,sizeof(msg.cmd),1,fp);
                         //读取1次fp,读到msg.cmd中
                        //ssize_t write(int fd, const void *buf, size_t count);
                        write(fd,&msg,sizeof(msg));
                        //把结果写到客户端(fd)
                        break;


                case CD:
                        dir=getDestDir(msg.cmd);
                        printf("dir:%s\n",dir);
                        // int chdir(const char *path); 
                        //changes the current working directory of the calling process to the directory specified in path.     
                        chdir(dir);     

                        break;

                case GET:
                        filePath=getDestDir(msg.cmd);
                        //文件不存在
                        if(access(filePath,F_OK)==-1){
                                strcpy(msg.cmd,"no such file!");
                                write(fd,msg.cmd,sizeof(msg.cmd));
                        }else{
                        //文件存在
                                msg.fileFlag=1;
                                printf("filePath is %s\n",filePath);
                                file_fd=open(filePath,O_RDWR);
                        //把文件读到dataBuf中
                                read(file_fd,dataBuf,sizeof(dataBuf));
                                close(file_fd);
                        //将dataBuf数据覆盖msg的cmd,要求cmd足够大,不然获取的文件稍大就会导致strcpy后的文件不完全的情况
                                strcpy(msg.cmd,dataBuf);
                                write(fd,&msg,sizeof(msg));
                        }
                        break;
                case PUT:
                        //1.获取待上传文件的名称 2.创建文件及权限 3.内容为空,待拷贝...
                        file_fd=open(getDestDir(msg.cmd),O_RDWR|O_CREAT,0644);
                        //write(file_fd,msg.contentBuf,sizeof(msg.contentBuf));
                        //写文件用strlen 区别看这里blog.csdn.net/sinat_25457161/article/details/48572033 
                        //客户端发送过来的文件的内容存在msg.contentBuf中,将contentBuf写到file_fd中
                        write(file_fd,msg.contentBuf,strlen(msg.contentBuf));
                        close(file_fd);
                        break;

                case QUIT:
                        printf("connection[%s] disconnected...\n",cip);
                        exit(-1);
        }
}

客户端代码

//头文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "config.h"
#include <fcntl.h>

main函数:建立客户端与服务端的连接,客户端发送指令给服务端


int main(int argc,char **argv){
	//变量定义
	int c_fd;
	int n_read;

	struct sockaddr_in c_addr;
	struct Message msg;
	//参数个数判断
	if(argc!=3){
		
		printf("error:params expected 3 but %d given\n",argc);
		exit(-1);
	}	
	//初始化内存
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	//socket
	// int socket(int domain, int type, int protocol); 
	c_fd=socket(AF_INET,SOCK_STREAM,0);

	//ip and port
	c_addr.sin_family=AF_INET;
	c_addr.sin_port=htons(atoi(argv[2]));
	//字符串IP地址转换为一个32位的网络序列IP地址
	inet_aton(argv[1],&c_addr.sin_addr);


	int clen=sizeof(struct sockaddr_in);

	//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
		if(connect(c_fd,(struct sockaddr *)&c_addr,clen)==-1){
			perror("create client socket failed...\n");
			exit(-1);
		}
		//消息提示:客户端连接到服务端
		printf("we have been connected with the server\n");
        //调节指令输入格式的标志位
		int mark=0;
		
		while(1){
			memset(msg.cmd,0,sizeof(msg.cmd));
			if(mark==0){
				putchar('>');
			}
			mark=1;
            //指令存到cmd中
			gets(msg.cmd);
			int commandType=commandHandler(msg,c_fd);
			printf("count=%d\n",count);
			if(commandType>IFGO){
				putchar('>');
				fflush(stdout);
				continue;
			}
            //无效指令
			if(commandType==-1){
				puts("command not found");	
				putchar('>');
				fflush(stdout);	
				continue;
			}
            //处理服务端响应的结果
	        	serverMessageHandler(c_fd,msg);
		
		}
				
	
	return 0;
}

getCommandType函数:将字符串指令转换为整形

//字符串指令转换为整形 供switch处理
int getCommandType(char *cmd){

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

      if(strstr(cmd,"lcd"))      return LCD;
      if(strstr(cmd,"cd"))       return CD;
      if(strstr(cmd,"get"))      return GET;
      if(strstr(cmd,"put"))     return PUT;
     //无效指令 返回-1
      return -1;
}

  getLocalDir函数:获取文件名或路径

char *getLocalDir(char *cmd){
        char *p=NULL;
        //::char *strtok(char *str, const char *delim);
        p=strtok(cmd," ");
        p=strtok(NULL," ");
        return p;
}

 commandHandler函数:处理客户端发送服务端的指令

int commandHandler(struct Message msg,int fd){

        char *filePath=NULL;
        char *dir;
        int file_fd;
        int cmdType=getCommandType(msg.cmd);
        char tempBuf[32];
        //调试信息
        printf("commandHandler:msg.cmd is %s\n",msg.cmd);
        switch(cmdType){
                case LS:
                case PWD:
                case CD:
                        count++;
                        write(fd,&msg,sizeof(msg));
                        break;

                case GET:
                        count++;
                        write(fd,&msg,sizeof(msg));
                        break;
                case PUT:
                        count++;
                        strcpy(tempBuf,msg.cmd);
                        //strtok函数会改变msg.cmd ,采用临时变量替代msg.cmd
                        filePath=getLocalDir(tempBuf);
                        printf("filePath is %s\n",filePath);
                        if(access(filePath,F_OK)==-1){
                                printf("%s doesn't exist!\n",filePath);
                        }else{

                                //本地存在和待上传的文件
                                file_fd=open(filePath,O_RDWR);
                                memset(msg.contentBuf,0,sizeof(msg.contentBuf));
                                //把文件读到contentBuf中
                                read(file_fd,msg.contentBuf,sizeof(msg.contentBuf));
                                close(file_fd);
                                //把结构体写到服务端
                                write(fd,&msg,sizeof(msg));
                        }
                        break;
                case LLS:
                        count++;
                        //列出服务端当前路径下的所有文件
                        system("ls");
                        break;

                case LCD:
                        count++;
                        dir=getLocalDir(msg.cmd);
                        chdir(dir);
                        break;

                case QUIT:
                        count++;
                        write(fd,&msg,sizeof(msg));
                        close(fd);
                        exit(-1);
        }
        return cmdType;
}

 serverMessageHandler函数:客户端处理服务端响应的信息

//处理服务端响应的信息
void serverMessageHandler(int fd,struct Message msg){

        struct Message recMsg;
        int nread=read(fd,&recMsg,sizeof(recMsg));
        //TCP协议某一方关闭连接时,read()返回0,读不到数据
        if(nread==0){
                puts("server disconnected...");
                exit(-1);
        }
        //处理get指令获取的文件
        else if(recMsg.fileFlag==1){
                char *dir=getLocalDir(msg.cmd);
                printf("local fileName:%s\n",dir);
                //创建文件,名为dir
                int newFilefd=open(dir,O_RDWR|O_CREAT,0600);
                write(newFilefd,recMsg.cmd,strlen(recMsg.cmd));
                putchar('>');
                fflush(stdout);
        }else{
                puts("----------------------------");
                printf("\n%s\n",recMsg.cmd);
                puts("----------------------------");
                putchar('>');
                fflush(stdout);
        }

}

4.整合代码

 client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "config.h"
#include <fcntl.h>

static int count=0;


char *getLocalDir(char *cmd){
	char *p=NULL;
	//::char *strtok(char *str, const char *delim);
	p=strtok(cmd," ");
	p=strtok(NULL," ");
	return p;
}

int commandHandler(struct Message msg,int fd){
	
	char *filePath=NULL;
	char *dir;
	int file_fd;
	int cmdType=getCommandType(msg.cmd);
	char tempBuf[32];
	printf("commandHandler:msg.cmd is %s\n",msg.cmd);
	switch(cmdType){
		case LS:
		case PWD:
		case CD:
			count++;
			write(fd,&msg,sizeof(msg));
			break;

		case GET:
			count++;
			write(fd,&msg,sizeof(msg));
			break;
		case PUT:
			count++;
			strcpy(tempBuf,msg.cmd);
			//strtok函数会改变msg.cmd ,采用临时变量替代msg.cmd
			filePath=getLocalDir(tempBuf);
			printf("filePath is %s\n",filePath);
			if(access(filePath,F_OK)==-1){
				printf("%s doesn't exist!\n",filePath);
			}else{	
				
				//本地存在待和上传的文件
				file_fd=open(filePath,O_RDWR);	
				memset(msg.contentBuf,0,sizeof(msg.contentBuf));
				read(file_fd,msg.contentBuf,sizeof(msg.contentBuf));
				close(file_fd);		
				write(fd,&msg,sizeof(msg));
			}
			break;
		case LLS:
			count++;
			system("ls");
			break;

		case LCD:
			count++;
			dir=getLocalDir(msg.cmd);
			chdir(dir);
			break;
			
		case QUIT:
			count++;
			write(fd,&msg,sizeof(msg));
			close(fd);
			exit(-1);
	}
	return cmdType;
}


//指令转换为整形 供switch处理
int getCommandType(char *cmd){
	
     if(!strcmp("ls",cmd))      return LS;
     if(!strcmp("lls",cmd))     return LLS;
     if(!strcmp("pwd",cmd))     return PWD;
     if(!strcmp("quit",cmd))    return QUIT;

      if(strstr(cmd,"lcd"))      return LCD;
      if(strstr(cmd,"cd"))       return CD;
      if(strstr(cmd,"get"))      return GET;
      if(strstr(cmd,"put")) 	return PUT;
	
      return -1;
}

//处理服务端响应的信息
void serverMessageHandler(int fd,struct Message msg){

	struct Message recMsg;
	int nread=read(fd,&recMsg,sizeof(recMsg));
	//TCP协议某一方关闭连接时,read()返回0,读不到数据
	if(nread==0){
		puts("server disconnected...");
		exit(-1);
	}else if(recMsg.fileFlag==1){
		char *dir=getLocalDir(msg.cmd);
		printf("local fileName:%s\n",dir);
		int newFilefd=open(dir,O_RDWR|O_CREAT,0600);
		write(newFilefd,recMsg.cmd,strlen(recMsg.cmd));
		putchar('>');
		fflush(stdout);
	}else{
		puts("----------------------------");
		printf("\n%s\n",recMsg.cmd);
		puts("----------------------------");
		putchar('>');
		fflush(stdout);
	}	

}


int main(int argc,char **argv){
	//变量定义
	int c_fd;
	int n_read;

	struct sockaddr_in c_addr;
	struct Message msg;
	//参数个数判断
	if(argc!=3){
		
		printf("error:params expected 3 but %d given\n",argc);
		exit(-1);
	}	
	//初始化内存
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	//socket
	// int socket(int domain, int type, int protocol); 
	c_fd=socket(AF_INET,SOCK_STREAM,0);

	//ip and port
	c_addr.sin_family=AF_INET;
	c_addr.sin_port=htons(atoi(argv[2]));
	//字符串IP地址转换为一个32位的网络序列IP地址
	inet_aton(argv[1],&c_addr.sin_addr);


	int clen=sizeof(struct sockaddr_in);
	//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
		if(connect(c_fd,(struct sockaddr *)&c_addr,clen)==-1){
			perror("create client socket failed...\n");
			exit(-1);
		}
		//客户端连接到服务端
		printf("we have been connected with the server\n");
		int mark=0;
		
		while(1){
			memset(msg.cmd,0,sizeof(msg.cmd));
			if(mark==0){
				putchar('>');
			}
			mark=1;
			gets(msg.cmd);
			int commandType=commandHandler(msg,c_fd);
			printf("count=%d\n",count);
			if(commandType>IFGO){
				putchar('>');
				fflush(stdout);
				continue;
			}
			if(commandType==-1){
				puts("command not found");	
				putchar('>');
				fflush(stdout);	
				continue;
			}
	        	serverMessageHandler(c_fd,msg);
		
		}
				
	
	return 0;
}

server.c 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "config.h"
#include <fcntl.h>


char *getDestDir(char *cmd){
	char *p;
	//::char *strtok(char *str, const char *delim);
	p=strtok(cmd," ");
	p=strtok(NULL," ");
	return p;
}


void messageHandler(struct Message msg,int fd,char *cip){
	
	char *filePath=NULL;
	char dataBuf[4096]={0};
	int file_fd;
	FILE *fp;
	char *dir;
	int cmdType=getCommandType(msg.cmd);
	printf("-----------------------\ncommand:%s   cmdType: %d\n",msg.cmd,cmdType);	
	if(cmdType==-1){
		puts("command not found");
	}
	
	switch(cmdType){
		case LS:
		case PWD:
			 //FILE *popen(const char *command, const char *type);
			fp=popen(msg.cmd,"r"); 
			 //1:执行ls 或pwd 指令 2:返回执行结果对应的文件指针fp

			//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
			fread(msg.cmd,sizeof(msg.cmd),1,fp);  
			 //读取1次fp,读到msg.cmd中
			//ssize_t write(int fd, const void *buf, size_t count);
			write(fd,&msg,sizeof(msg));
			//把结果写到客户端(fd)
			break;

		
		case CD:
			dir=getDestDir(msg.cmd);
			printf("dir:%s\n",dir);
		 	// int chdir(const char *path);     
			chdir(dir);	//changes the current working directory of the calling process to the directory specified in path.
			break;

		case GET:
			filePath=getDestDir(msg.cmd);
			//文件不存在
			if(access(filePath,F_OK)==-1){
				strcpy(msg.cmd,"no such file!");
				write(fd,msg.cmd,sizeof(msg.cmd));
			}else{
			//文件存在
				msg.fileFlag=1;
				printf("filePath is %s\n",filePath);
				file_fd=open(filePath,O_RDWR);
				read(file_fd,dataBuf,sizeof(dataBuf));
				close(file_fd);
				strcpy(msg.cmd,dataBuf);
				write(fd,&msg,sizeof(msg));
			}
			break;
		case PUT:
			file_fd=open(getDestDir(msg.cmd),O_RDWR|O_CREAT,0644);
			//write(file_fd,msg.contentBuf,sizeof(msg.contentBuf));
			//写文件用strlen 区别看这里blog.csdn.net/sinat_25457161/article/details/48572033
			write(file_fd,msg.contentBuf,strlen(msg.contentBuf));
			close(file_fd);
			break;
			
		case QUIT:
			printf("connection[%s] disconnected...\n",cip);
			exit(-1);
	}
}


//指令转换为整形 供switch
int getCommandType(char *cmd){

	if(!strcmp("ls",cmd)) 
		return LS;
	if(!strcmp("quit",cmd))
		return QUIT;
	if(!strcmp("pwd",cmd))
		return PWD;
	if(strstr(cmd,"cd")!=NULL)
		return CD;
	if(strstr(cmd,"get")!=NULL)
		return GET;
	if(strstr(cmd,"put")!=NULL)
		return PUT;
	
	return -1;
}



int main(int argc,char **argv){
	//变量定义
	int s_fd;
	int c_fd;
	int n_read;
	char readBuf[128];

	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;
	struct Message msg;
	//参数个数判断
	if(argc!=3){
		
		printf("error:params expected 3 but %d given\n",argc);
		exit(-1);
	}	
	//初始化内存
	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	//socket
	// int socket(int domain, int type, int protocol); 
	s_fd=socket(AF_INET,SOCK_STREAM,0);

	//ip and port
	s_addr.sin_family=AF_INET;
	s_addr.sin_port=htons(atoi(argv[2]));
	//字符串IP地址转换为一个32位的网络序列IP地址
	inet_aton(argv[1],&s_addr.sin_addr);
	// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

	//listen
	listen(s_fd,8);

	//accept
 	// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);	
	int clen=sizeof(struct sockaddr_in);
	while(1){
		c_fd=accept(s_fd,(struct sockaddr*)&c_addr,&clen);
		
		if(c_fd==-1){
			perror("create client socket failed!\n");
		}
		//客户端ip
		char *cip=inet_ntoa(c_addr.sin_addr);
		//已连接客户端
		printf("a new connection [%s] has been established...\n",cip);
		
		//创建子进程处理
		if(0==fork()){
			while(1){
				memset(msg.cmd,0,sizeof(msg.cmd));
				//ssize_t read(int fd, void *buf, size_t count);
				n_read=read(c_fd,&msg,sizeof(msg));
				//TCP 协议中,当一方关闭了连接,另一方通过 read() 函数从套接字中读取数据时,会返回一个值为 0 的结果,表示当前没有数据可以读取
				if(n_read==0){
					puts("client disconnected...");
					break;
				//有效指令
				}else if(n_read>0){
					messageHandler(msg,c_fd,cip);
				}		
			}
		}
	}
	return 0;
}

5.演示

 diff 指令 查看ftp下的hello.c和ftp/folder/hello.c(从服务端get到客户端的) 内容一致

 

 diff 指令 查看ftp下的herman.iu((客户端put到服务端的)和ftp/folder/herman.iu 内容一致 

6.总结

        Llinux网络及系统文件编程暂时告一段落,这个课其实9月底就学完了,期间因为工作缘故加上个人惰性,迟迟没有学习,今天好不容易抽出时间整理了一下这个小demo,算是监督一下自己吧。有看到这部分的朋友,存在疑惑的点可以相互交流。

 2023-10-30  02:08:00   Herman

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现FTP客户端服务端需要涉及到网络编程、文件操作和多线程等知识,下面给出一个简单的示例代码,仅供参考。 服务器端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8888 #define MAXLINE 1024 int main() { int listenfd, connfd; struct sockaddr_in serveraddr, clientaddr; socklen_t clientlen = sizeof(clientaddr); char buf[MAXLINE]; int n; //创建套接字 listenfd = socket(AF_INET, SOCK_STREAM, 0); //初始化地址结构体 memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(PORT); //绑定地址 bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); //监听 listen(listenfd, 5); printf("server is running...\n"); while (1) { //接受客户端连接 connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen); printf("client %s:%d connected\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); //处理客户端请求 while ((n = read(connfd, buf, MAXLINE)) > 0) { buf[n] = '\0'; printf("recv: %s", buf); //处理上传文件请求 if (strcmp(buf, "put") == 0) { int fd; char filename[MAXLINE]; //接受文件名和文件大小 read(connfd, filename, MAXLINE); read(connfd, &n, sizeof(int)); //打开文件并写入数据 fd = open(filename, O_WRONLY | O_CREAT, 0666); while (n > 0) { int len = read(connfd, buf, MAXLINE); write(fd, buf, len); n -= len; } close(fd); printf("upload %s success\n", filename); } //处理下载文件请求 else if (strcmp(buf, "get") == 0) { int fd; char filename[MAXLINE]; off_t filesize; //接受文件名 read(connfd, filename, MAXLINE); //打开文件并读取文件大小 fd = open(filename, O_RDONLY); filesize = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); //发送文件大小 write(connfd, &filesize, sizeof(off_t)); //发送文件内容 while ((n = read(fd, buf, MAXLINE)) > 0) { write(connfd, buf, n); } close(fd); printf("download %s success\n", filename); } //处理退出请求 else if (strcmp(buf, "quit") == 0) { printf("client %s:%d disconnected\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); break; } //无效的请求 else { printf("invalid request\n"); } } close(connfd); } return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8888 #define MAXLINE 1024 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in serveraddr; char buf[MAXLINE]; int n; //创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); //初始化地址结构体 memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(PORT); inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr); //连接服务器 connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); while (1) { //读取用户输入 printf("> "); fgets(buf, MAXLINE, stdin); //处理上传文件请求 if (strncmp(buf, "put", 3) == 0) { int fd; char filename[MAXLINE]; off_t filesize; //获取文件名和文件大小 sscanf(&buf[4], "%s %ld", filename, &filesize); //发送上传文件请求和文件名、文件大小 write(sockfd, "put", 3); write(sockfd, filename, strlen(filename) + 1); write(sockfd, &filesize, sizeof(off_t)); //打开文件并发送文件内容 fd = open(filename, O_RDONLY); while ((n = read(fd, buf, MAXLINE)) > 0) { write(sockfd, buf, n); } close(fd); printf("upload %s success\n", filename); } //处理下载文件请求 else if (strncmp(buf, "get", 3) == 0) { int fd; char filename[MAXLINE]; off_t filesize; //获取文件名 sscanf(&buf[4], "%s", filename); //发送下载文件请求和文件名 write(sockfd, "get", 3); write(sockfd, filename, strlen(filename) + 1); //接受文件大小 read(sockfd, &filesize, sizeof(off_t)); //打开文件并接受文件内容 fd = open(filename, O_WRONLY | O_CREAT, 0666); while (filesize > 0) { n = read(sockfd, buf, MAXLINE); write(fd, buf, n); filesize -= n; } close(fd); printf("download %s success\n", filename); } //处理退出请求 else if (strncmp(buf, "quit", 4) == 0) { write(sockfd, "quit", 4); break; } //无效的请求 else { printf("invalid request\n"); } } close(sockfd); return 0; } ``` 需要注意的是,以上代码仅为示例代码,存在一些不足之处,如: - 文件名、文件大小等信息没有进行错误检查和处理; - 文件内容的发送和接收没有进行分段处理,可能会导致传输失败; - 服务端没有进行多线程处理,只能同时处理一个客户端请求等。 在实际的应用中,还需要根据具体需求进行完善和改进。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值