您當前的位置:首頁 > 書法

計算機網路 socket方式傳輸檔案

作者:由 碼農愛學習 發表于 書法時間:2020-05-28

socket通訊實現檔案的傳輸,TCP傳輸方式,python版與C/C++版。

python版

伺服器端程式碼

TCPserver。py:

# -*- coding:utf-8 -*-

import

socket

import

os

import

threading

# 獲取本機ip

def

get_host_ip

():

try

s

=

socket

socket

socket

AF_INET

socket

SOCK_DGRAM

s

connect

((

‘8。8。8。8’

80

))

ip

=

s

getsockname

()[

0

finally

s

close

()

return

ip

# 處理客戶端請求下載檔案的操作(從主執行緒提出來的程式碼)

def

deal_client_request

ip_port

service_client_socket

):

# 連線成功後,輸出“客戶端連線成功”和客戶端的ip和埠

print

“客戶端連線成功”

ip_port

# 接收客戶端的請求資訊【recv】

file_name

=

service_client_socket

recv

1024

# 解碼

file_name_data

=

file_name

decode

“utf-8”

# 判斷檔案是否存在

if

os

path

exists

file_name_data

):

#輸出檔案位元組數

fsize

=

os

path

getsize

file_name_data

#轉化為兆單位

fmb

=

fsize

/

float

1024

*

1024

#要傳輸的檔案資訊

senddata

=

“檔名:

%s

檔案大小:

%。2f

MB”

%

file_name_data

fmb

#傳送和列印檔案資訊【send】

service_client_socket

send

senddata

encode

“utf-8”

))

print

“請求檔名:

%s

檔案大小:

%。2f

MB”

%

file_name_data

fmb

))

#接受客戶是否需要下載【recv】

options

=

service_client_socket

recv

1024

if

options

decode

“utf-8”

==

“y”

# 開啟檔案

with

open

file_name_data

“rb”

as

f

#計算總資料包數目

nums

=

fsize

/

1024

#當前傳輸的資料包數目

cnum

=

0

while

True

file_data

=

f

read

1024

cnum

=

cnum

+

1

#progress = cnum/nums*100

#print(“當前已下載:%。2f%%”%progress,end = “\r”)

if

file_data

# 只要讀取到資料,就向客戶端進行傳送【send】

service_client_socket

send

file_data

# 資料讀完,退出迴圈

else

print

“請求的檔案資料傳送完成”

break

else

print

“下載取消!”

else

print

“下載的檔案不存在!”

# 關閉服務當前客戶端的套接字【close】

service_client_socket

close

()

if

__name__

==

‘__main__’

# 獲取本機ip

print

“TCP檔案傳輸伺服器,本機IP:”

+

get_host_ip

())

# 把工作目錄切換到data目錄下

os

chdir

“。/data”

# 建立套接字【socket】

tcp_server_socket

=

socket

socket

socket

AF_INET

socket

SOCK_STREAM

# 繫結埠號【bind】

tcp_server_socket

bind

((

“”

3356

))

# 設定監聽,將主動套接字變為被動套接字【listen】

tcp_server_socket

listen

128

# 迴圈呼叫【accept】,可以支援多個客戶端同時連線,和多個客戶端同時下載檔案

while

True

service_client_socket

ip_port

=

tcp_server_socket

accept

()

# 連線成功後列印套接字號

#print(id(service_client_socket))

# 建立子執行緒

sub_thread

=

threading

Thread

target

=

deal_client_request

args

=

ip_port

service_client_socket

))

# 啟動子執行緒

sub_thread

start

()

客戶端程式碼

TCPclient。py:

# -*- coding:utf-8 -*-

# 多工檔案下載器客戶端

import

socket

import

os

if

__name__

==

‘__main__’

# 建立套接字【socket】

tcp_client_socket

=

socket

socket

socket

AF_INET

socket

SOCK_STREAM

# 和服務端連線【connect】

server_ip

=

input

“輸入伺服器IP:”

tcp_client_socket

connect

((

server_ip

3356

))

# 傳送下載檔案的請求

file_name

=

input

“請輸入要下載的檔名:”

# 編碼

file_name_data

=

file_name

encode

“utf-8”

# 傳送檔案下載請求資料【send】

tcp_client_socket

send

file_name_data

# 接收要下載的檔案資訊【recv】

file_info

=

tcp_client_socket

recv

1024

# 檔案資訊解碼

info_decode

=

file_info

decode

“utf-8”

print

info_decode

#獲取檔案大小

fileszie

=

float

info_decode

split

‘:’

)[

2

split

‘MB’

)[

0

])

fileszie2

=

fileszie

*

1024

# 是否下載?輸入y確認輸入q 取消

opts

=

input

“是否下載?(y 確認q 取消)”

if

opts

==

‘q’

print

“下載取消!程式退出”

else

print

“正在下載>>>>>>”

#向伺服器確認正在下載【send】

tcp_client_socket

send

b

‘y’

recvpath

=

“。/receive/”

if

not

os

path

