파이썬으로 PC간에 파일 전송을 하는 프로그램을 만들어서 Client에서 Server로 파일을 보내고, 받고 다 해보고 싶다.
고민한 방법은 몇 가지가 있다..
1. 그 중에서 가장 쉬워 보이는 방법으로 Server용으로 사용할 PC에 FTP 설정을 해놓고 Client PC에서 파이썬의 ftplib 모듈을 이용해서 FTP접속 및 파일 GET, PUT 등을 하는 방법?
2. 1번과 유사한데 FTP Server 설정까지 파이썬으로 구동 하는 방법? 이 있다. 구글링 해보니 pyftpdlib를 쓰면 되는 것 같은데 이것도 천천히 확인해 봐야겠다.
좀 더 보니 이런것도 있네? 뭔 내용인지는 천천히 살펴 봐야겠다..
3. 마지막으로는 socket 통신을 이용한 방법이다. socket에 대한 설명은 구글에 검색하면 바로 나온다. 그냥 Application에 접속하기 위한 통로(?) 정도로만 보면 되지 않을까?
Server 쪽에서 IP, Port, Socket 설정 등을 통해 통로를 열어두면 Client에서도 Socket을 열어 Server의 Socket으로 접속!
FTP를 이용한 방법으로 파일 전송을 할까 하다가.. 우선 Socket을 이용해서 파일 전송을 해보고자 한다.
아.. 그전에 하나 공유할 것이 있다.
보통 test를 할떄는 server와 client PC가 같은 경우가 많이 떄문에 여러개를 띄워야 할 경우도 있을 것이다. 이때 개인적으로 유용한거는 Powershell 창 열기이다.
파이썬 .py 파일이 있는 폴더에서 마우스 오른쪽 클릭을 하면 안뜨지만 Shift + 마우스 오른쪽 버튼을 누르면 확인이 가능하다.

<그냥 마우스 오른쪽 클릭 - Powershell 안보임>

<Shift + 마우스 오른쪽 클릭 - Powershell이 나타남>
Powershell 창을 누르면 아래처럼 파이썬 파일이 있는 경로가 뜨고 python xxx.py 명령어를 통해 바로 파이썬 파일을 실행할 수 있다. powershell 창을 2개 띄워서 한개는 server 한개는 client 창을 띄우면 조금 더 쉽게 test가 가능하다.
또한, python xxx.py -p -d 이런 식으로 뭔가 옵션을 넣어서 파이썬 파일을 실행시킬때도 유용하고..
pyinstaller --onefile 과 같은 파이썬 exe 실행팡리 만들때도 편한듯하다.

