How to implement threading in a Socket Programming Chat Application - python

So i'm also including the server side of the code, but the issues in the client side. It's a simple TCP client socket code.The thing is that in line 21, after the first while loop (i've also commented in the code to where i'm referring to), i'm asking for user input.
What then happens is that when more user connects, the chat screen of any user doesn't gets updated unless they press enter, as you can see, it only continues after an input is given to the 'message' variable.
Now i do know that threading need to be done in here, but i've not really got enough knowledge related to that. So if someone could kindly guide me or help me modify the code so that the chat gets updated without the need to enter.
Cient Code (Issue in line 21, after first while loop...commented)
from socket import *
import select
import errno
import sys
header_length = 10
ip = "127.0.0.1"
port = 1234
my_username = input("Username: ")
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect((ip, port))
client_socket.setblocking(False)
username = my_username.encode()
username_header = f"{len(username):<{header_length}}".encode()
client_socket.send(username_header + username)
while True:
#Where my issue is
message = input(f"{my_username} > ")
if message:
message = message.encode()
message_header = f"{len(message):<{header_length}}".encode()
client_socket.send(message_header + message)
try:
while True:
#receive messages
username_header = client_socket.recv(header_length)
if not len(username_header):
print("Connection closed by the server...")
sys.exit()
username_length = int(username_header.decode().strip())
username = client_socket.recv(username_length).decode()
message_header = client_socket.recv(header_length)
message_length = int(message_header.decode().strip())
message = client_socket.recv(message_length).decode()
print(f"{username} > {message}")
except IOError as e:
if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
print("Reading error: ", str(e))
sys.exit()
continue
except Exception as e:
print("General error: ", str(e))
sys.exit()
Server Code (Just if someone need):
from socket import *
import select
header_length = 10
ip = "127.0.0.1"
port = 1234
server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server_socket.bind((ip, port))
server_socket.listen()
socket_list = [server_socket]
clients = {}
#Handles message receiving
def recieve_message(client_socket):
try:
message_header = client_socket.recv(header_length)
if not len(message_header):
return False
message_length = int(message_header.decode().strip())
return {'header': message_header, 'data': client_socket.recv(message_length)}
except:
return False
print(f'Listening for connections on {ip}:{port}...')
while True:
read_sockets, _, exception_sockets = select.select(socket_list, [], socket_list)
for notified_socket in read_sockets:
if notified_socket == server_socket:
client_socket, client_address = server_socket.accept()
user = recieve_message(client_socket)
if user is False:
continue
socket_list.append(client_socket)
clients[client_socket] = user
print(f"Accepted new connection from {client_address[0]}:{client_address[1]} username:{user['data'].decode()}")
else:
message = recieve_message(notified_socket)
if message is False:
print(f"Closed connection from {clients[notified_socket]['data'].decode()}")
socket_list.remove(notified_socket)
del clients[notified_socket]
continue
user = clients[notified_socket]
print(f"Recieved messasge from {user['data'].decode()}: {message['data'].decode()}")
for client_socket in clients:
if client_socket != notified_socket:
client_socket.send(user['header'] + user['data'] + message['header'] + message['data'])
for notified_socket in exception_sockets:
socket_list.remove(notified_socket)
del clients[notified_socket]
I've also included the image...as you can see as i typed hello in cliend 1's window, client 2's doesnt show it. And it will not until i input something and press enter
Thanks a lot :)