exists

recvpath

):

os

mkdir

recvpath

# 把資料寫入到檔案裡

with

open

recvpath

+

file_name

“wb”

as

file

#目前接收到的資料包數目

cnum

=

0

while

True

# 迴圈接收檔案資料【recv】

file_data

=

tcp_client_socket

recv

1024

# 接收到資料

if

file_data

# 寫入資料

file

write

file_data

cnum

=

cnum

+

1

#progress =cnum/fileszie2*100

#print(“當前已下載:%。2f%%”%progress,end = “\r”)

# 接收完成

else

print

“下載結束!”

break

# 關閉套接字【close】

tcp_client_socket

close

()

上述程式修改搬運自:Python3使用TCP編寫一個簡易的檔案下載器——Linux公社 ,伺服器端添加了一段列印本機IP的程式碼,客戶端添加了一段新建receive資料夾儲存接收檔案的程式碼。

程式在Windows和Linux系統上均可執行,測試時需要在伺服器程式所在路徑新建一個data資料夾並放入用於測試的檔案,如圖片、影片檔案等。

另外,此程式在傳輸較小的檔案(如幾KB)時,程式中計算進度的語句會出現除數為0的錯誤,需要遮蔽傳輸進度相關語句或作某些修改。另一方面,進度的顯示也比較耗時,去掉進度顯示可以減小檔案傳輸時間。

測試結果

伺服器端(Ubuntu18。04):

D。。。@deeplearning:~/。。。/TCPsocketTest$ python3 TCPserver。py

TCP檔案傳輸伺服器,本機IP:192。168。1。143

客戶端連線成功 (‘192。168。1。110’, 53114)

請求檔名:1。jpg 檔案大小:0。04 MB

請求的檔案資料傳送完成

客戶端(Win10):

============= RESTART: G:\。。。\TCPsocketTest\TCPclient。py =============

輸入伺服器IP:192。168。1。143

請輸入要下載的檔名:1。jpg

檔名:1。jpg 檔案大小:0。04MB

是否下載?(y 確認q 取消)y

正在下載>>>>>>

下載結束!

>>>

G:\TCPsocketTest>

伺服器端執行在Ubuntu18。04系統,客戶端執行在Win10系統,當然也可以互換執行。另外,實測win10的伺服器端程式與Ubuntu10的客戶端通訊這種情況,win10的伺服器端必須在IDLE環境中執行,在cmd命令列中執行無法連線,原因未知。

C/C++版

將python程式改寫為C/C++語言,實現類似的檔案傳輸功能,以下程式用到了winsock以及dll庫,只能在Windows系統下執行。

伺服器端

server。cpp

// 客戶端傳送字串,伺服器接收字串,以相同內容返回 (迴圈服務)

#include

#include

#include

#include

#pragma comment (lib, “ws2_32。lib”)

//載入 ws2_32。dll

#define BUF_SIZE 1024

int

main

()

{

WSADATA

wsaData

WSAStartup

MAKEWORD

2

2

),

&

wsaData

);

// 載入套接字型檔

//建立套接字

SOCKET

servSock

=

socket

AF_INET

SOCK_STREAM

0

);

//【socket】

//繫結套接字

sockaddr_in

sockAddr

memset

&

sockAddr

0

sizeof

sockAddr

));

//每個位元組都用0填充

sockAddr

sin_family

=

PF_INET

//使用IPv4地址

//sockAddr。sin_addr。s_addr = inet_addr(“127。0。0。1”); //具體的IP地址

sockAddr

sin_addr

s_addr

=

htonl

INADDR_ANY

);

//不要這一句好像也行

sockAddr

sin_port

=

htons

3356

);

//埠

bind

servSock

SOCKADDR

*

&

sockAddr

sizeof

SOCKADDR

));

//【bind】

//進入監聽狀態

listen

servSock

20

);

//【listen】

//接收客戶端請求

SOCKADDR

clntAddr

int

nSize

=

sizeof

SOCKADDR

);

char

buffer

BUF_SIZE

=

{

0

};

//接收緩衝區

char

sbuffer

BUF_SIZE

=

{

0

};

// 本機IP

char

ip

20

=

{

0

};

struct

hostent

*

phostinfo

=

gethostbyname

“”

);

char

*

p

=

inet_ntoa

*

((

struct

in_addr

*

)(

*

phostinfo

->

h_addr_list

)));

strncpy

ip

p

sizeof

ip

-

1

);

ip

sizeof

ip

-

1

=

‘\0’

printf

“This is TCP file server(IP:%s)

\n

ip

);

printf

“waiting connect。。。

\n

);

while

1

{

SOCKET

clntSock

=

accept

servSock

SOCKADDR

*

&

clntAddr

&

nSize

);

//【accept】

// 獲取客戶端的IP的埠號

struct

sockaddr_in

*

sock

=

struct

sockaddr_in

*

&

clntAddr

printf

“client(IP:%s, PORT:%d)connect ok

\n

inet_ntoa

sock

->

sin_addr

),