물론 더 좋은 방법도 많을테지만.. (저도 좀 공유좀.. ㅠㅠ)
아무튼 이제 시작해보려고 한다.
우선은.. 지정된 폴더에 있는 지정된 파일명을 server -> client 로 보내기다.
먼저 아래처럼 server 쪽 스크립트를 만들어보자.
우선 모듈을 보면..
time 은 현재는 필요 없는데, 추후에 사용 예정(지금은 무시)
socket은 socket 을 열기 위해 사용
threading 도 현재는 미사용
os는 self로 찾아보자.. (질문 환영)
#FTP TEST
import time
import socket
import threading
import os
from os.path import exists
print("SERVER")
def run_server(port, directory):
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
s.bind((host, port))
s.listen(1)
conn, addr = s.accept()
fileName = conn.recv(1024)
fileName = fileName.decode()
## Client 로 파일 사이즈를 보냄
conn.sendall(getFileSize(fileName, directory).encode())
## client가 파일 사이즈를 받으면 "Ready" 라는 문구를 Server 로 보내고, 그 문구를 받으면 파일 전송
Client_Ready = conn.recv(1024)
if Client_Ready.decode() == "ready":
conn.sendall(getFileData(fileName, directory).encode())
conn.close()
## 파일 사이즈 확인
def getFileSize(fileName, directory):
fileSize = os.path.getsize(directory+"\\"+fileName)
return str(fileSize)
## 파일 내용
def getFileData(fileName, directory):
with open(directory+"\\"+fileName, 'r', encoding="UTF-8") as f:
data = ""
for line in f:
data += line
return data
host ='127.0.0.1'
port = 4001
directory = 'C:\pymin\servertest'
run_server(port,directory)
이제 하나하나 볼건데 우선 가장 아래를 보면
host ='127.0.0.1'
port = 4001
directory = 'C:\pymin\servertest'
run_server(port,directory)
host명 (현재는 localhost), port(reserved 된 1024이하는 피하고..), 어떤 directory에 있는 폴더를 복사할건지를 기입하고 run_server 라는 함수를 실행시킨다.
그럼 run_server는 어떤 내용일까?
def run_server(port, directory):
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
s.bind((host, port))
s.listen(1)
conn, addr = s.accept()
fileName = conn.recv(1024)
fileName = fileName.decode()
## Client 로 파일 사이즈를 보냄
conn.sendall(getFileSize(fileName, directory).encode())
## client가 파일 사이즈를 받으면 "Ready" 라는 문구를 Server 로 보내고, 그 문구를 받으면 파일 전송
Client_Ready = conn.recv(1024)
if Client_Ready.decode() == "ready":
conn.sendall(getFileData(fileName, directory).encode())
conn.close()
우선 socket을 열기 위해 socker.socket()을 사용한다. AF_INET은 IPv4이고 AF_INET6은 IPv6인데.. 난 당연히 IPv4를 사용하고 Socket stream으로 사용
*이게 기본값이라 아무것도 안넣고 ()만 해도 된다고 하는데 그냥 넣어주도록 한다..
s.bind는 socket을 생성할 IP랑 Port명을 넣어주고
s.listen은 Server가 몇 개의 client를 위해 listen할지? 개수를 나타내는데,
실제로 server 스크립트를 실행하고 윈도우 cmd창을 열어서 listen중인지 확인해보자.
C:\Users\Min>netstat -aon | findstr 4001
TCP 127.0.0.1:4001 0.0.0.0:0 LISTENING 4200
C:\Users\Min>
음.. 위에 등록해둔 127.0.0.1 IP와 4001 Port로 잘 Listening 하고 있는것을 확인할 수 있다.
*netstat -aon 을 치면 모든 port가 나오니 linex grep 과 유사한 findstr으로 원하는 port만 확인하자.
conn, addr = s.accept()
fileName = conn.recv(1024)
fileName = fileName.decode()
다음으로는 conn, addr 라는게 있는데.. 이건 client에서 socket.connect를 통해 server쪽으로 연동할 때 socket 연동을 하고 client 주소 등을 확인할 수 있다.
다음으로 client에서 socket.sendall 등을 통해 무언가 보냈을 때 받는다는 내용인데 client쪽 파이썬 스크립트(뒤에서 추가 예정)에 client가 원하는 파일명을 보내도록 설정해두어야 한다.
## Client 로 파일 사이즈를 보냄
conn.sendall(getFileSize(fileName, directory).encode())
## client가 파일 사이즈를 받으면 "Ready" 라는 문구를 Server 로 보내고, 그 문구를 받으면 파일 전송
Client_Ready = conn.recv(1024)
if Client_Ready.decode() == "ready":
conn.sendall(getFileData(fileName, directory).encode())
conn.close()
Client가 원하는 파일명을 받으면 해당 폴더에서 그 파일을 찾은 다음에 파일 사이즈를 다시 client로 보내준다. client가 파일 사이즈를 전달받으면 client는 파일을 받을 준비를 끝냈으므로 server로 파일을 요청하기 위해 client ready를 server로 알린다.
*혹시 이해가 가지 않으면 제일 위에서 def getFileSize(fileName, directory) 함수와 def getFileData(fileName, directory): 함수를 다시 한번 확인하도록 한다.
client ready를 받은 server는 최종적으로 sendall 을 통해 파일을 client로 전송한다.
남녀 주인공의 속 사정이 다른것처럼 server 쪽 이야기만 들어서는 완전하게 이해하기가 어려울 수 있다. 그럼 이제 client 파일을 보면서 client에는 어떤 내용들이 있는지 살펴보자.
import time
import socket
import threading
import os
print("CLIENT")
def run_client(port, file_name):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
s.sendall(file_name.encode())
reSize = s.recv(1024)
reSize = reSize.decode()
msg = "ready"
s.sendall(msg.encode())
with open(file_name, 'w', encoding="UTF-8") as f:
## 파일 사이즈만큼 recv
data = s.recv(int(reSize))
f.write(data.decode())
host ='127.0.0.1'
port = 4001
file_name = 'ping_result.txt'
run_client(port,file_name)
Client에서는 Server와는 반대이다.
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
s.sendall(file_name.encode())
Server에서 socket을 열고 기다렸다면, client에서 먼저 connect 한 후에 filename을 서버로 전송한다.
reSize = s.recv(1024)
reSize = reSize.decode()
이후 서버로부터 파일 사이즈를 받는다. (server쪽만 보거나 client만 보면 헷갈리므로 server, client 파일 2개를 한곳에 띄워놓고 비교해가면서 보면 이해하기가 쉽다.)
msg = "ready"
s.sendall(msg.encode())
다음으로는 client에서 server쪽으로 받을 준비가 되었음을 'ready'라는 메시지로 알린다.
*그럼 서버에는 'ready'라는 메시지를 받으면 파일을 전송하도록 설정되어 있다.
with open(file_name, 'w', encoding="UTF-8") as f:
## 파일 사이즈만큼 recv
data = s.recv(int(reSize))
f.write(data.decode())
이후 client는 서버에서 보내주는 파일을 받기만 하면 끝!
어렵지는 않다.. 다만.. 파일을 한번 보내고나면 실행창이 꺼진다.
만약에 server에 있는 파일을 특정 주기마다 계속 client로 보내고 싶다면 어떻게 해야할까?
각 server, client 함수가 반복적으로 돌아가도록 해야 할 것 같다.
다음 장에서는 server, client함수가 반복적으로 돌아가는 것을 확인해보도록 한다.
'Python > Do Something' 카테고리의 다른 글
python - JSON 파일 가공하여 DB에 넣기넣기 (0) | 2023.01.16 |
---|---|
python - PC(Server ~ Client) 간 주기적인 파일 전송(Socket) (0) | 2023.01.16 |
python - ping test 후 결과를 txt 파일로 저장하고, mysql DB에 저장하기 (ping3, pymysql) (0) | 2023.01.16 |
python - mysql DB 생성, txt 파일을 DB Table에 넣기 (pymysql) (0) | 2023.01.16 |
python - ping test 후 결과를 txt 파일로 저장하기 (0) | 2023.01.16 |