基于tcp下的文件传输下载

  1 //*****客户端******
  2  /*功能:   (1) input>list       查看服务端目录内容
  3            (2) input>get files  下载文件
  4            (3) input>put file   上传文件*/
  5 
  6 #include <head.h>
  7 
  8 #define UPLOAD_FILE   1 
  9 #define DOWNLOAD_FILE 2
 10 #define FILE_YEXIST   3 
 11 #define FILE_NEXIST   4 
 12 #define LIST_DIR      5
 13 #define LIST_OVER     6
 14 
 15 //定义协议报头结构体
 16 typedef struct 
 17 {
 18     unsigned char  type;
 19     unsigned char  file_name[256];
 20     unsigned char  file_type;
 21     unsigned int   file_size;
 22 }__attribute__ ((__packed__))head_t;//取消结构体内存自动对齐
 23 
 24 //初始化客户端套接字
 25 int init_tcp_client_socket(const char *server_ip,const char *server_port,\
 26         const char *client_ip,const char *client_port)
 27 {
 28     int ret;
 29     int sockfd;
 30     struct sockaddr_in server_addr;
 31     struct sockaddr_in client_addr;
 32 
 33     sockfd = socket(AF_INET,SOCK_STREAM,0);
 34     if(sockfd < 0){
 35         perror("Fail to socket");
 36         exit(EXIT_FAILURE);
 37     }
 38     //该段代码实现固定客户端ip,port
 39     if(client_ip != NULL && client_port != NULL){
 40         client_addr.sin_family = AF_INET;
 41         client_addr.sin_port   = htons(atoi(client_port));
 42         client_addr.sin_addr.s_addr = inet_addr(client_ip);
 43         ret = bind(sockfd,(struct sockaddr *)&client_addr,sizeof(client_addr));
 44         if(ret < 0){
 45             perror("Fail to bind");
 46             exit(EXIT_FAILURE);
 47         }
 48     }
 49     //填充服务端地址、端口信息
 50     server_addr.sin_family = AF_INET;
 51     server_addr.sin_port   = htons(atoi(server_port));
 52     server_addr.sin_addr.s_addr = inet_addr(server_ip);
 53     //connect server
 54     ret = connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
 55     if(ret < 0){
 56         perror("Fail to connect");
 57         exit(EXIT_FAILURE);
 58     }
 59 
 60     return sockfd;
 61 }
 62 
 63 //上传
 64 int do_put(int sockfd,const char *filename)
 65 {
 66     int n;
 67     int fd;
 68     int ret;
 69     int count = 0;
 70     char buf[1024];
 71     unsigned long total_size; 
 72     head_t *head  = (head_t *)buf;//报头存放于栈上,此处用到了强转
 73 
 74     fd = open(filename,O_RDONLY);
 75     if(fd < 0){
 76         fprintf(stderr,"Fail to open %s : %s\n",filename,strerror(errno));
 77         goto error_open;
 78     }
 79 
 80     total_size = lseek(fd,0,SEEK_END);
 81     lseek(fd,0,SEEK_SET);
 82 
 83     //填充协议头信息,发送协议
 84     head->type = UPLOAD_FILE;
 85     strcpy(head->file_name,filename);
 86     head->file_type = '-';
 87     head->file_size = total_size;
 88     ret = send(sockfd,buf,sizeof(head_t),0);
 89     if(ret < 0){
 90         perror("Faio to send");
 91         goto error_send;
 92     }
 93 
 94     //发送文件内容
 95     while(n = read(fd,buf,sizeof(buf)))
 96     {
 97         ret = send(sockfd,buf,n,0);
 98         if(ret < 0){
 99             perror("Faio to send");
100             goto error_send;
101         }
102 
103         count += n;
104 
105         fprintf(stderr,"send %d bytes : %%%.2f\r",count, (float)count / total_size * 100);
106         usleep(500);
107     }
108     printf("\n");
109 
110     close(fd);
111     close(sockfd);//关闭写端,告知服务器没有数据可发送
112 
113     return 0;
114 
115 
116 error_send:
117     close(fd);
118 error_open:
119     close(sockfd);
120     return -1;
121 }
122 
123 //客户端下载文件:[1]请求服务器端要下载文件(发送协议)
124 //               [2]等待服务端结果
125 //               [3]如果文件存在,则创建文件,读套接字,写文件
126 int do_get(int sockfd,char *filename)
127 {
128     int n;
129     int ret;
130     int fd;
131     int count = 0;
132     char buf[1024];
133     unsigned long total_size;
134     head_t *head = (head_t *)buf;
135     
136     //填充协议内容,并发送
137     head->type = DOWNLOAD_FILE;
138     strcpy(head->file_name,filename);
139     head->file_type = '-';
140     head->file_size = 0;
141 
142     ret = send(sockfd,buf,sizeof(head_t),0);
143     if(ret < sizeof(head_t)){
144         perror("Fail to send");
145         goto error_handler;
146     }
147 
148     //等待服务端结果(接收服务端返送回来的协议头,判断请求文件是否存在)    
149     while(1)
150     {
151         n = recv(sockfd,buf + count ,sizeof(head_t) -count,0);//接收完整协议头,防止粘包
152         if(n <= 0){
153             goto err_recv;
154         }
155         
156         count += n;
157         if(count >= sizeof(head_t))
158             break;
159     }
160     //如果返送回来的信息不存在
161     if(head->type == FILE_NEXIST){
162         printf("You want download file:%s not exist !\n",filename);
163         goto error_handler;
164     }
165     
166     //如果文件存在,得到请求文件的大小
167     total_size = head->file_size;
168     //打开文件
169     fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
170     if(fd < 0){
171         fprintf(stderr,"Fail to open %s : %s\n",filename,strerror(errno));
172         goto error_handler;
173     }
174     
175     count = 0;
176     //循环接收,并写入
177     while(1)
178     {
179         n = recv(sockfd,buf,sizeof(buf),0);
180         if(n <= 0){
181             break;
182         }
183 
184         ret = write(fd,buf,n);
185         if(ret < 0)
186             break;
187         
188         count += n;
189         fprintf(stderr,"download %d bytes : %%%.2f\r",count, (float)count / total_size * 100);
190         usleep(500);//延时,stderr,让下载情况实时显现
191 
192         if(count >= total_size)//如果接收总字节大于文件总大小,跳出
193             break;
194     }
195     //刚好相等,下载完成,否则失败
196     if(count == total_size){
197         printf("download success!\n");
198     }else{
199         printf("download failed,download %d bytes!\n",count);
200         close(fd);
201         goto error_handler;
202     }
203 
204     close(fd);
205     close(sockfd);
206 
207     return 0;
208 
209 error_handler:
210     close(sockfd);
211 err_recv:
212     return -1;
213 }
214 
215 //列出服务端目录文件内容
216 int do_list(int sockfd)
217 {
218     int n;
219     int ret;
220     int fd;
221     int count = 0;
222     char buf[1024];
223     unsigned long total_size;
224     head_t *head = (head_t *)buf;
225     
226     //send head 
227     head->type = LIST_DIR;
228     ret = send(sockfd,buf,sizeof(head_t),0);
229     if(ret < sizeof(head_t)){
230         perror("Fail to send");
231         return -1;
232     }
233 
234     //wait sever result 
235     while(1)
236     {
237         count = 0;
238         //接收完整协议头
239         while(1)
240         {
241             n = recv(sockfd,buf + count ,sizeof(head_t) - count,0);
242             if(n <= 0){
243                 goto err_recv;
244             }        
245             count += n;
246             if(count >= sizeof(head_t))
247                 break;
248         }
249         //目录不存在
250         if(head->type == LIST_OVER){
251             break;
252         }
253         printf("%s\n",head->file_name);//打印目录下的文件名
254     }
255 
256     close(sockfd);
257     return 0;
258 
259 err_recv:
260     close(sockfd);
261     return -1;
262 }
263 
264 //执行函数
265 int do_task(int sockfd,char *string)
266 {
267     char *pcmd;
268     char *pname;
269     //分割字符串,第一次调用,传递要分割字符,对同一字符连续分割,第一个参数指定为NULL
270     pcmd  = strtok(string," ");
271     pname = strtok(NULL," ");//得到分割后的第二个字符串
272 
273     if(strcmp(pcmd,"list") == 0){
274         do_list(sockfd);
275     }else if(strcmp(pcmd,"get") == 0){
276         do_get(sockfd,pname);
277     }else if(strcmp(pcmd,"put") == 0){
278         do_put(sockfd,pname);
279     }else if(strcmp(pcmd,"quit") == 0){
280         return -1;
281     }else{
282         printf("Unknow cmd : %s\n",pcmd);
283     }
284 
285     return 0;
286 }
287 
288 
289 //./tcp_server  server_ip  server_port
290 int main(int argc, const char *argv[])
291 {
292     int ret = 0;
293     int sockfd;
294     char buf[256];
295 
296     if(argc < 3){
297         fprintf(stderr,"Usage : %s <server_ip> <server_port>\n",argv[0]);
298         return -1;
299     }
300     
301     while(1)
302     {
303         printf("input>");
304         fgets(buf,sizeof(buf),stdin);
305         buf[strlen(buf) - 1] = '\0';
306         
307         //init socket 
308         sockfd = init_tcp_client_socket(argv[1],argv[2],NULL,NULL);
309         ret = do_task(sockfd,buf);
310         if (ret == -1)
311             break;
312     }
313 
314     return 0;
315 }

  1 //******服务端******
  2 // 功能:  判断客服端请求的文件
  3 //        【1】打开文件,如果只读方式打开失败,协议回送客服端,文件不存在
  4 //            如果只读方式打开成功,告诉客户端文件存在
  5 //        【2】读取文件写到套接字中去
  6 
  7 
  8 #include <head.h>
  9 
 10 #define UPLOAD_FILE   1 
 11 #define DOWNLOAD_FILE 2
 12 #define FILE_YEXIST   3 
 13 #define FILE_NEXIST   4 
 14 #define LIST_DIR      5
 15 #define LIST_OVER      6
 16 
 17 typedef struct 
 18 {
 19     unsigned char  type;
 20     unsigned char  file_name[256];
 21     unsigned char  file_type;
 22     unsigned int   file_size;
 23 }__attribute__ ((__packed__))head_t;
 24 
 25 //初始化服务端套接字
 26 int init_tcp_server_socket(const char *ip,const char *port)
 27 {
 28     int ret;
 29     int sockfd;
 30     struct sockaddr_in server_addr;
 31 
 32     sockfd = socket(AF_INET,SOCK_STREAM,0);
 33     if(sockfd < 0){
 34         perror("Fail to socket");
 35         exit(EXIT_FAILURE);
 36     }
 37     
 38     server_addr.sin_family = AF_INET;
 39     server_addr.sin_port   = htons(atoi(port));
 40     server_addr.sin_addr.s_addr = inet_addr(ip);
 41     ret = bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
 42     if(ret < 0){
 43         perror("Fail to bind");
 44         exit(EXIT_FAILURE);
 45     }
 46 
 47     listen(sockfd,128);//监听
 48     printf("Listen ...\n");
 49 
 50     return sockfd;
 51 }
 52 
 53 //接收
 54 int tcp_recv_file(int sockfd,head_t *head)
 55 {
 56     int n;
 57     int fd;
 58     int ret;
 59     int count = 0;
 60     char buf[1024];
 61     //打开文件
 62     fd = open(head->file_name,O_WRONLY | O_CREAT | O_TRUNC,0666);
 63     if(fd < 0){
 64         fprintf(stderr,"Fail to open %s : %s\n",head->file_name,strerror(errno));
 65         return -1;
 66     }
 67     
 68     //接收文件内容
 69     while(1)
 70     {
 71         n = recv(sockfd,buf,sizeof(buf),0);
 72         if(n <= 0){
 73             break;
 74         }
 75 
 76         ret = write(fd,buf,n);//循环写入
 77         if(ret < 0){
 78             perror("Fail to write");
 79             break;
 80         }
 81 
 82         count += n;
 83         if(count >= head->file_size)//循环跳出条件
 84             break;
 85     }
 86     
 87     if(count == head->file_size){
 88         printf("Recv file complete\n");
 89     }else{
 90         printf("Recv file uncomplete ,recv %d bytes\n",count);
 91     }
 92 
 93     return 0;
 94 }
 95 
 96 //发送
 97 int tcp_send_file(int sockfd,head_t *head)
 98 {
 99     int ret;
100     int fd;
101     int n;
102     int count = 0;
103     char buf[1024];
104     unsigned long total_size;
105 
106     fd = open(head->file_name,O_RDONLY);//只读方式打开文件
107     if(fd < 0){
108         goto error_open;
109     }
110     
111     total_size = lseek(fd,0,SEEK_END);//获得文件大小
112     lseek(fd,0,SEEK_SET); //复位
113 
114     //文件能打开,说明文件存在,发送协议,给客户端做回复
115     head->type = FILE_YEXIST;i
116     head->file_size = total_size;
117     ret = send(sockfd,head,sizeof(head_t),0);
118     if(ret < sizeof(head_t)){
119         perror("Fail to send");
120         close(fd);
121         goto error_send;
122     }
123     
124     //发送文件内容
125     while(1)
126     {
127         n = read(fd,buf,sizeof(buf));//循环读入文件到缓冲区
128         if(n <= 0){
129             break;
130         }
131 
132         ret = send(sockfd,buf,n,0);//循环从缓冲区写入文件到套接字
133         if(ret < n){
134             break;
135         }
136 
137         count += n;
138         if(count >= total_size)
139             break;
140     }
141     
142     if(count == total_size){
143         printf("send success!\n");
144     }else{
145         printf("send failed ,send %d bytes\n",count);
146     }
147 
148     close(fd);
149     return 0;
150 
151 error_open://打开只读文件失败,说明文件不存在,则发送协议头,告知客户端文件不存在
152     head->type = FILE_NEXIST;
153     ret = send(sockfd,head,sizeof(head_t),0);
154     if(ret < sizeof(head_t)){
155         perror("Fail to send");
156     }
157 
158 error_send:
159     return -1;
160 }
161 
162 //列出目录下的文件
163 int do_list(int sockfd)
164 {
165     int ret;
166     DIR *pdir;
167     char buf[1024];
168     head_t *head = (head_t *)buf;
169     struct dirent *pdirent;
170    
171     pdir = opendir(".");//打开当前目录
172     if(pdir == NULL){
173         perror("Fail to opendir");
174         return -1;
175     }
176     
177     while(pdirent = readdir(pdir))//循环读取目录中的文件
178     {
179         //过滤掉隐藏文件
180         if(pdirent->d_name[0] == '.'){
181             continue;
182         }
183         //文件名写入到协议报头中
184         strcpy(head->file_name,pdirent->d_name);
185         ret = send(sockfd,buf,sizeof(head_t),0);
186         if(ret < 0){
187             perror("fail to send");
188             return -1;
189         }
190     }
191     
192     //加上报头类型,发送协议给客户端
193     head->type = LIST_OVER;
194     ret = send(sockfd,buf,sizeof(head_t),0);
195     if(ret < 0){
196         perror("fail to send");
197         return -1;
198     }
199 
200     return 0;
201 }
202 
203 //子线程与客户端交互(处理文件的发送和接收)
204 void *do_client(void *arg)
205 {
206     int n;
207     int count = 0;
208     char buf[1024];
209     int sockfd = *(int *)arg;//强转类型
210     head_t *head = (head_t *)buf;
211 
212     free(arg);
213     
214     //接收协议
215     while(1)
216     {
217         n = recv(sockfd,buf + count ,sizeof(head_t) - count,0);
218         if(n <= 0){
219             goto err_recv;
220         }
221         
222         count += n;
223         if(count >= sizeof(head_t))
224             break;
225     }
226 
227     switch(head->type)
228     {
229     case UPLOAD_FILE:
230         tcp_recv_file(sockfd,head);
231         break;
232 
233     case DOWNLOAD_FILE:
234         tcp_send_file(sockfd,head);
235         break;
236 
237     case LIST_DIR:
238         do_list(sockfd);
239     }
240 
241 
242 err_recv:
243     close(sockfd);    
244     pthread_exit(NULL);
245 }
246 
247 //接收客户端请求
248 int recv_client_request(int listen_sockfd)
249 {
250     int ret;
251     pthread_t tid;
252     int *pconnect_sockfd;
253     struct sockaddr_in peer_addr;
254     socklen_t addrlen = sizeof(struct sockaddr);//初始化值结果(必须)
255     //创建线程,让每个客户端有一个与之对应的线程
256     while(1){
257         pconnect_sockfd = (int *)malloc(sizeof(int));
258 
259         *pconnect_sockfd = accept(listen_sockfd,(struct sockaddr *)&peer_addr,&addrlen);
260         if(*pconnect_sockfd < 0){
261             perror("Fail to accept");
262             exit(EXIT_FAILURE);
263         }
264         
265         //create thread
266     
267         printf("--------------------------\n");
268         printf("connect_sockfd : %d\n",*pconnect_sockfd);
269         printf("Port : %d\n",ntohs(peer_addr.sin_port));
270         printf("Ip   : %s\n",inet_ntoa(peer_addr.sin_addr));
271         printf("--------------------------\n");
272         
273         ret = pthread_create(&tid,NULL,do_client,pconnect_sockfd);
274         if(ret != 0){
275             fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret));
276             exit(EXIT_FAILURE);
277         }
278 
279         pthread_detach(tid);//系统自动回收线程资源
280     }
281 
282     return 0;
283 }
284 
285 //./tcp_server  ip  port
286 int main(int argc, const char *argv[])
287 {
288     int listen_sockfd;
289 
290     if(argc < 3){
291         fprintf(stderr,"Usage : %s <ip> <port>\n",argv[0]);
292         return -1;
293     }
294 
295     listen_sockfd = init_tcp_server_socket(argv[1],argv[2]);
296     
297     recv_client_request(listen_sockfd);
298 
299     return 0;
300 }

 

posted @ 2015-12-11 13:46  一抹火焰  阅读(560)  评论(0编辑  收藏  举报