간단하게짠 ftp프로그램인데요 문제가좀있어서요. 좀 도와주세요
글쓴이: taiha / 작성시간: 화, 2003/02/11 - 1:46오전
다른건 다 잘되는데요 get 명령으로 16kB 이상의 데이터를 받아오면
파일이 이상해저버려요..
그리고 실행파일의 경우는 파일 크기가 줄어저 생기는데 실행이 아주잘(ㅡ.ㅡ;)
되거든요..
왜근런지 못잡겠어요.. 소켓프로그램은 잘못해서 ㅜ.ㅜ
Redhat 7.3 에서 프로그램 한거에요
사용법은
get a.c b.c
ls
pwd
이런 식이에요
Makefile
all: gcc ftpcli.c -o ftpcli gcc ftpserv.c -o ftpserv -lcrypt
net.h
#define MYEXIT(str) {perror(str); exit(0);} #define STR_MAX 512 #define PORT_COMM 56161 #define NUM_CMD 10 #define EOT "^EOT^" #define LEN_EOT 5 #define OPK "^OPK^" // #define LEN_OPK 5 #define OPF "^OPF^" //명령 수행에 오류생김 #define LEN_OPF 5 unsigned int size_tran; /* //네트웍 바이트오더 고려한 코드 #define MYRECV( fd, buf){ read( fd, &size_tran, sizeof(int) ); size_tran = ntohl(size_tran); read( fd, buf, size_tran ); } #define MYSEND( fd, buf, size){ size_tran = htonl(size); write( fd, &size_tran, sizeof(int) ); size_tran = ntohl(size_tran); write( fd, buf, size_tran );} #define SENDSTR( fd, buf ){ size_tran = htonl(strlen(buf)+1); write( fd, &size_tran, sizeof(int) ); size_tran = ntohl(size_tran); write( fd, buf, size_tran );} */ //네트웍 바이트오더 고려안한 코드 #define MYRECV( fd, buf){ if( !read( fd, &size_tran, sizeof(int) ) )exit(0); read( fd, buf, size_tran ); } #define MYSEND( fd, buf, size){ size_tran = size; write( fd, &size_tran, sizeof(int) ); write( fd, buf, size_tran ); } #define SENDSTR( fd, buf ){ size_tran = strlen(buf)+1; write( fd, &size_tran, sizeof(int) ); write( fd, buf, size_tran ); }
ftpserv.c
#include <pwd.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #define _XOPEN_SOURCE #include <unistd.h> #include <signal.h> #include <netinet/in.h> #include <arpa/inet.h> #include <dirent.h> #include <sys/stat.h> #include <fcntl.h> #include "net.h" void service( int ); void sig_chld(int); void myls(int); void mycd(int); void mypwd( int ); void myrename( int ); void myput( int ); void myget( int ); void myrm( int ); int login( int ); int fd_sock_serv, fd_sock_cli; int len_addr_serv, len_addr_cli; int num_arg; struct sockaddr_in addr_serv, addr_cli, addr_data; struct passwd *pwent; char arg[2][STR_MAX]; int main() { int error; // if( fork() != 0 ) // exit(0); //안해도 별지장없을거같네쩝. chdir( "/" ); fd_sock_serv = socket(AF_INET, SOCK_STREAM, 0); if(fd_sock_serv == -1) MYEXIT("socket()") addr_serv.sin_family = AF_INET; addr_serv.sin_addr.s_addr = htonl(INADDR_ANY); addr_serv.sin_port = htons(PORT_COMM); len_addr_serv = sizeof(addr_serv); error = bind( fd_sock_serv, (struct sockaddr *)&addr_serv, len_addr_serv); if(error == -1) MYEXIT("bind()") error = listen(fd_sock_serv, 5); if(error == -1) MYEXIT("listen()"); for( ; ; ) { fd_sock_cli = accept( fd_sock_serv, (struct sockaddr *)&addr_cli, &len_addr_cli ); if(fd_sock_cli == -1) MYEXIT("accept() ") if( fork() == 0 ) //자식 프로세스인 경우 서비스를 시작 { service( fd_sock_cli ); exit(0); } else { signal( SIGCHLD, sig_chld); close(fd_sock_cli); } } close(fd_sock_serv); return 0; } /************************* sig_child() 함수 ******************************/ void sig_chld(int signo) { wait(); signal( SIGCHLD, sig_chld); } /************************** login() ***************************************/ int login( int fd_sock_cli ) { FILE *fd; int i; int index=0; char id[STR_MAX], passwd[STR_MAX]; char tmppw[STR_MAX], tmp[STR_MAX]; char shadow[STR_MAX]; for( i = 0 ; i < 3 ; i++ ) { MYRECV( fd_sock_cli, id ); MYRECV( fd_sock_cli, passwd ); pwent = getpwnam( id ); if( pwent == NULL ) { MYSEND( fd_sock_cli, OPF, LEN_OPF ); SENDSTR( fd_sock_cli, "사용자가 존재하지않음" ); continue; } if( strlen(pwent->pw_passwd) == 1 ) //shadow 쓰는경우. 아 진짜 힘들다 { fd = fopen("/etc/shadow", "r"); if( fd == NULL ) MYEXIT(" /etc/shadow 요거 여는데 에러나네ㅡ.ㅡ;;"); while(1) { fgets( tmppw, STR_MAX-1, fd ); for( i = 0, index = 0; ; i++, index++) { if( tmppw[i] ==':' ) //id부분을 다읽음 { index = 0; if( strncmp( tmp, pwent->pw_name, strlen(pwent->pw_name) ) == 0) //id 일치하면 { for( i++ ; ;i++, index++) { if( tmppw[i] == ':' ) //password 다읽음 { tmp[index] = '\0'; goto getpw; } tmp[index] = tmppw[i]; } } else break; } tmp[index] = tmppw[i]; } } } getpw: if( tmp[0] == '$' ) //MD5 인경우 { if( strcmp( (char *)crypt(passwd, tmp ), tmp) == 0 ) //비번이 일치하면 { MYSEND( fd_sock_cli, OPK, LEN_OPK ); return 1; } else { MYSEND( fd_sock_cli, OPF, LEN_OPF ); SENDSTR( fd_sock_cli, "비밀번호틀림. 바부팅이!" ); continue; } } else //DES인 경우 { char salt[3]; salt[0] = tmp[0]; salt[1] = tmp[1]; salt[2] = '\0'; if( strcmp( (char *)crypt(passwd, salt), tmp) == 0 ) //비번이 일치하면 { MYSEND( fd_sock_cli, OPK, LEN_OPK ); return 1; } else { MYSEND( fd_sock_cli, OPF, LEN_OPF ); SENDSTR( fd_sock_cli, "비밀번호틀림. 바부팅이!" ); continue; } } } return 0; } /**************************** service() 함수 *******************************************/ void service( int fd_sock_cli) { int cmd; int i; for( i = 0; i < 3; i++) { if( login( fd_sock_cli) ) //로그인 성공 goto ok; } exit(0); ok: if( setgid( pwent->pw_gid ) == -1) exit(0); if( setuid( pwent->pw_uid ) == -1) exit(0); chdir( pwent->pw_dir ); while(1) { cmd = -1; num_arg = -1; MYRECV( fd_sock_cli, &cmd ); //명령 번호르 읽어옴 MYRECV( fd_sock_cli, &num_arg); //인자의 수를 읽어옴 if( num_arg == 1) { MYRECV( fd_sock_cli, arg[0] ); } else { if( num_arg == 2 ) { MYRECV( fd_sock_cli, arg[0]); MYRECV( fd_sock_cli, arg[1]); } } switch( cmd ) { case 0: //ls myls(fd_sock_cli); break; case 1: //cd mycd(fd_sock_cli); break; case 2: //rename myrename(fd_sock_cli); break; case 3: //pwd mypwd(fd_sock_cli); break; case 4: //rm myrm(fd_sock_cli); break; case 5: //get myget(fd_sock_cli); break; case 6: //put myput(fd_sock_cli); break; } for( i =0; i < STR_MAX; i++) { arg[0][i] = '\0'; arg[1][i] = '\0'; } } } /**************************** myls() ****************************/ void myls(int fd_sock_cli) { DIR *p_dir; struct dirent *dir_ent; struct stat stat_buf; char msg[STR_MAX]; if( (p_dir = opendir(".")) == NULL ) { MYSEND( fd_sock_cli, OPF, LEN_OPF); SENDSTR( fd_sock_cli, "can't open directory" ); return; } else MYSEND( fd_sock_cli, OPK, LEN_OPK); while( dir_ent = readdir(p_dir) ) { stat(dir_ent->d_name,&stat_buf); if( S_ISDIR(stat_buf.st_mode) ) { sprintf( msg,"%s/", dir_ent->d_name, strlen(dir_ent->d_name) ); SENDSTR( fd_sock_cli, msg ); } else { sprintf( msg,"%s", dir_ent->d_name, strlen( dir_ent->d_name)); SENDSTR( fd_sock_cli, msg ); } } closedir(p_dir); MYSEND( fd_sock_cli, EOT, LEN_EOT); } /**************************** mycd() ****************************/ void mycd(int fd_sock_cli) { if( chdir(arg[0]) == -1 ) { MYSEND( fd_sock_cli, OPF, LEN_OPF); SENDSTR(fd_sock_cli, "directory가 없는디 ㅡ.ㅡ?" ); return; } else MYSEND( fd_sock_cli, OPK, LEN_OPK); MYSEND( fd_sock_cli, EOT, LEN_EOT); } /**************************** myget() ****************************/ void myget( int fd_sock_cli) { int fd_get; int readn; struct stat stats; mode_t mode; char buf[STR_MAX]; fd_get = open( arg[0], O_RDONLY); if( fd_get == -1) //파일이없는경우 { MYSEND( fd_sock_cli, OPF, LEN_OPF); SENDSTR( fd_sock_cli, "저쪽 파일여는데 실패해부렀네 @.@" ); return; } MYSEND( fd_sock_cli, OPK, LEN_OPK); //파일 열기 성공 fstat( fd_get, &stats); mode = htonl(stats.st_mode); MYSEND( fd_sock_cli, &mode, sizeof( mode_t ) ); //파일모드 전송 MYRECV( fd_sock_cli, buf ); if( strncmp( buf, OPF, LEN_OPF) == 0 ) { close(fd_get); return; } while(readn = read( fd_get , buf, STR_MAX)) { MYSEND( fd_sock_cli, buf, readn ); } MYSEND( fd_sock_cli, EOT, LEN_EOT ); close(fd_get); } /**************************** myput() ****************************/ void myput( int fd_sock_cli) { int fd_put; mode_t mode; char buf[STR_MAX]; MYRECV( fd_sock_cli, buf ); //local 파일 열기 성공여부를 받음 if( strncmp( buf, OPF, LEN_OPF ) == 0 ) return; unlink( arg[1]); //일단 지움 MYRECV( fd_sock_cli, &mode); //파일 모드를 받음 mode = ntohl(mode); fd_put = open( arg[1], O_WRONLY|O_CREAT|O_TRUNC, mode); //파일을 염. if( fd_put == -1 ) { MYSEND( fd_sock_cli, OPF, LEN_OPF); SENDSTR( fd_sock_cli, "저짝 파일 여는데 실패해부러써야.." ); return; } MYSEND( fd_sock_cli, OPK, LEN_OPK); while(1) { MYRECV( fd_sock_cli, buf); if( strncmp( buf, EOT, LEN_EOT ) == 0 ) { close( fd_put); return; } write( fd_put, buf, size_tran ); } } /**************************** myrename() ****************************/ void myrename(int fd_sock_cli) { if( rename( arg[0], arg[1] ) == -1 ) { MYSEND( fd_sock_cli, OPF, LEN_OPF); SENDSTR(fd_sock_cli,"파일의 이름을 변경할수 없습니다" ); return; } else { MYSEND( fd_sock_cli, OPK, LEN_OPK); SENDSTR(fd_sock_cli,"파일의 이름이 변경 되었습니다." ); } MYSEND( fd_sock_cli, EOT, LEN_EOT); } /**************************** mypwd() ****************************/ void mypwd(int fd_sock_cli) { char dir_name[STR_MAX] = "\0", msg[STR_MAX] = "\0"; if( getcwd(dir_name,STR_MAX) == NULL ) //현재 디렉토리명을 얻을수 없으면 에러를 출력 { MYSEND( fd_sock_cli, OPF, LEN_OPF); SENDSTR(fd_sock_cli,"디렉토리가 없는가본디..."); return; } else { MYSEND( fd_sock_cli, OPK, LEN_OPK); sprintf( msg, "저쪽 directory는 %s.", dir_name, strlen(dir_name) ); SENDSTR(fd_sock_cli, msg ); } MYSEND( fd_sock_cli, EOT, LEN_EOT ); } /**************************** myrm() ****************************/ void myrm(int fd_sock_cli) { if( unlink( arg[0] ) == -1) { MYSEND( fd_sock_cli, OPF, LEN_OPF); SENDSTR( fd_sock_cli, "파일 제거에 실패하였습니다" ); return; } else { MYSEND( fd_sock_cli, OPK, LEN_OPK); SENDSTR( fd_sock_cli, "파일을 제거하였습니다." ); } MYSEND( fd_sock_cli, EOT, LEN_EOT ); }
ftpcli.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <dirent.h> #include <string.h> #include <errno.h> #include "net.h" int token( char a[]); void mylcd(); void mycmd(); void myget( int ); void myput( int ); int login(void); //반환값은 0,1(실패,성공) int num_arg; int fd_sock; char cmd_list[NUM_CMD][10] = {"ls", "cd", "rename", "pwd", "rm", "get", "put", "lcd", "quit", "!" }; char cmd[3][STR_MAX+1]; struct sockaddr_in addr_serv; struct hostent *host_ent_serv; int main( int argc, char *argv[]) { int i; int error; int len_addr_serv; int cmd_num; char msg[STR_MAX]; char buf[STR_MAX]; if( argc != 2 ) { MYEXIT("ftpcli [host] 요거이 표준이제.."); } fd_sock = socket( AF_INET, SOCK_STREAM, 0 ); if( fd_sock == -1) MYEXIT("SOCKET() "); host_ent_serv = gethostbyname( argv[1] ); if(host_ent_serv == NULL ) MYEXIT("gethostbyname() error "); addr_serv.sin_family = AF_INET; addr_serv.sin_addr = *( (struct in_addr *)host_ent_serv->h_addr ); addr_serv.sin_port = htons(PORT_COMM); len_addr_serv = sizeof( addr_serv ); error = connect( fd_sock, (struct sockaddr *)&addr_serv, len_addr_serv ); if( error == -1 ) MYEXIT("connect() "); printf(" %s 서버에 연결되었습니다 \n" , argv[1]); for( i = 0; i < 3 ; i++) { if( login() ) goto login_ok; } printf("접속 실패\n"); exit(0); login_ok: while(1) { printf("MyFtp>> "); fgets( buf, STR_MAX-1, stdin); if( buf[0] == '\n' ) continue; buf[strlen(buf)-1] = '\0'; //-1을 한 것은 입력에서의 '\n'을 없애기위함 cmd_num = token( buf); if( cmd_num == -1) //잘못된 명령이면 { printf( "%s is bad command\n" ,cmd[0]); continue; } MYSEND( fd_sock , &cmd_num, sizeof(int) ); MYSEND( fd_sock , &num_arg, sizeof(int) ); if( num_arg == 1) { SENDSTR( fd_sock, cmd[1]); } else { if( num_arg == 2 ) { SENDSTR( fd_sock, cmd[1] ); SENDSTR( fd_sock, cmd[2] ); } } switch( cmd_num ) { case 0: //ls case 1: //cd case 2: //rename case 3: //pwd case 4: //rm MYRECV( fd_sock, msg ); if( strncmp( msg, OPF, LEN_OPF ) == 0 ) { MYRECV( fd_sock, msg ); printf("%s\n", msg); break; } while(1) { for( i =0; i < STR_MAX; i++) msg[i] = '\0'; MYRECV( fd_sock, msg ); if( strlen(msg) == LEN_EOT && strncmp( msg, EOT, LEN_EOT ) == 0 ) { break; } printf("%s\n", msg); } break; case 5: //get myget( fd_sock); break; case 6: //put myput( fd_sock ); break; case 7: mylcd(); break; case 8 : //quit close(fd_sock); return 0; break; case 9 : // ! mycmd(); break; } } close(fd_sock); return 0; } /************************ login() *************************************/ //반환값은 0(실패), 1(성공) int login(void) { char id[STR_MAX], passwd[STR_MAX]; char buf[STR_MAX]; printf("ID : "); fgets( id, STR_MAX-1, stdin); id[strlen(id)-1] = '\0'; //-1을 한 것은 입력에서의 '\n'을 없애기위함 printf("PASSWORD : "); fgets( passwd, STR_MAX-1, stdin); passwd[strlen(passwd)-1] = '\0'; //-1을 한 것은 입력에서의 '\n'을 없애기위함 SENDSTR( fd_sock, id ); SENDSTR( fd_sock, passwd ); MYRECV( fd_sock, buf ); if( strncmp( buf, OPF, 5 ) == 0 ) { MYRECV( fd_sock, buf ); printf("%s\n", buf); return 0; } return 1; } /******************************** token() **********************************/ //올바른 명령이면 해당 명령의 번호, 잘못된 명령이면 -1을 반환 int token( char buf[] ) { int i, index= 0, flag; for( i =0; i < STR_MAX; i++) { cmd[0][i] = '\0'; cmd[1][i] = '\0'; cmd[2][i] = '\0'; } num_arg = 0; flag = 0; //공백이면 0, 아니면 1 for( i = 0; buf[i] != '\0'; i++) { if( num_arg > 2) return -1; if( buf[i] != ' ' && buf[i] != '\0' ) //공백또는 스트링의 끝이 아니면 { cmd[num_arg][index] = buf[i]; index++; flag = 1; continue; } if( flag != 0 ) //이전 문자가 공백이 아니면 카운트를 증가 { flag = 0; cmd[num_arg][index] = '\0'; num_arg++; index = 0; } } for( i = 0; i < NUM_CMD; i++) //입력받은 명령어의 번호 return { if( strcmp( cmd_list[i], cmd[0] ) == 0 ) return i; } return -1; } /**************************** mylcd() ****************************/ void mylcd() { if( chdir( cmd[1] ) == -1 ) { printf("디렉토리 변경이 실패 하였습니다\n"); } } /**************************** mycmd() ****************************/ void mycmd() { system( cmd[1] ); } /****************************** myget() *************************/ void myget(int fd_sock) { mode_t mode; int fd_get; char buf_get[STR_MAX]; MYRECV( fd_sock, buf_get ); //받을 파일을 열었는지 확인 if( strncmp( buf_get, OPF, LEN_OPF) == 0 ) //받을 파일 열기 실패 { MYRECV( fd_sock, buf_get ); printf("%s\n", buf_get); return; } unlink( cmd[2] ); //안묻고걍지움 MYRECV( fd_sock, &mode); //파일 모드를 받음 mode = ntohl(mode); fd_get = open( cmd[2], O_WRONLY|O_CREAT|O_TRUNC, mode); //파일 염 if( fd_get == -1 ) { printf("Can't open local file\n"); close(fd_get); MYSEND(fd_sock, OPF, LEN_OPF); return; } MYSEND(fd_sock, OPK, LEN_OPK); while(1) { MYRECV( fd_sock, buf_get ); if( strncmp( buf_get, EOT, LEN_EOT ) == 0 ) { close( fd_get); return; } write( fd_get, buf_get, size_tran ); } close(fd_get); } /********************************* myput() **************************/ void myput( int fd_sock ) { int fd_put; int readn; struct stat stats; mode_t mode; char buf_put[STR_MAX]; fd_put = open( cmd[1], O_RDONLY); if( fd_put == -1) { MYSEND(fd_sock, OPF, LEN_OPF ); printf("Can't open file\n"); return; } MYSEND(fd_sock, OPK, LEN_OPK ); //local 파일 열기 성공 fstat( fd_put, &stats); mode = htonl(stats.st_mode); MYSEND( fd_sock, &mode, sizeof( mode_t ) ); //파일모드 전송 MYRECV( fd_sock, buf_put ); if( strncmp( buf_put, OPF, 5) == 0) //서버측 파일열기 실패 { MYRECV( fd_sock, buf_put ); printf("%s\n", buf_put); return; } while( readn = read( fd_put , buf_put, STR_MAX)) { MYSEND( fd_sock, buf_put, readn ); } MYSEND( fd_sock, EOT, LEN_EOT); close(fd_put); }
Forums:
송수신 Macro를 너무 신뢰하고 계신듯합니다.
두번송신에 두번 수신하는 모습으로 되어있는데, 사실 이렇게 되려면,
처음 송신한 길이부분이 확실히 전송되었는지, 확실히 수신되었는지를
확인시켜줘야합니다.
즉, 두번째에 있는 read,write를 보면,
송신하는 녀석의 return 값이 원하는 만큼 송신되지 않을 수 도 있고,
수신하는 녀석의 return 값이 기대하는 만큼 오지 않을 수 도 있습니다.
두 경우에 대해서 원하는 만큼이 될 때까지 loop을 돌아야하구요...
loop을 돌다가 connection이 끊어지는 것도 처리해줘야하지요....
macro보다는 함수로 만드는 것이 좋겠네요.
제가 자주하는 말인데, TCP/IP에서처럼 연결지향 전송에서는 송신한 수와 수신한 수는 일치하지 않을 수 있고, 송신한 양과 수신한 양이 일치하는 것을 전제로 합니다.
---
http://coolengineer.com
다른부분은 문제가없는데 get 에서만 문제가 생기거든요
전송량을 확인하기위해 매크로뒷부분에
MYRECV {..... printf("%d\n",size_tran}; }
이런식으로해서 전송된 크기를 보면 서버측의 전송크기는 정확한데
클라이언트즉의 전송크기가 16kB 전송 이후로 이상한값이 들어가버립니다.
put 의 경우에는 위의 문제가 없고요. 다른경우도 마찬가지 입니다
님의 말씀대로 해보긴 하겠습니다. 근데 get 에서만 문제가 생기는 이유는 정말 모르겠네요
매크로를 readn, writen으로 바꿔보세요.
그렇게 하시고,
if ( n != readn ( sd, buff, n ))
{
에러처리;
}
이런식으로 사용하세요. 물론 이것도 문제는 있을 수 있지만....
큰 문제는 없을 겁니다.
댓글 달기