#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "socket_wrapper.h" #ifndef DEFAULT_MAX_LISTEN_SOCKET static const int MAX_LISTEN_SOCKET = 16; #else static const int MAX_LISTEN_SOCKET = DEFAULT_MAX_LISTEN_SOCKET; #endif #ifdef USE_SENDFILE #include #ifndef DEFAULT_SEND_FILE_CHUNK_SIZE const size_t SEND_FILE_CHUNK_SIZE = 0x100000; /*1MB*/ #else const size_t SEND_FILE_CHUNK_SIZE = DEFAULT_SEND_FILE_CHUNK_SIZE; /*1MB*/ #endif #endif #ifndef DEFAULT_SERVER_PORT static const in_port_t SERVER_PORT = 9091; #else static const in_port_t SERVER_PORT = DEFAULT_SERVER_PORT; #endif #ifndef DEFAULT_MAX_PATH_SIZE /*0 < x < MAX_PATH_SIZE*/ static const uint16_t MAX_PATH_SIZE = 256; #else static const uint16_t MAX_PATH_SIZE = DEFAULT_MAX_PATH_SIZE; #endif #ifndef DEFAULT_TIMEOUT static const int TIMEOUT = 5; #else static const int TIMEOUT = DEFAULT_TIMEOUT; #endif #ifndef DEFAULT_RESPONSE_REQUEST static const int RESPONSE_REQUEST = 3; #else static const int RESPONSE_REQUEST = DEFAULT_RESPONSE_REQUEST; #endif #ifdef USE_TRACE #include #include #include "timerhelper.h" enum{ #ifndef DEFAULT_TOP_TRACE_TIMER_ID Top_Trace_Timer_ID = CLOCK_REALTIME, #else Top_Trace_Timer_ID = DEFAULT_TOP_TRACE_TIMER_ID, #endif #ifndef DEFAULT_BOTTOM_TRACE_TIMER_ID Bottom_Trace_Timer_ID = CLOCK_THREAD_CPUTIME_ID #else Bottom_Trace_Timer_ID = DEFAULT_BOTTOM_TRACE_TIMER_ID #endif }; static inline void report_resolution() { struct timespec top_res,bottom_res; clock_getres(Top_Trace_Timer_ID,&top_res); clock_getres(Bottom_Trace_Timer_ID,&bottom_res); fprintf(stderr,"top res: %ld, bottom res: %ld\n",top_res.tv_nsec,bottom_res.tv_nsec); } #endif /*======== *Operation *========*/ /** * send user error message * 80 character limit * thread safe */ int send_fail(int sock,const char * msg){ struct TransferResult res; res.res = RES_USR_ERR; res.file_size = 0; res.err_number = 0; res.error_msg_size = strlen(msg); //os will be combining if tcp_autocorking emabled. if(send(sock,&res,sizeof(res),0) < 0){ perror("error msg send"); return -1; } if (send(sock,msg,res.error_msg_size,0) < 0){ perror("error msg send"); return -1; } return 0; } /** * send errno to client * thread safe */ int send_errno(int sock){ struct TransferResult r; r.res = RES_ERR; r.err_number = errno; r.file_size = 0; r.error_msg_size = 0; if(send(sock,&r,sizeof(r),0)){ perror("errno send"); return -1; } return 0; } /** * return fd, if success. otherwise, return -1. * thread safe */ int read_request(int sock,uint8_t * buf,size_t bufsize){ struct ReadOp p; int fd; ssize_t n = recv_until_byte(sock,&p,sizeof(p),TIMEOUT); if (n < 0){ if (n == -2) fprintf(stderr,"timeout!"); else perror("receive fail"); return -1; } if(bufsize <= ((size_t)p.file_url_size) + sizeof(p) + 1){ send_fail(sock,"buffer overflow"); return -1; } else if(p.file_url_size + 1 > MAX_PATH_SIZE){ send_fail(sock,"max path fail"); return -1; } else if(p.file_url_size == 0){ send_fail(sock,"filename zero fail"); return -1; } n = recv_until_byte(sock,buf,p.file_url_size,TIMEOUT); buf[p.file_url_size] = '\0'; //truncate fprintf(stdout,"str size: %d, request %s\n",p.file_url_size,buf); if(strchr((char *)buf,'/') != NULL){ send_fail(sock,"Illegal character /"); return -1; } fd = open((char *)buf,O_RDONLY); if(fd < 0){ send_errno(sock); close(fd); return -1; } return fd; } /** * send response to client * thread safe */ int send_response(int sock,int fd, uint8_t * buf, size_t bufsize){ struct TransferResult r; struct stat st; off_t offset = 0; ssize_t readed = 0; r.res = RES_OK; r.err_number = 0; r.error_msg_size = 0; if(fstat(fd,&st) < 0){ return send_errno(sock); } if(S_ISDIR(st.st_mode)){ return send_fail(sock,"is a directory"); } r.file_size = st.st_size; if(send(sock,&r,sizeof(r),0)<0){ perror("send fail"); return -1; } #ifdef USE_SENDFILE while (r.file_size != offset) { size_t count = SEND_FILE_CHUNK_SIZE < (r.file_size - offset) ? SEND_FILE_CHUNK_SIZE : (r.file_size - offset); if((readed = sendfile(sock,fd,&offset,count)) < 0){ perror("send file fail"); return -1; } } #else while (offset < r.file_size) { readed = bufsize < (r.file_size - offset) ? bufsize : r.file_size - offset; if(read(fd,buf,readed)<0){ perror("send response read fail"); return -1; } if(send(sock,buf,readed,0)<0){ perror("send response send fail"); return -1; } offset += readed; } #endif return 0; } /** print help message * arg `n`: executable name */ const char * help(const char * n){ const char * msg = "USASE : %s [Option] ...\n" "Options and arguments: \n" "-p port\t:set to port binding. couldn't set to 0\n" "-h\t:print help message.\n"; printf(msg,n); return msg; } /** parse arguments. * if return 0, success. otherwise arguments are invalid format.*/ int parse_args(int argc,const char * argv[] , in_port_t * port){ int pos = 1; const char * opt; while (pos < argc) { opt = argv[pos++]; if (strcmp(opt,"-h") == 0 || strcmp(opt,"--help") == 0) { help(argv[0]); return 2; } else if(strcmp(opt,"-p") == 0 || strcmp(opt,"--port") == 0){ if (pos < argc){ const char * value = argv[pos++]; *port = atoi(value); if (port == 0){ // either not number or zero fprintf(stderr,"port argument is either not number or zero\n"); return 2; } } else{ fprintf(stderr,"need argument\n"); return 2; //failed to find argument. } } } return 0; } static int sock; void safe_exit(){ close(sock); } int main(int argc, const char *argv[]){ uint8_t * buf; struct sockaddr_in addr; struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); int csock; int bufsize; int i; in_port_t binding_port_number = SERVER_PORT; if (argc > 1){ int d = parse_args(argc,argv,&binding_port_number); if(d != 0 ) return d; } #ifdef USE_TRACE report_resolution(); #endif sock = socket(AF_INET,SOCK_STREAM,0); atexit(safe_exit); if(sock < 0){ perror("sock create fail"); return 1; } else { int option = 1; if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&option,sizeof(option)) < 0){ perror("setsockopt"); } } bufsize = getBufferSizeFrom(sock); buf = malloc(bufsize * sizeof(*buf)); if (buf == NULL){ fprintf(stderr,"lack of memory"); return 1; } addr.sin_addr.s_addr = htonl(INADDR_ANY); /*0.0.0.0 모든 네트워크 인터페이스에 묶임.*/ addr.sin_family = AF_INET; addr.sin_port = htons(binding_port_number); if(bind(sock, (struct sockaddr *)&addr,sizeof(addr)) < 0){ perror("bind failed"); return 1; } else { char ip_buf[INET_ADDRSTRLEN]; const char * msg = inet_ntop(AF_INET,&addr.sin_addr,ip_buf,sizeof(ip_buf)); assert(msg != NULL); fprintf(stderr,"server bind on %s:%d\n",msg ,binding_port_number); } if(listen(sock,MAX_LISTEN_SOCKET) < 0){ perror("listen failed"); return 1; } for (i = 0; i < RESPONSE_REQUEST; i++) { int fd, pid; char ip_buf[INET_ADDRSTRLEN]; const char * msg; int retval = 0; #ifdef USE_TRACE struct timespec ts_top_begin,ts_top_end, ts_bottom_begin, ts_bottom_end; #endif if((csock = accept(sock, (struct sockaddr *)&client_addr,&client_addr_len)) < 0){ free(buf); perror("accept error"); return 1; } msg = inet_ntop(AF_INET,&client_addr.sin_addr,ip_buf,sizeof(ip_buf)); #ifdef USE_TRACE clock_gettime(Top_Trace_Timer_ID,&ts_top_begin); #endif fprintf(stdout,"Connected on : %s:%d\n",msg == NULL ? "(null)" : msg , ntohs(client_addr.sin_port)); pid = fork(); if(pid == 0){ #ifdef USE_TRACE clock_gettime(Top_Trace_Timer_ID,&ts_top_end); clock_gettime(Bottom_Trace_Timer_ID,&ts_bottom_begin); #endif if((fd = read_request(csock,buf,bufsize)) > 0){ retval = send_response(csock,fd,buf,bufsize); close(fd); } else retval = fd; if(close(csock) < 0) perror("csock close error"); #ifdef USE_TRACE clock_gettime(Bottom_Trace_Timer_ID,&ts_bottom_end); struct timespec tophalf = timespec_sub(ts_top_end,ts_top_begin); struct timespec bottomhalf = timespec_sub(ts_bottom_end,ts_bottom_begin); fprintf(stderr,"top: %ld ns, bottom: %ld ns\n",tophalf.tv_nsec,bottomhalf.tv_nsec); #endif free(buf); return retval; } } for (i = 0; i < RESPONSE_REQUEST; i++) { int retval; int pid = wait(&retval); printf("[%d] %d: %d\n",i,pid,retval); } free(buf); return 0; }