Why do you not use select.select for the client as you do for the server?
It works perfectly.
Linux version
from socket import *
import select
import errno
import sys
def prompt(username):
sys.stdout.write(f"{username} > ")
sys.stdout.flush()
header_length = 10
ip = "127.0.0.1"
port = 1234
my_username = input("Username: ")
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect((ip, port))
client_socket.setblocking(False)
username = my_username.encode()
username_header = f"{len(username):<{header_length}}".encode()
client_socket.send(username_header + username)
while True:
socket_list = [sys.stdin, client_socket]
prompt(my_username)
read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
for socket in read_sockets:
try:
if socket == sys.stdin:
message = sys.stdin.readline()
message = message.encode()
message_header = f"{len(message):<{header_length}}".encode()
client_socket.send(message_header + message)
elif socket == client_socket:
username_header = client_socket.recv(header_length)
if not len(username_header):
print("Connection closed by the server...")
sys.exit()
username_length = int(username_header.decode().strip())
username = client_socket.recv(username_length).decode()
message_header = client_socket.recv(header_length)
message_length = int(message_header.decode().strip())
message = client_socket.recv(message_length).decode()
print(f"\n{username} > {message}")
except IOError as e:
if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
print("Reading error: ", str(e))
sys.exit()
continue
except Exception as e:
print("General error: ", str(e))
sys.exit()
Windows/Linux version (because of select restriction)
Note:
File objects on Windows are not acceptable, but sockets are. On Windows, the
underlying select() function is provided by the WinSock library, and does not
handle file descriptors that don’t originate from WinSock.
import threading
from socket import *
import select
import errno
import sys
def prompt(username):
sys.stdout.write(f"{username} > ")
sys.stdout.flush()
def redirect_sdtin(dest):
for ln in sys.stdin:
dest.send(ln.encode())
header_length = 10
ip = "127.0.0.1"
port = 1234
my_username = input("Username: ")
stdin_in, stdin_out = socketpair()
threading.Thread(target=redirect_sdtin, args=(stdin_in,), daemon=True).start()
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect((ip, port))
client_socket.setblocking(False)
username = my_username.encode()
username_header = f"{len(username):<{header_length}}".encode()
client_socket.send(username_header + username)
while True:
socket_list = [stdin_out, client_socket]
prompt(my_username)
read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
for socket in read_sockets:
try:
if socket == stdin_out:
message = stdin_out.recv(1024)
message_header = f"{len(message):<{header_length}}".encode()
client_socket.send(message_header + message)
elif socket == client_socket:
username_header = client_socket.recv(header_length)
if not len(username_header):
print("Connection closed by the server...")
sys.exit()
username_length = int(username_header.decode().strip())
username = client_socket.recv(username_length).decode()
message_header = client_socket.recv(header_length)
message_length = int(message_header.decode().strip())
message = client_socket.recv(message_length).decode()
print(f"\n{username} > {message}")
except IOError as e:
if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
print("Reading error: ", str(e))
sys.exit()
continue
except Exception as e:
print("General error: ", str(e))
sys.exit()

Related

How to break while loop when a new message arrives?

