1. 程式人生 > >Linux下使用socket傳輸檔案的C語言簡單實現

Linux下使用socket傳輸檔案的C語言簡單實現

伺服器程式和客戶端程式應當分別執行在兩臺計算機上。

在執行伺服器端的計算機終端執行:./file_server

在執行客戶端的計算終端上執行:./file_client   ipaddr_server

然後根據提示輸入要傳輸的伺服器上的檔案,該檔案必須在伺服器的當前執行目錄中,否則會提示找不到檔案。

直接上原始碼吧:

[cpp] view plaincopyprint?
  1. ////////////////////////////////////////////////////////////////////////
  2. // file_server.c -- socket檔案傳輸伺服器端示例程式碼
  3. // /////////////////////////////////////////////////////////////////////
  4. #include<netinet/in.h>
  5. #include<sys/types.h>
  6. #include<sys/socket.h>
  7. #include<stdio.h>
  8. #include<stdlib.h>
  9. #include<string.h>
  10. #define HELLO_WORLD_SERVER_PORT    6666
  11. #define LENGTH_OF_LISTEN_QUEUE     20
  12. #define BUFFER_SIZE                1024
  13. #define FILE_NAME_MAX_SIZE         512
  14. int main(int argc, char **argv)  
  15. {  
  16. // set socket's address information
  17. // 設定一個socket地址結構server_addr,代表伺服器internet的地址和埠
  18. struct sockaddr_in   server_addr;  
  19.     bzero(&server_addr, sizeof(server_addr));  
  20.     server_addr.sin_family = AF_INET;  
  21.     server_addr.sin_addr.s_addr = htons(INADDR_ANY);  
  22.     server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
  23. // create a stream socket
  24. // 建立用於internet的流協議(TCP)socket,用server_socket代表伺服器向客戶端提供服務的介面
  25. int server_socket = socket(PF_INET, SOCK_STREAM, 0);  
  26. if (server_socket < 0)  
  27.     {  
  28.         printf("Create Socket Failed!\n");  
  29.         exit(1);  
  30.     }  
  31. // 把socket和socket地址結構繫結
  32. if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))  
  33.     {  
  34.         printf("Server Bind Port: %d Failed!\n", HELLO_WORLD_SERVER_PORT);  
  35.         exit(1);  
  36.     }  
  37. // server_socket用於監聽
  38. if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))  
  39.     {  
  40.         printf("Server Listen Failed!\n");  
  41.         exit(1);  
  42.     }  
  43. // 伺服器端一直執行用以持續為客戶端提供服務
  44. while(1)  
  45.     {  
  46. // 定義客戶端的socket地址結構client_addr,當收到來自客戶端的請求後,呼叫accept
  47. // 接受此請求,同時將client端的地址和埠等資訊寫入client_addr中
  48. struct sockaddr_in client_addr;  
  49.         socklen_t          length = sizeof(client_addr);  
  50. // 接受一個從client端到達server端的連線請求,將客戶端的資訊儲存在client_addr中
  51. // 如果沒有連線請求,則一直等待直到有連線請求為止,這是accept函式的特性,可以
  52. // 用select()來實現超時檢測
  53. // accpet返回一個新的socket,這個socket用來與此次連線到server的client進行通訊
  54. // 這裡的new_server_socket代表了這個通訊通道
  55. int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);  
  56. if (new_server_socket < 0)  
  57.         {  
  58.             printf("Server Accept Failed!\n");  
  59. break;  
  60.         }  
  61. char buffer[BUFFER_SIZE];  
  62.         bzero(buffer, sizeof(buffer));  
  63.         length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);  
  64. if (length < 0)  
  65.         {  
  66.             printf("Server Recieve Data Failed!\n");  
  67. break;  
  68.         }  
  69. char file_name[FILE_NAME_MAX_SIZE + 1];  
  70.         bzero(file_name, sizeof(file_name));  
  71.         strncpy(file_name, buffer,  
  72.                 strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));  
  73. FILE *fp = fopen(file_name, "r");  
  74. if (fp == NULL)  
  75.         {  
  76.             printf("File:\t%s Not Found!\n", file_name);  
  77.         }  
  78. else
  79.         {  
  80.             bzero(buffer, BUFFER_SIZE);  
  81. int file_block_length = 0;  
  82. while( (file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0)  
  83.             {  
  84.                 printf("file_block_length = %d\n", file_block_length);  
  85. // 傳送buffer中的字串到new_server_socket,實際上就是傳送給客戶端
  86. if (send(new_server_socket, buffer, file_block_length, 0) < 0)  
  87.                 {  
  88.                     printf("Send File:\t%s Failed!\n", file_name);  
  89. break;  
  90.                 }  
  91.                 bzero(buffer, sizeof(buffer));  
  92.             }  
  93.             fclose(fp);  
  94.             printf("File:\t%s Transfer Finished!\n", file_name);  
  95.         }  
  96.         close(new_server_socket);  
  97.     }  
  98.     close(server_socket);  
  99. return 0;  
  100. }  
