Linux c socket,client,server transfer file傳送檔案

下面兩隻程式在 linux 利用 socket 來傳送檔案,目前設定為讀取本機端的 TEST.MP3 送到本機端的 GET.MP3
基本上,想要了解這兩隻程式的話,可以先去參考下面兩個連結,先弄懂比較簡單的傳遞文字訊息
然後再來研究傳遞二進位檔案的問題。

socket教學
socket傳遞文字訊息範例

/*
 * Name : server.c
 * Author : Wen chi-ching
 * Date : 2009/10/14
 * Send file
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(){
	int sockfd, new_fd, numbytes, sin_size;
	char buf[1000000];
	struct sockaddr_in my_addr;
	struct sockaddr_in their_addr;
	struct stat filestat;
	FILE *fp;

	//TCP socket
	if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
		perror("socket");
		exit(1);
	}

	//Initail, bind to port 2323
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(2324);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	bzero( &(my_addr.sin_zero), 8 );

	//binding
	if ( bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == -1 ){
		perror("bind");
		exit(1);
	}

	//Start listening
	if ( listen(sockfd, 10) == -1 ){
		perror("listen");
		exit(1);
	}

	//Get file stat
	if ( lstat("TEST.MP3", &filestat) < 0){
		exit(1);
	}
	printf("The file size is %lun", filestat.st_size);

	fp = fopen("TEST.MP3", "rb");

	//Connect
	if ( (new_fd = accept(sockfd, (struct sockaddr*)&their_addr, &sin_size)) == -1 ){
		perror("accept");
		exit(1);
	}

	//Sending file
	while(!feof(fp)){
		numbytes = fread(buf, sizeof(char), sizeof(buf), fp);
		printf("fread %d bytes, ", numbytes);
		numbytes = write(new_fd, buf, numbytes);
		printf("Sending %d bytesn",numbytes);
	}

	close(new_fd);
	close(sockfd);
	return 0;
}

解釋一下 Sending file 的部份:
除非你打算把程式碼寫死成為只能傳送特定大小的檔案,否則,為了讓程式可以傳送任意大小的檔案,我們會在 server 端每次讀取一部分檔案出來傳送,一直讀到檔案結尾為止,所以我們需要一個 while 迴圈,迴圈終止的條件是『fread』讀到檔案結尾。

1.先用 fread 將 fp 開啟的檔案讀到 buf 裡面,讀取 sizeof(char) 大小,sizeof(buf) 次。為避免不同平台可能會有不同 char 大小問題,一定要用 sizeof(char)來寫比較保險。

2.印出目前讀到多少bytes。

3.用 write 將 buf 裡面的資料寫入上面已經連線好了的 new_fd 這個檔案描述子,第三個參數是用來限制要寫入多少 bytes 過去,因為不見得每次都可以把 buf 填滿,例如每次都讀出1M來傳送,則最後通常會有不足1M的剩餘檔案部份需要傳送,所以不能寫 sizeof(buf) ,所以利用 fread 的回傳值來判斷要從 buf 裡面讀出多少來傳送,讀太多的話會送垃圾資訊過去。把傳送過去的大小的值回傳到numbytes裡。

4.印出送了多少 bytes 到遠端。

/*
 * Name : client.c
 * Author : Wen chi-ching
 * Date : 2009/10/14
 * Recieve file
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char* argv[]){
	int sockfd, numbytes;
	char buf[1000000];
	struct sockaddr_in address;
	FILE *fp;

	//TCP socket
	if ( ( sockfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1 ){
		perror("socket");
		exit(1);
	}

	//Initial, connect to port 2323
	address.sin_family = AF_INET;
	address.sin_port = htons(2324);
	address.sin_addr.s_addr = inet_addr("127.0.0.1");
	bzero( &(address.sin_zero), 8 );

	//Connect to server
	if ( connect(sockfd, (struct sockaddr*)&address, sizeof(struct sockaddr)) == -1){
		perror("connect");
		exit(1);
	}

	//Open file
	if ( (fp = fopen("GET.MP3", "wb")) == NULL){
		perror("fopen");
		exit(1);
	}

	//Receive file from server
	while(1){
		numbytes = read(sockfd, buf, sizeof(buf));
		printf("read %d bytes, ", numbytes);
		if(numbytes == 0){
			break;
		}
		numbytes = fwrite(buf, sizeof(char), numbytes, fp);
		printf("fwrite %d bytesn", numbytes);
	}

	fclose(fp);
	close(sockfd);
	return 0;
}

解釋一下 Receive file from server 的部份:
所以這邊跟 server 都同樣會需要一個 while 迴圈,控制迴圈結束的條件是『接收到大小為零』,這代表檔案已經傳送完了!

1.用 read 將上面已經連線了的 sockfd 檔案描述子的內容讀取到 buf 裡面,每次最多寫入 sizeof(buf) bytes。將回傳值存在 numbytes 這個變數裡,下兩行的寫檔會需要這個數值。

2.印出本次 read 接收到了多少 bytes 。

3.如果接收到的 bytes 為零的話就結束迴圈,因為我們認為這樣的條件代表檔案傳送完了。

4.用 fwrite 將 buf 的資料寫入 fp ,每次寫入 sizeof(char) bytes,寫入 numbytes 次,寫入太多的話會寫入垃圾資訊,所以要用剛才 read 的回傳值來控制要寫入多少 bytes。

5.印出本次 fwrite 寫入了多少 byets 。

Makefile

all : client.c server.c
	gcc -Wall -g -o client client.c
	gcc -Wall -g -o server server.c

clean :
	rm client
	rm server

輸入 make 即可編譯,不過可能會出現一點警告,可以暫時不用理他。

4 關於 “Linux c socket,client,server transfer file傳送檔案” 的評論

  1. 引用通告: magicalloveshe » Blog Archive » Linux c socket,client,server transfer file with multi-thread,多執行緒傳送檔案

  2. 引用通告: Magicalloveshe » Blog Archive » Linux c socket,client,server transfer file with multi-thread,多執行緒傳送檔案

因這code獲益良多的同學 發表迴響 取消回覆

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s