I have used Python socket in ESP as a server and Laptop as a client. I customized the socket codes from this site. When I send the loop as the client input, I enter a loop on the server. I don't know how the while loop is broken when I send a word other than loop, For example "Hello".
server.py:
import socket
host = ''
port = 5560
def setupServer():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Socket created.")
try:
s.bind((host, port))
except socket.error as msg:
print(msg)
print("Socket bind comlete.")
return s
def setupConnection():
s.listen(1)
conn, address = s.accept()
print("Connected to: " + address[0] + ":" + str(address[1]))
return conn
def Hello_():
print('Hello')
def Loop_():
while True:
print('yes')
def dataTransfer(conn):
while True:
data = conn.recv(1024)
data = data.decode('utf-8')
dataMessage = data.split(' ', 1)
command = dataMessage[0]
if command == 'loop':
Loop_()
if command == 'Hello':
Hello_()
else:
print("X")
conn.close()
s = setupServer()
while True:
try:
conn = setupConnection()
dataTransfer(conn)
except:
break
client.py
import socket
host = '192.168.56.1'
port = 5560
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
while True:
command = input("Enter your command: ")
s.send(str.encode(command))
s.close()
I know your time is valuable and I appreciate your attention for spending time for help me.
If you want the Loop_() method to return when more data is received on the socket, you can modify the method so that it calls select() to poll the socket to see if more data has arrived, as shown below. (Note that I've added a conn argument to the Loop_() method so I can pass in the socket to check it)
import select
[...]
def Loop_(conn):
while True:
print('yes')
inReady, outReady, exReady = select.select([conn], [], [], 0.0)
if (conn in inReady):
print('more data has arrived at the TCP socket, returning from Loop_()')
break
def dataTransfer(conn):
while True:
data = conn.recv(1024)
data = data.decode('utf-8')
dataMessage = data.split(' ', 1)
command = dataMessage[0]
if command == 'loop':
Loop_(conn)
if command == 'Hello':
Hello_()
else:
print("X")
conn.close()

How to print something above an input?

Im making a messaging thing with sockets.
One problem I am having is that you need input to get the users message
for example
user >> hello
user2 >> hi
you >>
how would you receive a message while having an input and display it above the input.
so if you received a message it would be:
user >> hello
user2 >> hi
user 1 >> nice to meet you
you >>
This is the code i have so far:
import socket
import select
import errno
import sys
HEADER_LENGTH = 10
IP = "127.0.0.1"
PORT = 1234
my_username = input('Username: ')
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((IP,PORT))
client_socket.setblocking(False)
username = my_username.encode("utf-8")
username_header = f"{len(username):<{HEADER_LENGTH}}".encode("utf-8")
client_socket.send(username_header + username)
def send_message():
message = input(f"{my_username} > ")
if message:
message = message.encode("utf-8")
message_header = f"{len(message):<{HEADER_LENGTH}}".encode("utf-8")
client_socket.send(message_header + message)
def receive_message():
try:
while True:
#receive things
username_header = client_socket.recv(HEADER_LENGTH)
if not len(username_header):
print("connection closed by the server")
sys.exit()
username_length = int(username_header.decode("utf-8"))
username = client_socket.recv(username_length).decode("utf-8")
message_header = client_socket.recv(HEADER_LENGTH)
message_length = int(message_header.decode("utf-8"))
message = client_socket.recv(message_length).decode("utf-8")
print(f"{username} > {message}")
except IOError as E:
if E.errno != errno.EAGAIN and E.errno != errno.EWOULDBLOCK:
print('Reading error',str(e))
sys.exit()
except Exception as e:
print('General Error ',str(e))
sys.exit()
running = True
while running:
send_message()
receive_message()

How to send files in "chunks" by socket?

I'm trying to send a large file (.avi) over socket by sending the content of the file in chunks (a little bit like torrents). The problem is that the script doesn't send the file. I'm out of ideas here.
Any help or twerking of the script would be very appreciated.
Server:
import socket
HOST = ""
PORT = 8050
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, PORT))
sock.listen(1)
conn, addr = sock.accept()
print("Connected by ", str(addr))
while 1:
data = conn.recv(1024)
if data.decode("utf-8") == 'GET':
with open(downFile,'rb') as output:
l = output.read(1024)
while (l):
conn.send(l)
l = output.read(1024)
output.close()
conn.close()
Client:
import socket
HOST = "localhost"
PORT = 8050
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST,PORT))
while 1:
message = input()
sock.send(bytes(message,'UTF-8'))
conn.send(str.encode('GET'))
with open(downFile, 'wb+') as output:
while True:
rec = str(sock.recv(1024), "utf-8")
if not rec:
break
output.write(rec)
output.close()
print('Success!')
sock.close()
Here are a working client and server that should demonstrate transferring a file over a socket. I made some assumptions about what your code was supposed to do, for example, I assumed that the initial message the client sent to the server was supposed to be the name of the file to download.
The code also includes some additional functionality for the server to return an error message to the client. Before running the code, make sure the directory specified by DOWNLOAD_DIR exists.
Client:
import socket
import sys
import os
HOST = "localhost"
PORT = 8050
BUF_SIZE = 4096
DOWNLOAD_DIR = "downloads"
def download_file(s, down_file):
s.send(str.encode("GET\n" + down_file))
rec = s.recv(BUF_SIZE)
if not rec:
return "server closed connection"
if rec[:2].decode("utf-8") != 'OK':
return "server error: " + rec.decode("utf-8")
rec = rec[:2]
if DOWNLOAD_DIR:
down_file = os.path.join(DOWNLOAD_DIR, down_file)
with open(down_file, 'wb') as output:
if rec:
output.write(rec)
while True:
rec = s.recv(BUF_SIZE)
if not rec:
break
output.write(rec)
print('Success!')
return None
if DOWNLOAD_DIR and not os.path.isdir(DOWNLOAD_DIR):
print('no such directory "%s"' % (DOWNLOAD_DIR,), file=sys.stderr)
sys.exit(1)
while 1:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((HOST, PORT))
except Exception as e:
print("cannot connect to server:", e, file=sys.stderr)
break
file_name = input("\nFile to get: ")
if not file_name:
sock.close()
break
err = download_file(sock, file_name)
if err:
print(err, file=sys.stderr)
sock.close()
Server:
import socket
import sys
import os
HOST = ""
PORT = 8050
BUF_SIZE = 4096
def recv_dl_file(conn):
data = conn.recv(1024)
if not data:
print("Client finished")
return None, None
# Get command and filename
try:
cmd, down_file = data.decode("utf-8").split("\n")
except:
return None, "cannot parse client request"
if cmd != 'GET':
return None, "unknown command: " + cmd
print(cmd, down_file)
if not os.path.isfile(down_file):
return None, 'no such file "%s"'%(down_file,)
return down_file, None
def send_file(conn):
down_file, err = recv_dl_file(conn)
if err:
print(err, file=sys.stderr)
conn.send(bytes(err, 'utf-8'))
return True
if not down_file:
return False # client all done
# Tell client it is OK to receive file
sent = conn.send(bytes('OK', 'utf-8'))
total_sent = 0
with open(down_file,'rb') as output:
while True:
data = output.read(BUF_SIZE)
if not data:
break
conn.sendall(data)
total_sent += len(data)
print("finished sending", total_sent, "bytes")
return True
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, PORT))
sock.listen(1)
keep_going = 1
while keep_going:
conn, addr = sock.accept()
print("Connected by", str(addr))
keep_going = send_file(conn)
conn.close() # close clien connection
print()
sock.close() # close listener

