CDW (Coding Discussion World)
python - PC(Server ~ Client) 간 주기적인 파일 전송(Socket) 본문
이전편에서 PC(server~client)간 파일 전송하는 것을 만들어보았는데 파일을 한번만 전송하면 종료되었다.. 이번에는 thread를 이용해서 주기적으로 파일을 전송하고자 한다.
결론적으로는 client PC는 설정된 시간마다 주기적으로 server PC에 있는 장비 리스트를 가져온 후, 해당 장비 리스트에 대해 ping test를 진행하고 결과값을 다시 Server PC로 보내주고자 한다.
혹시 원본파일이 필요하면 쪽지나 댓글 주세요^^
우선 서버쪽부터 살펴보도록 한다.
import time
import socket
import threading
import os
from os.path import exists
print("SERVER(10)")
def run_server1(port, directory):
host ='127.0.0.1'
port = 4001
#pre_file_name = Client 파일에 저장된 'pre_system_list.txt' 파일이 pre_directory 안에 있어야함
pre_directory = os.getcwd() + '/pingtest'
#Client로부터 받은 결과를 아래 폴더에 저장
directory = 'C:/pymin/file_mer_test'
file_name = 'ping_result_4001.txt'
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
s.bind((host, port))
s.listen(1)
conn, addr = s.accept()
get_put_check = conn.recv(1024)
if get_put_check.decode() == "get_file":
msg = "get_file_ack"
conn.sendall(msg.encode())
pre_file_name = conn.recv(1024)
pre_file_name = pre_file_name.decode()
conn.sendall(getFileSize(pre_file_name, pre_directory).encode())
## client가 파일 내용을 받을 준비 확인 및 파일전송
reReady = conn.recv(1024)
if reReady.decode() == "ready":
conn.sendall(getFileData(pre_file_name, pre_directory).encode())
conn.close()
elif get_put_check.decode() == "put_file":
msg = "put_file_ack"
conn.sendall(msg.encode())
reSize = conn.recv(1024)
reSize = reSize.decode()
msg = "ready"
conn.sendall(msg.encode())
with open(directory + '/' + file_name, 'w', encoding="UTF-8") as f:
## 파일 사이즈만큼 recv
data = conn.recv(int(reSize))
f.write(data.decode())
conn.close()
runserver1_thread = threading.Thread(target=run_server1, args=(port,directory))
runserver1_thread.start()
## 파일 크기 반환
def getFileSize(pre_file_name, pre_directory):
fileSize = os.path.getsize(pre_directory+"/"+pre_file_name)
return str(fileSize)
## 파일 함수
def getFileData(pre_file_name, pre_directory):
with open(pre_directory + "/"+pre_file_name, 'r', encoding="UTF-8") as f:
data = ""
## 파일이 매번 각 라인을 읽어 리턴할 수 있기 때문에 라인마다 끊어서 저장
for line in f:
data += line
return data
host ='0.0.0.0' #미사용
port = 0000 #미사용
directory = '0' # 미사용
file_name = '0' #미사용
runserver1_thread = threading.Thread(target=run_server1, args=(port,directory))
runserver1_thread.start()
while True:
time.sleep(1)
pass
이런식으로 나오는데 하나하나 보도록 하겠다.
import time
import socket
import threading
import os
from os.path import exists
print("SERVER(10)")
def run_server1(port, directory):
host ='127.0.0.1'
port = 4001
pre_directory = os.getcwd() + '/pingtest'
#Client로부터 받은 결과를 아래 폴더에 저장
directory = 'C:/pymin/file_mer_test'
file_name = 'ping_result_4001.txt'
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
s.bind((host, port))
s.listen(1)
conn, addr = s.accept()
get_put_check = conn.recv(1024)
우선 함수를 추가했고, 나중에는 thread를 여러개 돌릴려고 한다.
먼저 server의 Host IP와 port를 지정해주고 client로 보내줄 파일이 있는 directory(pre_directory)와 client로 받은 파일을 저장할 directory(directrory)와 파일명(file_name)을 지정하였다.
directory를 전부 static으로 설정할까 하다가 일부는 os.getcwd()를 하용하여 현재 파이썬 파일이 있는 폴더를 활용하여 지정하였다.
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
s.bind((host, port))
s.listen(1)
conn, addr = s.accept()
get_put_check = conn.recv(1024)
다음으로는 client가 접속할 수 있도록 socket 을 열고 listen 설정. (1)은 1개의 client 접속 허용
if get_put_check.decode() == "get_file":
msg = "get_file_ack"
conn.sendall(msg.encode())
pre_file_name = conn.recv(1024)
pre_file_name = pre_file_name.decode()
conn.sendall(getFileSize(pre_file_name, pre_directory).encode())
## client가 파일 내용을 받을 준비 확인 및 파일전송
reReady = conn.recv(1024)
if reReady.decode() == "ready":
conn.sendall(getFileData(pre_file_name, pre_directory).encode())
conn.close()
다음에는 server가 client로 파일을 전송하기 위해 설정하였다.
clinet가 server에 연결한 후 get_file이라는 메시지를 보내면 client가 파일을 get한다는 것을 의미하고 파일 사이즈와 파일을 보내준다. (상세 내용은 이전편 참조)
elif get_put_check.decode() == "put_file":
msg = "put_file_ack"
conn.sendall(msg.encode())
reSize = conn.recv(1024)
reSize = reSize.decode()
msg = "ready"
conn.sendall(msg.encode())
with open(directory + '/' + file_name, 'w', encoding="UTF-8") as f:
## 파일 사이즈만큼 recv
data = conn.recv(int(reSize))
f.write(data.decode())
conn.close()
이번에는 client가 server로 파일을 전송하는 경우에 해당되며, client가 put_file 이라는 메시지를 보내면 server는 파일 사이즈와 파일을 받을 준비를 한다.
## 파일 크기 반환
def getFileSize(pre_file_name, pre_directory):
fileSize = os.path.getsize(pre_directory+"/"+pre_file_name)
return str(fileSize
이 함수는 파일 사이즈를 확인하는 함수이고, 파일 사이즈를 확인해서 client쪽으로 보내준다.
## 파일 함수
def getFileData(pre_file_name, pre_directory):
with open(pre_directory + "/"+pre_file_name, 'r', encoding="UTF-8") as f:
data = ""
## 파일이 매번 각 라인을 읽어 리턴할 수 있기 때문에 라인마다 끊어서 저장
for line in f:
data += line
return data
이 함수는 server가 client로 파일 사이즈를 1024로 끊어서 보내주기 위해 설정하였다.
runserver1_thread = threading.Thread(target=run_server1, args=(port,directory))
runserver1_thread.start()
마지막으로 def run_server1(port, directory) 함수 안에 또다시 thread를 설정한 이유는 client가 server로 파일을 전송하거나 server가 client로 파일을 한번 전송하면 socket이 닫혀서.. (물론 client가 파일을 보내거나 받고 close()하기는 함..) socket이 끊어져도 다시 socket을 열도록 하였다.
C:\Users\Min>netstat -aon |findstr :40
TCP 127.0.0.1:4001 0.0.0.0:0 LISTENING 6084
TCP 127.0.0.1:4001 127.0.0.1:62937 TIME_WAIT 0
TCP 127.0.0.1:4002 0.0.0.0:0 LISTENING 6084
TCP 127.0.0.1:62939 127.0.0.1:4001 TIME_WAIT 0
C:\Users\Min>
그래서 파일을 막 주고받으면 위와 같이 열었던 socket이 닫히고 새로운 socket이 열린것을 확인할 수 있다.
이번에는 client쪽 설정을 보고자 한다. 우선 대략적인 설정이다.
#FTP TEST
import time
import re
import sys
import socket
import threading
import os
import pymysql
import shutil
from os.path import exists
from datetime import datetime
from ping3 import ping,verbose_ping
print("CLIENT")
def runclient_get(port, file_name):
host ='127.0.0.1' # 접속할 서버명
port = 4001
#아래 폴더에 pingtest가 필요한 장비리스트를 저장
pre_directory = os.getcwd()
pre_file_name = 'pre_system_list.txt'
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
msg = "get_file"
s.sendall(msg.encode())
get_file_check = s.recv(1024)
if get_file_check.decode() == "get_file_ack":
s.sendall(pre_file_name.encode())
reSize = s.recv(1024)
reSize = reSize.decode()
msg = "ready"
s.sendall(msg.encode())
with open(pre_directory + '/' + pre_file_name, 'w', encoding="UTF-8") as f:
## 파일 사이즈만큼 recv
data = s.recv(int(reSize))
f.write(data.decode())
s.close()
else:
s.close()
time.sleep(0.5)
def runclient_put(port, file_name):
host ='127.0.0.1' # 접속할 서버명
port = 4001
#아래 폴더에 있는 pingtest 결과를 server로 전송
file_name = 'ping_result.txt'
directory = os.getcwd()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
msg = "put_file"
s.sendall(msg.encode())
put_file_check = s.recv(1024)
if put_file_check.decode() == "put_file_ack":
getFileSize(file_name, directory)
s.sendall(getFileSize(file_name, directory).encode())
reReady = s.recv(1024)
if reReady.decode() == "ready":
s.sendall(getFileData(file_name, directory).encode())
s.close()
else:
s.close()
time.sleep(10)
runclient_get_thread = threading.Thread(target=runclient_get, args=(port,file_name))
runclient_get_thread.start()
def getFileSize(file_name, directory):
fileSize = os.path.getsize(directory+"/"+file_name)
return str(fileSize)
def getFileData(file_name, directory):
with open(directory+"/"+file_name, 'r', encoding="UTF-8") as f:
data = ""
## 파일이 매번 각 라인을 읽어 리턴할 수 있기 때문에 라인마다 끊어서 저장
for line in f:
data += line
return data
조금 길어졌는데 하나하나 보도록 한다.
def runclient_get(port, file_name):
host ='127.0.0.1' # 접속할 서버명
port = 4001
#아래 폴더에 pingtest가 필요한 장비리스트를 저장
pre_directory = os.getcwd()
pre_file_name = 'pre_system_list.txt'
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
msg = "get_file"
s.sendall(msg.encode())
server에서의 설정과 비슷하다.
Host IP와 Port를 설정하고, server로부터 받아올 파일명을 설정한다.
다음으로 socket을 열고 server PC에 연결한 다음에 파일을 받고 싶으니 'get_file' 이라는 전송해서 파일을 받는 동작을 수행한다.
get_file_check = s.recv(1024)
if get_file_check.decode() == "get_file_ack":
s.sendall(pre_file_name.encode())
reSize = s.recv(1024)
reSize = reSize.decode()
msg = "ready"
s.sendall(msg.encode())
with open(pre_directory + '/' + pre_file_name, 'w', encoding="UTF-8") as f:
## 파일 사이즈만큼 recv
data = s.recv(int(reSize))
f.write(data.decode())
s.close()
else:
s.close()
client는 server가 'get_file'이라는 메시지를 받은는지 안받았는지 알고 싶어졌다. 갑자기 TCP 3 Handshake의 SYN SYNACK SYNACK 이 생각나서 비슷하게 만들어 보기로 했었고, client에서 'get_file'을 보내면 서버도 'get_file_ack'를 보내도록 해봤다. 이후의 절차는 이전과 동일하게 파일 사이즈를 확인하고, server로부터 파일을 받는다.
def runclient_put(port, file_name):
host ='127.0.0.1' # 접속할 서버명
port = 4001
#아래 폴더에 있는 pingtest 결과를 server로 전송
file_name = 'ping_result.txt'
directory = os.getcwd()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
msg = "put_file"
s.sendall(msg.encode())
다음은 client가 server쪽으로 파일을 보내기 위해 runclient_put이라는 함수를 만들었다.
이전 'get_file'을 보내는 대신 이번에는 server쪽으로 'put_file'을 보내도록 설정하였다.
if put_file_check.decode() == "put_file_ack":
getFileSize(file_name, directory)
s.sendall(getFileSize(file_name, directory).encode())
reReady = s.recv(1024)
if reReady.decode() == "ready":
s.sendall(getFileData(file_name, directory).encode())
s.close()
else:
s.close()
그 외 나머지는 동일하다. server로부터 'put_file_ack'가 오면 설정된 파일을 server로 전송한다.
time.sleep(10)
runclient_get_thread = threading.Thread(target=runclient_get, args=(port,file_name))
runclient_get_thread.start()
이전과 마찬가지로 client에서 server로 파일을 지속적으로 전달하기 위해 함수 안에 다른 함수를 추가하였다. 이로써 client PC가 server PC에 있는 장비 리스트 파일을 가져와서 해당 파일 안에 있는 장비들에 대해 ping 테스트를 수행하고, 결과를 저장한 후 다시 server PC로 해당 결과를 보내주도록 하는 것이 거의 완성되었다.
이제 client PC에서 ping 테스트를 수행하고 결과만 저장하면 되고(방법은 이전 글을 참조), 완성 코드는 아래와 같다.
#FTP TEST
import time
import re
import sys
import socket
import threading
import os
import pymysql
import shutil
from os.path import exists
from datetime import datetime
from ping3 import ping,verbose_ping
print("CLIENT")
def runclient_get(port, file_name):
host ='127.0.0.1' # 접속할 서버명
port = 4001
#아래 폴더에 pingtest가 필요한 장비리스트를 저장
pre_directory = os.getcwd()
pre_file_name = 'pre_system_list.txt'
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
msg = "get_file"
s.sendall(msg.encode())
get_file_check = s.recv(1024)
if get_file_check.decode() == "get_file_ack":
s.sendall(pre_file_name.encode())
reSize = s.recv(1024)
reSize = reSize.decode()
msg = "ready"
s.sendall(msg.encode())
with open(pre_directory + '/' + pre_file_name, 'w', encoding="UTF-8") as f:
## 파일 사이즈만큼 recv
data = s.recv(int(reSize))
f.write(data.decode())
s.close()
else:
s.close()
time.sleep(0.5)
runclient_thread = threading.Thread(target=ping_test)
runclient_thread.start()
def runclient_put(port, file_name):
host ='127.0.0.1' # 접속할 서버명
port = 4001
#아래 폴더에 있는 pingtest 결과를 server로 전송
file_name = 'ping_result.txt'
directory = os.getcwd()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
msg = "put_file"
s.sendall(msg.encode())
put_file_check = s.recv(1024)
if put_file_check.decode() == "put_file_ack":
getFileSize(file_name, directory)
s.sendall(getFileSize(file_name, directory).encode())
reReady = s.recv(1024)
if reReady.decode() == "ready":
s.sendall(getFileData(file_name, directory).encode())
s.close()
else:
s.close()
time.sleep(10)
runclient_get_thread = threading.Thread(target=runclient_get, args=(port,file_name))
runclient_get_thread.start()
def getFileSize(file_name, directory):
fileSize = os.path.getsize(directory+"/"+file_name)
return str(fileSize)
def getFileData(file_name, directory):
with open(directory+"/"+file_name, 'r', encoding="UTF-8") as f:
data = ""
## 파일이 매번 각 라인을 읽어 리턴할 수 있기 때문에 라인마다 끊어서 저장
for line in f:
data += line
return data
def ping_test():
# with open('C:/Users/dnsndata/Desktop/bomis/data/ping/system_list.txt', 'r') as f:
with open(os.getcwd() + '/pre_system_list.txt', 'r') as f:
for line in f.readlines():
site = line.split(',')
# sys.stdout = open('C:/Users/dnsndata/Desktop/bomis/data/ping/ping_raw.txt','a')
sys.stdout = open(os.getcwd() + '/ping_raw.txt','a')
if site[0][0] == '#':
print(str(datetime.now())[:19] + ',' + site[1] + ',' + site[0][1:16] + ',' + '0')
elif site[0][0] == '=':
# pass
# sys.stdout = open('C:/Users/dnsndata/Desktop/bomis/data/ping/ping_raw.txt','w')
sys.stdout = open(os.getcwd() + '/ping_raw.txt','w')
elif site[0][0] == '<':
pass
else:
result = ping(site[0], timeout=0.5)
if result == None:
print(str(datetime.now())[:19] + ',' + site[1] + ',' + site[0] + ',' + '-1')
else:
print(str(datetime.now())[:19] + ',' + site[1] + ',' + site[0] + ',' + '0')
f.close()
# shutil.copy('C:/Users/dnsndata/Desktop/bomis/data/ping/ping_raw.txt', 'C:/Users/dnsndata/Desktop/bomis/data/ping/ping_result.txt')
shutil.copy(os.getcwd() + '/ping_raw.txt', os.getcwd() + '/ping_result.txt')
time.sleep(0.5)
f.close()
runclient_put_thread = threading.Thread(target=runclient_put, args=(port,file_name))
runclient_put_thread.start()
runclient_get_thread = threading.Thread(target=runclient_get, args=(port,file_name))
runclient_get_thread.start()
while True:
time.sleep(1)
pass
결국 ping 테스트를 하기 위한 def ping_test() 함수만 추가했고, 나머지는 동일하다. 함수의 순서는 먼저 get 함수를 통해 server의 파일을 가져오고, ping_test 라는 함수를 통해 ping 테스트를 수행하고 결과를 저장한 후 put 함수를 통해 server쪽에 data를 저장한다.
이제 이를 활용하면 서버에서는 여러개의 socket을 열어 두고, 여러대의 Client PC는 각각 병렬적으로 파일을 가져와서 ping테스트를 수행하고 결과를 다시 server로 저장할 수 있게 되었다. server는 해당 결과들을 다시 murge해서 저장만 하면 끝!
실제로는 여러대의 PC에서 수행하는 코드를 작성했지만 여기에 추가해두지는 않겠다. 혹시라도 궁금하면 댓글이나 쪽지 주세요.
감사합니다.
'Python > Do something' 카테고리의 다른 글
python - Appium 으로 스마트폰 무선 연동하고 컨트롤하기 - 1 (0) | 2022.05.15 |
---|---|
python - JSON 파일 가공하여 DB에 넣기넣기 (0) | 2022.05.15 |
python - PC(Server ~ Client)간 파일 전송 (Socket) (0) | 2022.05.15 |
python - ping test 후 결과를 txt 파일로 저장하고, mysql DB에 저장하기 (ping3, pymysql) (0) | 2022.05.15 |
python - mysql DB 생성, txt 파일을 DB Table에 넣기 (pymysql) (0) | 2022.05.15 |