////////////////////////////////////////////////////////////////////////
// file_server.c -- socket檔案傳輸伺服器端示例程式碼
// /////////////////////////////////////////////////////////////////////
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define HELLO_WORLD_SERVER_PORT    6666
#define LENGTH_OF_LISTEN_QUEUE     20
#define BUFFER_SIZE                1024
#define FILE_NAME_MAX_SIZE         512

int main(int argc, char **argv)
{
	// set socket's address information
	// 設定一個socket地址結構server_addr,代表伺服器internet的地址和埠
	struct sockaddr_in   server_addr;
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htons(INADDR_ANY);
	server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);

	// create a stream socket
	// 建立用於internet的流協議(TCP)socket,用server_socket代表伺服器向客戶端提供服務的介面
	int server_socket = socket(PF_INET, SOCK_STREAM, 0);
	if (server_socket < 0)
	{
		printf("Create Socket Failed!\n");
		exit(1);
	}

	// 把socket和socket地址結構繫結
	if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
	{
		printf("Server Bind Port: %d Failed!\n", HELLO_WORLD_SERVER_PORT);
		exit(1);
	}

	// server_socket用於監聽
	if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
	{
		printf("Server Listen Failed!\n");
		exit(1);
	}

	// 伺服器端一直執行用以持續為客戶端提供服務
	while(1)
	{
		// 定義客戶端的socket地址結構client_addr,當收到來自客戶端的請求後,呼叫accept
		// 接受此請求,同時將client端的地址和埠等資訊寫入client_addr中
		struct sockaddr_in client_addr;
		socklen_t          length = sizeof(client_addr);

		// 接受一個從client端到達server端的連線請求,將客戶端的資訊儲存在client_addr中
		// 如果沒有連線請求,則一直等待直到有連線請求為止,這是accept函式的特性,可以
		// 用select()來實現超時檢測
		// accpet返回一個新的socket,這個socket用來與此次連線到server的client進行通訊
		// 這裡的new_server_socket代表了這個通訊通道
		int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
		if (new_server_socket < 0)
		{
			printf("Server Accept Failed!\n");
			break;
		}

		char buffer[BUFFER_SIZE];
		bzero(buffer, sizeof(buffer));
		length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);
		if (length < 0)
		{
			printf("Server Recieve Data Failed!\n");
			break;
		}

		char file_name[FILE_NAME_MAX_SIZE + 1];
		bzero(file_name, sizeof(file_name));
		strncpy(file_name, buffer,
				strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));

		FILE *fp = fopen(file_name, "r");
		if (fp == NULL)
		{
			printf("File:\t%s Not Found!\n", file_name);
		}
		else
		{
			bzero(buffer, BUFFER_SIZE);
			int file_block_length = 0;
			while( (file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0)
			{
				printf("file_block_length = %d\n", file_block_length);

				// 傳送buffer中的字串到new_server_socket,實際上就是傳送給客戶端
				if (send(new_server_socket, buffer, file_block_length, 0) < 0)
				{
					printf("Send File:\t%s Failed!\n", file_name);
					break;
				}

				bzero(buffer, sizeof(buffer));
			}
			fclose(fp);
			printf("File:\t%s Transfer Finished!\n", file_name);
		}

		close(new_server_socket);
	}

	close(server_socket);

	return 0;
}
[cpp] view plaincopyprint?
  1. //////////////////////////////////////////////////////
  2. // file_client.c  socket傳輸檔案的client端示例程式
  3. // ///////////////////////////////////////////////////
  4. #include<netinet/in.h>                         // for sockaddr_in
  5. #include<sys/types.h>                          // for socket
  6. #include<sys/socket.h>                         // for socket
  7. #include<stdio.h>                              // for printf
  8. #include<stdlib.h>                             // for exit
  9. #include<string.h>                             // for bzero
  10. #define HELLO_WORLD_SERVER_PORT       6666
  11. #define BUFFER_SIZE                   1024
  12. #define FILE_NAME_MAX_SIZE            512
  13. int main(int argc, char **argv)  
  14. {  
  15. if (argc != 2)  
  16.     {  
  17.         printf("Usage: ./%s ServerIPAddress\n", argv[0]);  
  18.         exit(1);  
  19.     }  
  20. // 設定一個socket地址結構client_addr, 代表客戶機的internet地址和埠
  21. struct sockaddr_in client_addr;  
  22.     bzero(&client_addr, sizeof(client_addr));  
  23.     client_addr.sin_family = AF_INET; // internet協議族
  24.     client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自動獲取本機地址
  25.     client_addr.sin_port = htons(0); // auto allocated, 讓系統自動分配一個空閒埠
  26. // 建立用於internet的流協議(TCP)型別socket,用client_socket代表客戶端socket
  27. int client_socket = socket(AF_INET, SOCK_STREAM, 0);  
  28. if (client_socket < 0)  
  29.     {  
  30.         printf("Create Socket Failed!\n");  
  31.         exit(1);  
  32.     }  
  33. // 把客戶端的socket和客戶端的socket地址結構繫結
  34. if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)))  
  35.     {  
  36.         printf("Client Bind Port Failed!\n");  
  37.         exit(1);  
  38.     }  
  39. // 設定一個socket地址結構server_addr,代表伺服器的internet地址和埠
  40. struct sockaddr_in  server_addr;  
  41.     bzero(&server_addr, sizeof(server_addr));  
  42.     server_addr.sin_family = AF_INET;  
  43. // 伺服器的IP地址來自程式的引數
  44. if (inet_aton(argv[1], &server_addr.sin_addr) == 0)  
  45.     {  
  46.         printf("Server IP Address Error!\n");  
  47.         exit(1);  
  48.     }  
  49.     server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
  50.     socklen_t server_addr_length = sizeof(server_addr);  
  51. // 向伺服器發起連線請求,連線成功後client_socket代表客戶端和伺服器端的一個socket連線
  52. if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length) < 0)  
  53.     {  
  54.         printf("Can Not Connect To %s!\n", argv[1]);  
  55.         exit(1);  
  56.     }  
  57. char file_name[FILE_NAME_MAX_SIZE + 1];  
  58.     bzero(file_name, sizeof(file_name));  
  59.     printf("Please Input File Name On Server.\t");  
  60.     scanf("%s", file_name);  
  61. char buffer[BUFFER_SIZE];  
  62.     bzero(buffer, sizeof(buffer));  
  63.     strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));  
  64. // 向伺服器傳送buffer中的資料,此時buffer中存放的是客戶端需要接收的檔案的名字
  65.     send(client_socket, buffer, BUFFER_SIZE, 0);  
  66. FILE *fp = fopen(file_name, "w");  
  67. if (fp == NULL)  
  68.     {  
  69.         printf("File:\t%s Can Not Open To Write!\n", file_name);  
  70.         exit(1);  
  71.     }  
  72. // 從伺服器端接收資料到buffer中
  73.     bzero(buffer, sizeof(buffer));  
  74. int length = 0;  
  75. while(length = recv(client_socket, buffer, BUFFER_SIZE, 0))  
  76.     {  
  77. if (length < 0)  
  78.         {  
  79.             printf("Recieve Data From Server %s Failed!\n", argv[1]);  
  80. break;  
  81.         }  
  82. int write_length = fwrite(buffer, sizeof(char), length, fp);  
  83. if (write_length < length)  
  84.         {  
  85.             printf("File:\t%s Write Failed!\n", file_name);  
  86. break;  
  87.         }  
  88.         bzero(buffer, BUFFER_SIZE);  
  89.     }  
  90.     printf("Recieve File:\t %s From Server[%s] Finished!\n", file_name, argv[1]);  
  91. // 傳輸完畢,關閉socket
  92.     fclose(fp);  
  93.     close(client_socket);  
  94. return 0;  
  95. }  

客戶端不一定要bind(),服務端一定要bind(),為什麼?不然客戶端怎麼知道伺服器位置(IP+PORT)。 一般客戶端不繫結埠,因為客戶程式經常開關, 由於一些原因(這裡我說不清楚,你碰到了自然理解), 斷開時埠很少立刻釋放(一般要1、2分鐘)。 所以客戶端繫結埠容易出問題。
注:伺服器繫結的是偵聽埠,客戶連線後,  新分配一個sock和它連線(這個sock的port是不同的,相當於沒有bind的一個埠)  由於偵聽埠是沒有實際聯接的,所以斷開時不需握手,也就沒有釋放問題了。   (注這段是回答時突然想到的,自我感覺是正確的,大家來批判啊)

相關推薦

no