Multi-client server

I read a little bit about using select function instead of using threads but I can't understand how to use it in my code.
EDIT:
I tried to run the following code, the program prints "Client connected", but the problem is that it doesn't print "WORKING" which means it will not send data to the client..What's wrong with my code?
import socket
import select
SERVER_PORT = 3000
SERVER_IP = "127.0.0.1"
MESSAGE_START = 3
CONNECTION_LIST = []
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind((SERVER_IP, SERVER_PORT))
server_sock.listen(10)
CONNECTION_LIST.append(server_sock)
def broadcast_data (sock, message):
for socket in CONNECTION_LIST:
if socket != server_sock and socket != sock :
try :
socket.send(message)
except :
socket.close()
CONNECTION_LIST.remove(socket)
def main():
while (True):
read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])
for sock in read_sockets:
if sock == server_sock:
client_sock, address_sock = server_sock.accept()
CONNECTION_LIST.append(client_sock)
print "Client:"+str(address_sock)+" connected!"
broadcast_data(client_sock,"Welcome to the server")
else:
print "Working:"
ans = ""
while (True):
msg = client_sock.recv(1024)
if (msg == "Hello"):
ans = "Hey"
elif (msg == "QUIT"):
broadcast_data(client_sock,"Bye")
sock.close()
CONNECTION_LIST.remove(sock)
break
else:
ans = "Error!"
broadcast_data(client_sock,ans)
server_sock.close()
main()

str not callable sendto python

