C語言基於socket的檔案傳輸(可迴圈傳送多個檔案)
阿新 • • 發佈:2019-02-15
基本簡介:
本次檔案傳輸的實現主要是通過客戶端向伺服器傳送下載請求,然後在伺服器中找到對應的檔案並開啟檔案,再繼續向客戶端傳送檔案,而客戶端就在不停的接收。這是因為檔案可能比較大,一個緩衝陣列只能儲存一部分檔案內容,因此伺服器得不斷從檔案中讀取內容併發給客戶端,而客戶端得不停的迴圈接收。
但是在事先,得將相應要傳送的檔案(照片,音訊,視訊等)儲存在伺服器相應的目錄下。而這個是不符合實際要求的,通常來講,是應該將客戶端1的檔案傳送給客戶端2,而伺服器僅僅只是起到一箇中轉站的作用,即檔案應該事先儲存在客戶端1下。這裡我們只是完成檔案傳輸的相應功能就行了,就不在計較這些啦。因為只要你理解了這一塊,可以根據自己的實際需要,在進行修改。
具體編譯:
gcc server.c -o server -lpthread //這是因為在伺服器中加入了執行緒函式,所以編譯的時候需要加上 -lpthread 。
gcc client.c -o client
記住一定要先執行伺服器,在執行客戶端。在客戶端執行的時候回提醒你輸入伺服器對應的pc ip,如實輸入就行啦。如果是在本機pc上進行測試的話,也可以輸入0.0.0.0 。
server.c:
#include <stdio.h> #include <netdb.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/types.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <netinet/in.h> #include <pthread.h> #define portnum 12345 #define FILE_SIZE 500 #define BUFFER_SIZE 1024 void *net_thread(void * fd); int main() { //初始化套接字 int server_fd=socket(AF_INET,SOCK_STREAM,0); if(-1==server_fd) { perror("socket"); exit(1); } //繫結埠和ip; struct sockaddr_in server_addr; //struct sockaddr_in為結構體型別 ,server_addr為定義的結構體 server_addr.sin_family=AF_INET; //Internet地址族=AF_INET(IPv4協議) server_addr.sin_port=htons(portnum); //將主機位元組序轉化為網路位元組序 ,portnum是埠號 (server_addr.sin_addr).s_addr=htonl(INADDR_ANY);//IP地址 if(-1==bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr))) //套接字與埠繫結 { perror("bind"); exit(6); } //開啟監聽 if(-1==listen(server_fd,5)) //5是最大連線數,指伺服器最多連線5個使用者 { perror("listen"); exit(7); } while(1) { struct sockaddr_in client_addr; int size=sizeof(client_addr); int new_fd=accept(server_fd,(struct sockaddr *)&client_addr,&size); //server_fd伺服器的socket描述字,&client_addr指向struct sockaddr *的指標,&size指向協議地址長度指標 if(-1==new_fd) { perror("accept"); continue; //進行下一次迴圈 } printf("accept client ip:%s:%d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port); //inet_ntoa將一個十進位制網路位元組序轉換為點分十進位制IP格式的字串。 printf("new_fd=%d\n",new_fd); // 開啟檔案,存入客戶端的檔案描述符 FILE *file_fp = fopen("01.file_fp", "w+"); if(NULL == file_fp) { printf(" open 01.file_fp failure\n" ); exit(1); } else { int a=new_fd; fprintf(file_fp,"%d\n",new_fd); fclose(file_fp); } int pthread_id; int ret = pthread_create((pthread_t *)&pthread_id,NULL,net_thread,(void *)&new_fd); if(-1==ret) { perror("pthread_create"); close(new_fd); continue; } } close(server_fd); return 0; } void *net_thread(void * fd) { pthread_detach(pthread_self()); //執行緒分離 int new_fd=*((int *)fd); int file2_fp; while(1) { // recv函式接收資料到緩衝區buffer中 char buffer[BUFFER_SIZE]; memset( buffer,0, sizeof(buffer) ); if(read(new_fd, buffer, sizeof(buffer)) < 0) { perror("Server Recieve Data Failed:"); break; } // 然後從buffer(緩衝區)拷貝到file_name中 char file_name[FILE_SIZE]; memset( file_name,0, sizeof(file_name) ); strncpy(file_name, buffer, strlen(buffer)>FILE_SIZE?FILE_SIZE:strlen(buffer)); memset( buffer,0, sizeof(buffer) ); printf("%s\n", file_name); if( strcmp(file_name,"null")==0 ) { break; close(new_fd); } // 開啟檔案並讀取檔案資料 file2_fp = open(file_name,O_RDONLY,0777); if(file2_fp<0) { printf("File:%s Not Found\n", file_name); } else { int length = 0; memset( buffer,0, sizeof(buffer) ); // 每讀取一段資料,便將其傳送給客戶端,迴圈直到檔案讀完為止 while( (length = read(file2_fp, buffer, sizeof(buffer))) > 0 ) { if( write(new_fd, buffer, length) < 0) { printf("Send File:%s Failed.\n", file_name); break; } memset( buffer,0, sizeof(buffer) ); } // 關閉檔案 close(file2_fp); printf("File:%s Transfer Successful!\n", file_name); } } close(new_fd); }
client.c:
#include <stdio.h> #include <netdb.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/types.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <netinet/in.h> #define portnum 12345 #define FILE_SIZE 500 #define BUFFER_SIZE 1024 int sendfile(int sockfd); int main() { char name[30]={0}; printf("請輸入伺服器的主機名或者ip\n"); scanf("%s",name); struct hostent *h; //獲取伺服器資訊 h=gethostbyname(name); if(NULL==h) { perror("geyhostbyname"); exit(1); } //初始化套接字 int sockfd=socket(AF_INET,SOCK_STREAM,0); if(-1==sockfd) { perror("socket"); exit(2); } struct sockaddr_in server_addr; server_addr.sin_family=AF_INET; server_addr.sin_port=htons(portnum); server_addr.sin_addr=*((struct in_addr *)h->h_addr_list[0]); if(-1==connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr))) { perror("connect"); exit(3); } while(1) { sendfile(sockfd); } return 0; } int sendfile(int sockfd) { // 輸入檔名 並放到緩衝區buffer中等待發送 int file_fp; char file_name[FILE_SIZE]; memset( file_name,0, sizeof(file_name) ); printf("Please Input File Name On Server: "); scanf("%s", file_name); char buffer[BUFFER_SIZE]; memset( buffer,0, sizeof(buffer) ); strncpy(buffer, file_name, strlen(file_name)>sizeof(buffer)?sizeof(buffer):strlen(file_name)); // 向伺服器傳送buffer中的資料 if(write(sockfd, buffer, sizeof(buffer)) < 0) { perror("Send File Name Failed:"); exit(1); } if( strcmp(file_name,"null")==0 ) { exit(1); close(sockfd); } // 開啟檔案,準備寫入 file_fp = open(file_name,O_CREAT|O_RDWR,0777); if( file_fp<0 ) { printf("File:\t%s Can Not Open To Write\n", file_name); exit(1); } // 從伺服器接收資料到buffer中 // 每接收一段資料,便將其寫入檔案中,迴圈直到檔案接收完並寫完為止 int length = 0; memset( buffer,0, sizeof(buffer) ); while((length = read(sockfd, buffer, sizeof(buffer))) > 0) { if( write( file_fp, buffer, length ) < length) { printf("File:\t%s Write Failed\n", file_name); break; } if(length < sizeof(buffer)) { break; } memset( buffer,0, sizeof(buffer) ); } // 接收成功後,關閉檔案,關閉socket printf("Receive File:\t%s From Server IP Successful!\n", file_name); close(file_fp); }