Linux下使用socket傳輸檔案的C語言簡單實現
阿新 • • 發佈:2019-01-18
伺服器程式和客戶端程式應當分別執行在兩臺計算機上。
在執行伺服器端的計算機終端執行:./file_server
在執行客戶端的計算終端上執行:./file_client ipaddr_server
然後根據提示輸入要傳輸的伺服器上的檔案,該檔案必須在伺服器的當前執行目錄中,否則會提示找不到檔案。
直接上原始碼吧:
[cpp] view plaincopyprint?- ////////////////////////////////////////////////////////////////////////
- // 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;
- }
////////////////////////////////////////////////////////////////////////
// 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?
- //////////////////////////////////////////////////////
- // file_client.c socket傳輸檔案的client端示例程式
- // ///////////////////////////////////////////////////
- #include<netinet/in.h> // for sockaddr_in
- #include<sys/types.h> // for socket
- #include<sys/socket.h> // for socket
- #include<stdio.h> // for printf
- #include<stdlib.h> // for exit
- #include<string.h> // for bzero
- #define HELLO_WORLD_SERVER_PORT 6666
- #define BUFFER_SIZE 1024
- #define FILE_NAME_MAX_SIZE 512
- int main(int argc, char **argv)
- {
- if (argc != 2)
- {
- printf("Usage: ./%s ServerIPAddress\n", argv[0]);
- exit(1);
- }
- // 設定一個socket地址結構client_addr, 代表客戶機的internet地址和埠
- struct sockaddr_in client_addr;
- bzero(&client_addr, sizeof(client_addr));
- client_addr.sin_family = AF_INET; // internet協議族
- client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自動獲取本機地址
- client_addr.sin_port = htons(0); // auto allocated, 讓系統自動分配一個空閒埠
- // 建立用於internet的流協議(TCP)型別socket,用client_socket代表客戶端socket
- int client_socket = socket(AF_INET, SOCK_STREAM, 0);
- if (client_socket < 0)
- {
- printf("Create Socket Failed!\n");
- exit(1);
- }
- // 把客戶端的socket和客戶端的socket地址結構繫結
- if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)))
- {
- printf("Client Bind Port Failed!\n");
- exit(1);
- }
- // 設定一個socket地址結構server_addr,代表伺服器的internet地址和埠
- struct sockaddr_in server_addr;
- bzero(&server_addr, sizeof(server_addr));
- server_addr.sin_family = AF_INET;
- // 伺服器的IP地址來自程式的引數
- if (inet_aton(argv[1], &server_addr.sin_addr) == 0)
- {
- printf("Server IP Address Error!\n");
- exit(1);
- }
- server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
- socklen_t server_addr_length = sizeof(server_addr);
- // 向伺服器發起連線請求,連線成功後client_socket代表客戶端和伺服器端的一個socket連線
- if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length) < 0)
- {
- printf("Can Not Connect To %s!\n", argv[1]);
- exit(1);
- }
- char file_name[FILE_NAME_MAX_SIZE + 1];
- bzero(file_name, sizeof(file_name));
- printf("Please Input File Name On Server.\t");
- scanf("%s", file_name);
- char buffer[BUFFER_SIZE];
- bzero(buffer, sizeof(buffer));
- strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));
- // 向伺服器傳送buffer中的資料,此時buffer中存放的是客戶端需要接收的檔案的名字
- send(client_socket, buffer, BUFFER_SIZE, 0);
- FILE *fp = fopen(file_name, "w");
- if (fp == NULL)
- {
- printf("File:\t%s Can Not Open To Write!\n", file_name);
- exit(1);
- }
- // 從伺服器端接收資料到buffer中
- bzero(buffer, sizeof(buffer));
- int length = 0;
- while(length = recv(client_socket, buffer, BUFFER_SIZE, 0))
- {
- if (length < 0)
- {
- printf("Recieve Data From Server %s Failed!\n", argv[1]);
- break;
- }
- int write_length = fwrite(buffer, sizeof(char), length, fp);
- if (write_length < length)
- {
- printf("File:\t%s Write Failed!\n", file_name);
- break;
- }
- bzero(buffer, BUFFER_SIZE);
- }
- printf("Recieve File:\t %s From Server[%s] Finished!\n", file_name, argv[1]);
- // 傳輸完畢,關閉socket
- fclose(fp);
- close(client_socket);
- return 0;
- }
客戶端不一定要bind(),服務端一定要bind(),為什麼?不然客戶端怎麼知道伺服器位置(IP+PORT)。 一般客戶端不繫結埠,因為客戶程式經常開關, 由於一些原因(這裡我說不清楚,你碰到了自然理解), 斷開時埠很少立刻釋放(一般要1、2分鐘)。 所以客戶端繫結埠容易出問題。
注:伺服器繫結的是偵聽埠,客戶連線後, 新分配一個sock和它連線(這個sock的port是不同的,相當於沒有bind的一個埠) 由於偵聽埠是沒有實際聯接的,所以斷開時不需握手,也就沒有釋放問題了。 (注這段是回答時突然想到的,自我感覺是正確的,大家來批判啊)