I'm doing a little localhost socket project in python, I currently only have the thread implementation so multiple clients can connect to the same server. I have a specific message from client to disconnect from server which is DISSCONECT_MESSAGE = b'0' when the server recognize the byte zero it should close the connection between the server and the client who sent it. I handle this by using an if statement to catch the byte zero.
The clients send function always encode the message to send it to the server, so if user input is 0 it pass through encode and now I have b'0', when server recieve this message it decode it so again I have 0, thats why in this if statement I encode it again to know if the message is 0:
if msg.encode() == DISSCONECT_MESSAGE:
connected = False
I tested handle_client by adding a print before the conn.close () function but it never shows that print so it never leaves the while loop and therefore never closes the connection to the client.
Here is my code:
server.py
import socket
import threading
HEADER = 64
PORT = 65500
SERVER = ''
ADDRESS = (SERVER,PORT)
FORMAT = 'utf-8'
DISSCONECT_MESSAGE = b'0'
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDRESS)
def handle_client(conn, addr):
print(f"[NEW CONNECTION] {addr} connected")
connected = True
while connected:
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
if msg.encode() == DISSCONECT_MESSAGE:
connected = False
if msg[:2] == "10":
conn.send("[CODE 10] Log in request".encode(FORMAT))
print(f"[{addr}] {msg}")
conn.send("Message recived".encode(FORMAT))
print(f"[{addr}] DISSCONNECTED")
conn.close()
def start():
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
conn, addr = server.accept() # Wait to a new connection
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print(f"[ACTIVE CONNECTIONS] {threading.activeCount()-1}")
print("[STARTING] Server is starting...")
start()
client.py
import socket
HEADER = 64
PORT = 65500
FORMAT = 'utf-8'
DISSCONECT_MESSAGE = b'0'
SERVER = 'localhost'
ADDRESS = (SERVER,PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 65500))
def send(msg):
message = msg.encode(FORMAT)
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
send_length += b' ' * (HEADER - len(send_length))
client.send(send_length)
client.send(message)
print(client.recv(1024).decode(FORMAT))
while True:
msg = input()
if msg.encode() == DISSCONECT_MESSAGE:
print("[DISSCONECTED FROM SERVER]")
client.send(DISSCONECT_MESSAGE)
else:
send(msg)
Can anyone tell me what I'm doing wrong?
First, check the return values of send and recv. There is no guarantee all bytes were sent (try sendall) and and recv returns 0 (socket closed) or 1-length bytes requested, not necessarily the total number requested.
As far as your main bug, you've sent the character zero ('0') and your server receives it as the length with:
msg_length = conn.recv(HEADER).decode(FORMAT)
The result is a length of 0. msg then becomes an empty string with:
msg = conn.recv(msg_length).decode(FORMAT)
msg.encode() == b'' (an empty string) not b'0' a length 1 string containing a zero character.
Instead, you could abort when you get a length of 0. Or the normal way to disconnect is to close the connection. Then the server receives b'' (a length zero recv) which indicates a closed connection.
Related
basically this code is a server for a project that I am doing the client sends over 600 photos which then I want to be stored in a folder called incoming. first error was me running into my pickle data being Truncated while being sent to my server to try rectify this. I added a chunking system where it splits the data up into smaller chunks to make sure there is a smaller change of data being lost. doing this in theory should work. but when it has finished chunking all my data the server just quits out of the 'handle_client' function but gives me no errors and in turn leaves my client hanging and unresponsive until I shut the server down
import pickle
import socket
import os
import threading
HEADER = 64
PORT = 5050
SERVER = 'Left this out for obvious reasons'
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "..DISCONNECT"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
def handle_client(conn, addr):
print(f"[NEW CONNECTION] {addr} connected.")
connected = True
while connected:
data_length = conn.recv(HEADER).decode(FORMAT)
if data_length:
data_length = int(data_length)
recv_data = conn.recv(data_length)
bytes_received = 0
while bytes_received < data_length:
chunk = conn.recv(4096)
recv_data += chunk
bytes_received += len(chunk)
data = pickle.loads(recv_data)
if data == DISCONNECT_MESSAGE:
connected = False
try:
for files in os.listdir('incoming'):
file_path = os.path.join('incoming', files)
os.remove(file_path)
except:
print('[SERVER_ERROR] No files inside ./Incoming/ folder')
for file in data:
with open(os.path.join('incoming', file['name']), "wb") as f:
f.write(file['data'])
print('[SAVING] Server has saved file')
with open('siamesemodel.h5','rb') as f:
data = f.read()
pickledData = pickle.dumps(data)
data_length = len(pickledData)
send_length = str(data_length).encode(FORMAT)
send_length += b' ' * (HEADER - len(send_length))
conn.send(send_length)
conn.send(pickledData)
def start():
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")
print("[STARTING] server is starting...")
start()
tried debugging to no avail
So now I have a server and client script. I'm trying to upload a file from the client to the server. However, the data from the file in the client will be cut out by the HEADER size. How do I send multiple packets under the same send command to the server?
server.py:
import socket
import threading
HEADER=2048
PORT=5050
SERVER=socket.gethostbyname(socket.gethostname())
ADDR=(SERVER,PORT)
FORMAT='utf-8'
DISCONNECT_MESSAGE='!DISCONNECT'
SEPARATOR='<SEPERATE>'
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(ADDR)
def handle_client(conn,addr):
print(f'[NEW CONNECTION] {addr} connected.')
connected=True
while connected:
data=conn.recv(HEADER).decode(FORMAT)
if data==DISCONNECT_MESSAGE:
connected=False
else:
data=data.split(SEPARATOR)
file=open(data[0],'w')
file.write(data[1])
print('file received')
conn.send('file received'.encode(FORMAT))
conn.close()
print(f'[DISCONNECT] {addr} disconnected')
def start():
server.listen()
print(f'[LISTENING] Server is listening on {SERVER}')
while True:
conn,addr=server.accept()
thread=threading.Thread(target=handle_client,args=(conn,addr))
thread.start()
print(f'[ACTIVE CONNECTIONS] {threading.activeCount()-1}')
print("[STARTING] server is starting...")
start()
client.py:
import socket
HEADER=2048
PORT=5050
FORMAT='utf-8'
DISCONNECT_MESSAGE='!DISCONNECT'
SEPARATOR='<SEPERATE>'
SERVER=socket.gethostbyname(socket.gethostname())
ADDR=(SERVER,PORT)
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(ADDR)
def send(msg):
message=msg.encode(FORMAT)
client.send(message)
print(client.recv(HEADER).decode(FORMAT))
file=open('question_pool.csv','r')
data=file.read()
send(f'question_pool.csv{SEPARATOR}{data}')
file.close()
send(DISCONNECT_MESSAGE)
In short, you want to send multiple chunks of any file that is larger than your HEADER size. Split the file into chunks smaller than the HEADER size, and send each chunk individually. Then when all the chunks are set, send a message that says the whole file has been sent so that the server can save it.
Here is my code for the solution described above:
server.py:
import socket
import threading
HEADER = 2048
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!DISCONNECT'
SEPARATOR = '<SEPERATE>'
FILE_FINISHED_SENDING = '<!FILE_SENT!>'
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(ADDR)
def handle_client(conn, addr):
print(f'[NEW CONNECTION] {addr} connected.')
connected = True
current_file = None
while connected:
data = conn.recv(HEADER).decode(FORMAT)
if data == DISCONNECT_MESSAGE:
connected = False
elif data == FILE_FINISHED_SENDING:
current_file.close()
current_file = None
conn.send(b'file received.')
else:
data = data.split(SEPARATOR)
if len(data) == 2 and data[1] == '':
# The name of the file was sent, more will follow.
current_file = open(data[0], 'w')
conn.send(b'filename recieved')
else:
# File data was send, so write it to the current file
current_file.write(data[1])
print('chunk of file recv''d')
conn.send(b'chunk received')
conn.close()
print(f'[DISCONNECT] {addr} disconnected')
def start():
server.listen()
print(f'[LISTENING] Server is listening on {SERVER}')
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn,addr))
thread.start()
print(f'[ACTIVE CONNECTIONS] {threading.activeCount()-1}')
print("[STARTING] server is starting...")
start()
client.py:
import socket
from pathlib import Path
HEADER = 2048
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!DISCONNECT'
SEPARATOR = '<SEPERATE>'
FILE_FINISHED_SENDING = '<!FILE_SENT!>'
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
def chunker(string: str, size: int):
return (string[pos:pos + size] for pos in range(0, len(string), size))
def send(msg):
message = msg.encode(FORMAT)
client.send(message)
print(client.recv(HEADER).decode(FORMAT))
def send_file(filepath: str):
with open(filepath, 'r', encoding=FORMAT) as f:
data = f.read()
first_bits = f'{Path(filepath).name}{SEPARATOR}' # Easy way of getting just a file's name from its path
send(first_bits) # Send the filename to the server
for chunk in chunker(data, HEADER-48): # Leave a safe buffer
# Send each chunk of the file
send(f"{SEPARATOR}{chunk}")
# Tell the server that's all for this file.
# Now it can close the file object.
send(FILE_FINISHED_SENDING)
send_file("/path/to/file.html")
send(DISCONNECT_MESSAGE)
Tips:
make sure that your special messages like SEPARATOR, FILE_FINISHED_SENDING and DISCONNECT_MESSAGE are NOT going to appear in the files you are sending. Otherwise things might get wonky.
You may want to read files as raw bytes when you send them through the socket, instead of reading as strings, encoding, decoding, etc. This way you could send binary files such as .mp3, for example.
I'm sorry if this is a really dumb question, I'm sure someone could probably find the answer in a minute, I've just recently been getting into Python sockets.
I want my server to continually send a stream of data to my client, but for some reason, after receiving the first piece of data my client just does not receive/print out any more data.
My simplified server.py:
while True:
#do some stuff with dfwebsites here
senddata = True
#time.sleep(1)
#Starting the sending data part
HEADERSIZE = 10
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(),1236))
s.listen(5) #queue of five
while senddata==True:
clientsocket, address = s.accept()
print(f"Connection from {address} has been established!")
d = pd.DataFrame(dfwebsites)
msg = pickle.dumps(d)
#header to specify length
#msg = "Welcome to the server!"
msg = bytes(f'{len(msg):<{HEADERSIZE}}','utf-8')+msg
clientsocket.send(msg) #type of bytes is utf-8
#clientsocket.close()
senddata = False
My client.py:
import socket
import pickle
import time
HEADERSIZE = 10
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 1236))
while True:
full_msg = b''
new_msg = True
while True:
msg = s.recv(1024)
if new_msg:
print("new msg len:",msg[:HEADERSIZE])
msglen = int(msg[:HEADERSIZE])
new_msg = False
print(f"full message length: {msglen}")
full_msg += msg
print(len(full_msg))
if len(full_msg)-HEADERSIZE == msglen:
print("full msg recvd")
print(full_msg[HEADERSIZE:])
print(pickle.loads(full_msg[HEADERSIZE:]))
new_msg = True
full_msg = b""
Why can it not receive more than one peice of data?
Thank you so much for your help! I would really love even a comment telling me how to improve my qeustion!
To send more than one message to each client, you need a loop after the accept() has happened.
#!/usr/bin/env python
import socket
import pickle
import pandas as pd
HEADERSIZE = 10
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(),1236))
s.listen(5) # only one client at a time, but let up to five wait in line
while True:
clientsocket, address = s.accept()
print(f"Connection from {address} has been established!")
while senddata:
# FIXME: refresh dfwebsites every time through this loop?
d = pd.DataFrame(dfwebsites)
msg = pickle.dumps(d)
msg = bytes(f'{len(msg):<{HEADERSIZE}}','utf-8')+msg
try:
clientsocket.send(msg) #type of bytes is utf-8
except socket.error as exc:
print(f"Ending connection from client {address} due to {exc}")
# FIXME: Do the below only when you want to disconnect a client
#senddata = False
clientsocket.close()
I've been experimenting recently with password protecting sockets in python and I've come across an issue.
When checking the clients input with the servers set password the server seems to think the set password isn't the same as the password input by the user.
My first script: server.py
import socket
import threading
from requests import get
import uuid
HEADER = 64
PORT = 9090
#To connect over the internet change SERVER to your public IP
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
def handle_client(conn, addr, password):
print(f"[PNet] NEW CONNECTION: {addr} connected.")
connected = True
while connected:
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
print(msg)
if msg == password:
connected = True
if msg != password:
print("Huh?")
if msg == DISCONNECT_MESSAGE:
connected = False
print(f"[PNet] {addr}: {msg}")
conn.send("[PNet] CLIENT: Message received.".encode(FORMAT))
conn.close()
def start():
print("[PNet] STARTING: Server is starting...")
print("[PNet] STARTING: Generating key...")
password = uuid.uuid4()
print(f"[PNet] FINALIZING: Key generated: {password}")
server.listen()
print(f"[PNet] LISTENING: Server is listening on {SERVER}.")
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr, password))
thread.start()
#print(f"[PNet] ACTIVE CONNECTIONS: {threading.activeCount() - 1}")
start()
and my second script: client.py
import socket
HEADER = 64
PORT = 9090
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
SERVER = input("[PNet] CLIENT: Server IP: ")
PASS = input("[PNet] SERVER: Enter password: ")
ADDR = (SERVER, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
def send(msg):
message = msg.encode(FORMAT)
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
send_length += b' ' * (HEADER - len(send_length))
client.send(send_length)
client.send(message)
print(client.recv(2048).decode(FORMAT))
send(PASS)
connected = True
while connected:
msg = input("[PNet] CLIENT: Send a message: ")
if msg != DISCONNECT_MESSAGE:
send(msg)
else:
send(msg)
connected = False
when run, and the password is copied exactly from the terminal it still returns False. Thanks for your help :)
You are comapring UUID object with a string, thats why its returning false,
to check, put a print before comparing,
...
print(type(msg),type(password)) # will print <class 'str'> <class 'uuid.UUID'>
if msg == password:
connected = True
...
to solve it pass the password as string,
password = str(uuid.uuid4())
I have a server-client setup that works as follows:
The client connects to the server.
The client sends the server a 64-byte message telling the server how much data to read
The server reads that many bytes of data, responds, and the process repeats.
When the client is finished, it sends the server a null message
The server sees that the message length is 0 and closes the connection.
This seems to work fine for the first pass. After the server responds though, it doesn't wait for the client to send more data. Instead the server immediately reads 64 bytes. Since the client hasn't responded, the length of the message is 0 and the connection is closed.
I'm unsure why the server is not pausing until the client sends more data.
Here is the server loop:
self.__stop = False
while not self.__stop:
if self.client_sock:
# Check if the client is still connected and if data is available
try:
rdy_read, rdy_write, sock_err = select.select(
[self.client_sock, ], [self.client_sock, ], [], 5)
except select.error as err:
self.stop()
return
if len(rdy_read) > 0:
# msg length will be sent as bytes
read_data = self.client_sock.recv(64)
# Check if the socket has been closed
if read_data == 0:
self.stop()
else:
msg_length = int(read_data)
msg = self.client_sock.recv(msg_length)
response = f"{[12, 15, 66]}\n"
msg_size = padded_size_of_msg(response)
self.client_sock.send(msg_size)
self.client_sock.send(f"{response}".encode('utf-8'))
else:
print(
f"[THREAD {self.number}] No client connected.")
self.stop()
self.close()
The function padded_size_of_msg() is to calculate the length of the message, pad that number to be 64-bytes, then send that to the client:
def padded_size_of_msg(msg):
msg_length = len(msg)
send_length = str(msg_length).encode('utf-8')
send_length += b' ' * (64- len(send_length))
return send_length
The complete class declaration is below:
class ServerSocketThread(Thread):
def __init__(self, client_sock, client_addr, number):
Thread.__init__(self)
self.client_sock = client_sock
self.client_addr = client_addr
self.number = number
def run(self):
self.__stop = False
while not self.__stop:
if self.client_sock:
# Check if the client is still connected and if data is available
try:
rdy_read, rdy_write, sock_err = select.select(
[self.client_sock, ], [self.client_sock, ], [], 5)
except select.error as err:
print(
f"[THREAD {self.number}] Select() failed on socket with {self.client_addr}")
self.stop()
return
if len(rdy_read) > 0:
# msg length will be sent as bytes
read_data = self.client_sock.recv(64)
# Check if the socket has been closed
if read_data == 0:
print(
f"[THREAD {self.number}] {self.client_addr} closed the socket")
self.stop()
else:
msg_length = int(read_data)
# Client will send msg as bytes. No need to decode to str
msg = self.client_sock.recv(msg_length)
response = f"{[12, 15, 66]}\n"
# Send outputs to client as bytes
msg_size = padded_size_of_msg(response)
self.client_sock.send(msg_size)
self.client_sock.send(f"{response}".encode('utf-8'))
else:
print(
f"[THREAD {self.number}] No client connected.")
self.stop()
self.close()
def stop(self):
self.__stop = True
def close(self):
if self.client_sock:
print(
f"[THREAD {self.number}] Closing conn with {self.client_addr}")
self.client_sock.close()
This ended up being an issue with using nc from the command line.
If I were to try and send "test" from the client, it would register the size as 4-bytes and tell the server to read 4-bytes. From the terminal, nc would instead send "test\n". The newline character '\n' would not be read and instead waited in the queue. When the second recv() was called, it immediately read the '\n' and took that as an indication to close the connection.