ntohs

sock

->

sin_port

));

int

strLen

=

recv

clntSock

buffer

BUF_SIZE

0

);

//接收客戶端發來的資料 【recv】

//printf(“%s\n”, buffer);

// 判斷檔案是否存在

FILE

*

pFile

pFile

=

fopen

buffer

“rb”

);

//獲取已開啟檔案的指標

if

pFile

{

fseek

pFile

0

SEEK_END

);

//先用fseek將檔案指標移到檔案末尾

int

n

=

ftell

pFile

);

//再用ftell獲取檔案內指標當前的檔案位置。

//printf(“file:%s, size:%dKB\n”, buffer, n/1024);

sprintf

sbuffer

“file:%s, size:%dKB

\n

buffer

n

/

1024

);

send

clntSock

sbuffer

BUF_SIZE

0

);

//【send】檔案大小資訊

recv

clntSock

buffer

BUF_SIZE

0

);

//【recv】

if

*

buffer

==

‘y’

{

fseek

pFile

0

SEEK_SET

);

//開頭

int

len

while

1

{

if

((

len

=

fread

&

sbuffer

1

BUF_SIZE

pFile

))

&&

len

>

0

{

send

clntSock

sbuffer

len

0

);

//【send】

//printf(“FILE p offset:%d\n”, (int)ftell(pFile));

//printf(“%d\n”, len);

}

else

{

printf

“Download down!

\n

);

break

}

}

}

else

printf

“Download Canceled!

\n

);

}

else

{

sprintf

sbuffer

“file:%s not exist!!!

\n

buffer

);

send

clntSock

sbuffer

BUF_SIZE

0

);

//【send】檔案大小資訊

}

closesocket

clntSock

);

//關閉套接字

memset

buffer

0

BUF_SIZE

);

//重置緩衝區

memset

sbuffer

0

BUF_SIZE

);

}

//關閉套接字

closesocket

servSock

);

//終止 DLL 的使用

WSACleanup

();

return

0

}

客戶端

client。cpp:

// 客戶端傳送字串,伺服器接收字串,以相同內容返回 (迴圈服務)

#include

#include

#include

#pragma comment(lib, “ws2_32。lib”)

//載入 ws2_32。dll

#define BUF_SIZE 1024

int

main

()

{

//初始化DLL

WSADATA

wsaData

WSAStartup

MAKEWORD

2

2

),

&

wsaData

);

//向伺服器發起請求

sockaddr_in

sockAddr

memset

&

sockAddr

0

sizeof

sockAddr

));

//每個位元組都用0填充

sockAddr

sin_family

=

PF_INET

//sockAddr。sin_addr。s_addr = inet_addr(“127。0。0。1”);//伺服器IP

sockAddr

sin_port

=

htons

3356

);

char

bufSend

BUF_SIZE

=

{

0

};

char

bufRecv

BUF_SIZE

=

{

0

};

//輸入伺服器IP

printf

“Input server IP: ”

);

gets_s

bufSend

BUF_SIZE

);

//此處先借用bufSend接收輸入的IP地址

sockAddr

sin_addr

s_addr

=

inet_addr

bufSend

);

while

1

{

//建立套接字

SOCKET

sock

=

socket

PF_INET

SOCK_STREAM

IPPROTO_TCP

);

//【socket】

connect

sock

SOCKADDR

*

&

sockAddr

sizeof

SOCKADDR

));

//【connect】

//獲取使用者輸入的字串併發送給伺服器

printf

“Input a file name for download: ”

);

gets_s

bufSend

BUF_SIZE

);

send

sock

bufSend

BUF_SIZE

0

);

//【send】

//接收伺服器傳回的資料

recv

sock

bufRecv

BUF_SIZE

0

);

//【recv】

//輸出接收到的資料

printf

“%s

\n

bufRecv

);

if

strstr

bufRecv

“not”

))

{

printf

“Download now?(y/n)”

);

if

getchar

()

==

‘y’

{

printf

“Download 。。。

\n

);

send

sock

“y”

sizeof

char

),

0

);

//【send】

FILE

*

pFile

pFile

=

fopen

bufSend

“wb”

);

//獲取已開啟檔案的指標

int

len

while

((

len

=

recv

sock

bufRecv

BUF_SIZE

0

))

&&

len

>

0

//【recv】

{

fwrite

&

bufRecv

1

len

pFile

);

//printf(“receive。。。%d\n”, (int)ftell(pFile));

//printf(“%d\n”, len);

}

printf

“Download down!

\n

);

fclose

pFile

);

}

else

printf

“Cancel download

\n

);

}

getchar

();

//此句是為了消除上面gets_s()或的問題

memset

bufSend

0

BUF_SIZE

);

//重置緩衝區

memset

bufRecv

0

BUF_SIZE

);

//重置緩衝區

closesocket

sock

);

//關閉套接字【close】

}

WSACleanup

();

//終止使用 DLL

return

0

}

執行效果與python版的類似。

標簽: Socket  file  客戶端  client  buf