attempting message encryption with a basic client to host connection
client code:
import socket
import datetime
import time
import threading
tLock = threading.Lock()
shutdown = False
def receving(name, sock):
while not shutdown:
try:
tLock.acquire()
while True:
data, addr = socket.recvfrom(1024)
print (str(data))
except:
pass
finally:
tLock.release()
host = '127.0.0.1'
port = 0
server = ('127.0.0.1',5000)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host, port))
s.setblocking(0)
rT = threading.Thread(target=receving, args=("RecvThread",s))
rT.start()
alias = input("Name: ")
IP=int(socket.gethostbyname(socket.gethostname()).replace(".","5"))
time=(int(datetime.datetime.now().strftime("%Y%m%d%H%M%S")))
qw=(int(str((time)+(IP))))
a=int("934")
b=int("346")
c=int("926")
d=int("9522")
e=int("7334")
f=int("5856")
g=int("2432")
h=int("2027")
i=int("7024")
j=int("828")
k=int("798")
m=int("593")
n=int("662")
l=int("5950")
o=int("357")
p=int("506")
q=int("237")
r=int("98")
s=int("372")
t=int("636")
u=int("553")
v=int("255")
w=int("298")
x=int("8822")
y=int("458")
z=int("657")
space=("633")
msg=input("")
msg=msg.replace("a",(str(a)))
msg=msg.replace("b",(str(b)))
msg=msg.replace("c",(str(c)))
msg=msg.replace("d",(str(d)))
msg=msg.replace("e",(str(e)))
msg=msg.replace("f",(str(f)))
msg=msg.replace("g",(str(g)))
msg=msg.replace("h",(str(h)))
msg=msg.replace("i",(str(i)))
msg=msg.replace("j",(str(j)))
msg=msg.replace("k",(str(k)))
msg=msg.replace("m",(str(m)))
msg=msg.replace("n",(str(n)))
msg=msg.replace("l",(str(l)))
msg=msg.replace("o",(str(o)))
msg=msg.replace("p",(str(p)))
msg=msg.replace("q",(str(q)))
msg=msg.replace("r",(str(r)))
msg=msg.replace("s",(str(s)))
msg=msg.replace("t",(str(t)))
msg=msg.replace("u",(str(u)))
msg=msg.replace("v",(str(v)))
msg=msg.replace("w",(str(w)))
msg=msg.replace("x",(str(x)))
msg=msg.replace("y",(str(y)))
msg=msg.replace("z",(str(z)))
msg=msg.replace(" ",(str(space)))
print(msg)
msg=int(msg)
msg=int(msg)*(qw)
print(msg)
fileb=open("key.txt","w")
filec=fileb.write(str(qw))
fileb.close()
file=open("msg decrypt.txt","w")
filea=file.write(str(msg))
file.close()
msg=(str(e)(msg))
print(IP)
print(qw)
if msg != 'q':
if msg != '':
s.sendto(alias.encode() + ": ".encode() + (str(msg).encode)(), server)
tLock.acquire()
msg = input(alias + "-> ")
tLock.release()
shudown = True
rT.join()
s.close()
host code:
import socket
import time
host = '127.0.0.1'
port = 5000
clients = []
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host,port))
s.setblocking(0)
quitting = False
print ("Server Started.")
while not quitting:
try:
data, addr = s.recvfrom(1024)
if "Quit" in str(data):
quitting = True
if addr not in clients:
clients.append(addr)
print (time.ctime(time.time()) + str(addr) + ": :" + str(data))
for client in clients:
s.sendto(data, client)
except:
pass
s.close()
Im struggling as my poor excuse of a encryption is mostly numbers so therefore when im sending using the sendto function only uses str`s or so I think?
either way I get the error:
Traceback (most recent call last):
File "H:\client 2.py", line 103, in <module>
msg=(str(e)(msg))
TypeError: 'str' object is not callable
If msg is an index you should write :
msg = str(e)[msg]

Categories

Resources