首先上原码:
#include <sys/types.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
int mftpd_main(int argc,char **argv);
int mftp_main(int argc,char **argv);
int main(int argc,char **argv)
{
char *tmp;
tmp = strrchr(argv[0],'/');
if(tmp != NULL)
argv[0] =tmp+1;
if(strcmp(argv[0],"mftpd")==0)
return mftpd_main(argc,argv);
else if(strcmp(argv[0],"mftp")==0)
return mftp_main(argc,argv);
else
fprintf(stderr,"%s: no such command.\n",argv[0]);
return -1;
}
#define PORT 5588
#define BUF_LEN 4096
void create_server();
int mftpd_main(int argc,char **argv)
{
create_server();
/*
switch(fork()){
case 0:
setsid();
switch(fork()){
case 0:
create_server();
break;
case -1:
perror("fork:");
return -1;
default:
exit(0);
}
break;
case -1:
perror("fork:");
return -1;
default:
exit(0);
}
*/
}
void s_push(int sockfd,char *cmd);
void s_pull(int sockfd,char *cmd);
void create_server()
{
struct sockaddr_in lcaddr,rmaddr;
int sockfd,rmaddr_len,rmsockfd;
ssize_t recv_len;
char buf[BUF_LEN];
lcaddr.sin_port = htons(PORT);
lcaddr.sin_addr.s_addr = INADDR_ANY;
lcaddr.sin_family = AF_INET;
bzero(lcaddr.sin_zero,sizeof(lcaddr.sin_zero));
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1){
perror("socket:");
return;
}
if(bind(sockfd,(struct sockaddr*)&lcaddr,sizeof(lcaddr))==-1){
perror("bind:");
close(sockfd);
return;
}
if(listen(sockfd,1)==-1){
perror("listen:");
close(sockfd);
return;
}
while(1){
rmsockfd = accept(sockfd,(struct sockaddr*)&rmaddr,&rmaddr_len);
if(rmsockfd==-1){
// perror("accept:");
fprintf(stderr,"accept error!\n");
continue;
}
recv_len = recv(rmsockfd,buf,BUF_LEN,MSG_WAITALL);
if(recv_len != BUF_LEN){
fprintf(stderr,"recieve header error:%s\n",buf);
close(rmsockfd);
continue;
}
if(strncmp(buf,"push",strlen("push"))==0)
s_push(rmsockfd,buf);
else if(strncmp(buf,"pull",strlen("pull"))==0)
s_pull(rmsockfd,buf);
else
fprintf(stderr,"no such command.\n");
close(rmsockfd);
}
}
void s_push(int sockfd,char *fn)
{
int fd,len;
off_t fsize;
struct stat fst;
do{
memcpy(&fst,fn+BUF_LEN-sizeof(fst),sizeof(fst));
fn += strlen("push");
while(*fn == ' ')fn++;
if(S_ISLNK(fst.st_mode)){
continue; //暂时不处理链接文件
}
if(S_ISDIR(fst.st_mode)){
printf("mkdir %s\n",fn);
mkdir(fn,fst.st_mode);
}
else if(S_ISREG(fst.st_mode)){
fd = open(fn,O_WRONLY|O_CREAT|O_TRUNC,fst.st_mode);
fsize = fst.st_size;
printf("recieve file %s\n",fn);
while(fsize>0){
len = recv(sockfd,fn,BUF_LEN<fsize?BUF_LEN:fsize,0);
if(len<0&&errno == EINTR)
continue;
if(len>0){
write(fd,fn,len);
fsize-=len;
continue;
}
}
close(fd);
}
}while(recv(sockfd,fn,BUF_LEN,MSG_WAITALL)==BUF_LEN);
}
void s_pull(int sockfd,char *fn)
{
int fd;
off_t fsize,offset=0;
ssize_t sndlen;
fn += strlen("pull ");
while(*fn == ' ')fn++;
fd = open(fn,O_RDONLY);
if(fd<0){
perror("open:");
return;
}
fsize = lseek(fd,0,SEEK_END);
if(fsize==(off_t)-1){
perror("lseek:");
close(fd);
return;
}
sndlen = sendfile(sockfd,fd,&offset,fsize);
if(sndlen != fsize)
perror("sendfile:");
close(fd);
}
void c_push(int sockfd,char *fn,char *rmfn);
void c_pull(int sockfd,char *fn);
int mftp_main(int argc,char **argv)
{
int sockfd;
struct sockaddr_in lcaddr,rmaddr;
char buf[BUF_LEN];
if(argc!=6){
fprintf(stderr,"%s:\n\tusage:%s ipaddr port push[pull] localfile[remotefile] remotefile[localfile]\n",argv[0],argv[0]);
return -1;
}
lcaddr.sin_family = AF_INET;
lcaddr.sin_port = 0;
lcaddr.sin_addr.s_addr = INADDR_ANY;
bzero(lcaddr.sin_zero,sizeof(lcaddr.sin_zero));
rmaddr.sin_family = AF_INET;
rmaddr.sin_addr.s_addr = inet_addr(argv[1]);
rmaddr.sin_port = htons(atoi(argv[2]));
bzero(rmaddr.sin_zero,sizeof(rmaddr.sin_zero));
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket:");
return -1;
}
if(connect(sockfd,(struct sockaddr*)&rmaddr,sizeof(rmaddr))==-1){
perror("connet:");
close(sockfd);
return -1;
}
if(strcmp(argv[3],"push")==0){
c_push(sockfd,argv[4],argv[5]);
}
else if(strcmp(argv[3],"pull")==0){
strcpy(buf,argv[3]);
strcat(buf," ");
strcat(buf,argv[4]);
if(send(sockfd,buf,BUF_LEN-1,0)!=BUF_LEN-1){
perror("send header failed:");
return -1;
}
c_pull(sockfd,argv[5]);
}
else
fprintf(stderr,"%s:command not implement.\n",argv[3]);
close(sockfd);
return 0;
}
void c_push(int sockfd,char *fn,char *rmfn)
{
int fd;
off_t fsize;
off_t offset=0;
struct stat fst;
char buf[BUF_LEN];
if(stat(fn,&fst)==-1){
perror("stat:");
return;
}
if(S_ISLNK(fst.st_mode)){
return;
}
strcpy(buf,"push");
strcat(buf,rmfn);
if(strlen(buf)+sizeof(fst)>BUF_LEN){
fprintf(stderr,"length of path too long!\n");
return;
}
memcpy(buf+BUF_LEN-sizeof(fst),&fst,sizeof(fst));
if(send(sockfd,buf,BUF_LEN,0)!=BUF_LEN){
perror("send header failed:");
return;
}
if(S_ISDIR(fst.st_mode)){
DIR* pdir;
struct dirent *cf;
pdir = opendir(fn);
if(pdir==NULL){
perror("opendir");
return;
}
while((cf = readdir(pdir))!=NULL){
if(strcmp(cf->d_name,".")==0||strcmp(cf->d_name,"..")==0)
continue;
char *rmsubf = (char*) malloc(strlen(rmfn)+strlen(cf->d_name)+2);
char *subf = (char*) malloc(strlen(fn)+strlen(cf->d_name)+2);
if(subf==NULL||rmsubf==NULL){
fprintf(stderr,"no enough memory!\n");
free(subf);
free(rmsubf);
return;
}
strcpy(subf,fn);
strcat(subf,"/");
strcat(subf,cf->d_name);
strcpy(rmsubf,rmfn);
strcat(rmsubf,"/");
strcat(rmsubf,cf->d_name);
c_push(sockfd,subf,rmsubf);
free(subf);
free(rmsubf);
}
closedir(pdir);
}
else if(S_ISREG(fst.st_mode)){
fd = open(fn,O_RDONLY);
if(fd <0){
perror("open:");
return;
}
fsize = lseek(fd,0,SEEK_END);
printf("sending file %s...\n",fn);
if(sendfile(sockfd,fd,&offset,fsize)!=fsize){
perror("sendfile:");
}
close(fd);
}
}
void c_pull(int sockfd,char *fn)
{
int fd;
ssize_t len;
char buf[BUF_LEN];
fd = open(fn,O_WRONLY|O_CREAT|O_TRUNC,0666);
if(fd<0){
perror("open:");
return;
}
while(1){
len = recv(sockfd,buf,BUF_LEN,0);
if(len == 0){
break;
}
else if(len>0){
if(write(fd,buf,len)==-1)
perror("write:");
continue;
}
else if(errno==EINTR)
continue;
break;
}
close(fd);
}
仅需分析push那部分(pull还没改)。实在不知道什么情况了。传少量文件可以,但是传很多,比如某个工程的源代码时,就会出现错误。我是在本地127.0.0.1测试的。
编译后为mftp,创建mftpd软链接指向mftp,然后运行mftpd,再./mftp 127.0.0.1 5588 push ~/somefolder ~/newfolder