I have followed a tutorial from a YouTuber under the name of DrapsTV. This tutorial was made in Python 2.7 and makes a networked chat using UDP. I converted this to Python 3 and got everything to work. However, the way the threading is setup is that I have to send a message(or press enter, which is a blank message) to refresh and receive any messages from other clients. Here is the video incase you may need it: https://www.youtube.com/watch?v=PkfwX6RjRaI
And here is my server code:
from socket import *
import time
address = input("IP Address: ")
port = input("Port: ")
clients = []
serversock = socket(AF_INET, SOCK_DGRAM)
serversock.bind((address, int(port)))
serversock.setblocking(0)
quitting = False
print("Server is up and running so far.")
while not quitting:
try:
data, addr = serversock.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.decode()))
for client in clients:
serversock.sendto(data, client)
except:
pass
serversock.close()
Here is my client code:
from socket import *
import threading
import time
tLock = threading.Lock()
shutdown = False
def receiving(name, sock):
while not shutdown:
try:
tLock.acquire()
while True:
data, addr = sock.recvfrom(1024)
print(str(data.decode()))
except:
pass
finally:
tLock.release()
address = input("IP Address: ")
port = 0
server = address, 6090
clientsock = socket(AF_INET, SOCK_DGRAM)
clientsock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
clientsock.bind((address, int(port)))
clientsock.setblocking(0)
rT = threading.Thread(target=receiving, args=("RecvThread", clientsock))
rT.start()
nick = input("How about we get you a nickname: ")
message = input(nick + "> ").encode()
while message != "q":
if message != "":
clientsock.sendto(nick.encode() + "> ".encode() + message, server)
tLock.acquire()
message = input(nick + "> ").encode()
tLock.release()
time.sleep(0.2)
shutdown = True
rT.join()
clientsock.close()
#furas has kindly explained my issue for me: it is not my receiving methods that are flawed(such as my threading or functions), it is the input call that is preventing the client from not receiving anything. So, in order to fix this, I or anyone else having this issue needs to find a way to call for input when a certain button is pressed so that unless your typing, you can receive messages or data.
Thank you #furas! https://stackoverflow.com/users/1832058/furas
Related
First, i ask a lot of questions i know it and i'm sorry... Anyway, i have a few problem on my Application.
I developed a TCP Chat Room with socket and thread modules. Everything is fine but when I write something I see it twice. I only want to see this once and like (ME : Message). Not only that, when I press CTRL + C I can't exit the application and even if I shut down the server, clients can message and the application stops. Finally, I want all messages from the user to be logged. How can I do that?
def broadcast(MESSAGE):
for CLIENT in CLIENT_LIST:
CLIENT.send(MESSAGE)
def handle(CLIENT):
while True:
try:
MESSAGE = CLIENT.recv(1024)
broadcast(MESSAGE)
except:
INDEX = CLIENT_LIST.index(CLIENT)
CLIENT.close()
USERNAME = USERNAME_LIST[INDEX]
broadcast (f'{USERNAME} Left The Chat!'.encode('utf-8'))
USERNAME_LIST.remove(USERNAME)
break
I will share my codes for you. I want learn it and solve with you. I know when i see a problem i'm writing here. Sorry, i'm writing there because i'm beginner so i'm a new in this world :)
import socket
import threading
HOST = "127.0.0.1"
PORT = 7777
ADDR = (HOST,PORT)
SERVER_SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
SERVER_SOCKET.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
SERVER_SOCKET.bind((ADDR))
SERVER_SOCKET.listen()
CLIENT_LIST = []
USERNAME_LIST = []
def broadcast(MESSAGE):
for CLIENT in CLIENT_LIST:
CLIENT.send(MESSAGE)
def handle(CLIENT):
while True:
try:
MESSAGE = CLIENT.recv(1024)
broadcast(MESSAGE)
except:
INDEX = CLIENT_LIST.index(CLIENT)
CLIENT.close()
USERNAME = USERNAME_LIST[INDEX]
broadcast (f'{USERNAME} Left The Chat!'.encode('utf-8'))
USERNAME_LIST.remove(USERNAME)
break
def take():
while True:
CLIENT, ADDRESS = SERVER_SOCKET.accept()
print(f'Connected With {str(ADDRESS)}')
CLIENT.send("USERNAME".encode('utf-8'))
USERNAME = CLIENT.recv(1024).decode('utf-8')
USERNAME_LIST.append(USERNAME)
CLIENT_LIST.append(CLIENT)
print(f'Username Of The Client Is {USERNAME}!')
broadcast(f'{USERNAME} Joined The Chat! Welcome!'.encode('utf-8)'))
CLIENT.send("Connected To The Server!".encode('utf-8'))
thread = threading.Thread(target=handle, args=(CLIENT, ))
thread.start()
print("Server Is Listening...")
take()
Disclaimer: The following are direct solutions, but may not be the best design choice overall.
Seeing message twice:
The CLIENT sending MESSAGE is being broadcasted to again. Exclude sender from broadcast():
def broadcast(MESSAGE, sender):
for CLIENT in CLIENT_LIST:
if CLIENT == sender: continue
CLIENT.send(MESSAGE)
...
broadcast(MESSAGE, CLIENT)
Threads not stopping with CTRL+C:
You can set daemon=True to stop the thread with the parent process, but read the warning here
thread = threading.Thread(target=handle, args=(CLIENT, ), daemon=True)
when I send message from client 1 , the message does not appear immediately in client 2 ,Although it appears immediately on the server, on client 2 i should double press "enter" to show other messages.
when I send message from client 1 , the message does not appear immediately in client 2 ,Although it appears immediately on the server, on client 2 i should double press "enter" to show other messages.
the server code :
import socket
import select
import sys
from _thread import *
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if len(sys.argv) != 3:
print ("Correct usage: script, IP address, port number")
exit()
IP_address = str(sys.argv[1])
Port = int(sys.argv[2])
server.bind((IP_address, Port))
server.listen(5)
list_of_clients = []
def broadcast(message, connection):
for clients in list_of_clients:
if clients!=connection:
try:
clients.send(message.encode())
except:
clients.close()
remove(clients)
def remove(connection):
if connection in list_of_clients:
list_of_clients.remove(connection)
def clientthread(conn, addr):
# sends a message to the client whose user object is conn
conn.send(b'chat made by galal')
while True:
try:
message = conn.recv(2048).decode()
if message:
print ("<" + addr[0] + "> " + message)
message_to_send = "<" + addr[0] + "> " + message
broadcast(message_to_send, conn)
else:
remove(conn)
except:
continue
while True:
conn, addr = server.accept()
list_of_clients.append(conn)
print (addr[0] + " connected")
start_new_thread(clientthread,(conn,addr))
conn.close()
server.close()
the client code :
import socket
import select
import sys
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if len(sys.argv) != 3:
print ("Correct usage: script, IP address, port number")
exit()
IP_address = str(sys.argv[1])
Port = int(sys.argv[2])
server.connect((IP_address, Port))
while True:
# maintains a list of possible input streams
sockets_list = [socket.socket(), server]
read_sockets,write_socket, error_socket = select.select(sockets_list,[],[])
for socks in read_sockets:
if socks != server:
message = sys.stdin.readline()
server.send(message.encode())
sys.stdout.write("<You>")
sys.stdout.write(message)
sys.stdout.flush()
else:
message = socks.recv(2048).decode()
print (message)
server.close()
Your client code waits until pressed enter since there is a
sys.stdin.readline() in your client code (Line 17).
sys.stdin.readline() waits for the press of your enter key, since it tries to read a newline from your stdin (Your input), therefore blocking the whole execution of your client application.
What you have to do is using separate threads for:
Recieving messages + print them
Read from stdin (e.g. through sys.stdin.readline() or input()) and send that message to the server like you're already doing
I think the issue is the client's for loop.
sys.stdin.readline() stops the flow until Enter is pressed, so when you send a message from client1, client2 is still waiting for the user to write its own message. Basically since the first item in read_sockets is always socket.socket() every client's first action will be to wait for user input to send a message. The server on the other hand is always listening and never waits, so it receives it immediately.
To solve this you can either have an async connection, meaning you send 1 message then wait to receive 1 message and so on, or use multithreading and dedicate a thread to receiving and one to sending.
I am trying to establish a peer to peer communication using UDP hole punching. I am first establishing a connection with the server and then trying to make communication between 2 clients, but I am not able to communicate between 2 computers that are behind 2 different NATs as I am not understanding what IP address and port must I enter for the establishment of communication.
Please tell me what changes must I make in the code below so that 2 computers are able to communicate.
P.S : External IP doesn't seem to work and I am not supposed to use any additional tool like ngrok
Server.py
import socket
import struct
import sys
server_listening_port = 12345
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sockfd.bind(("", server_listening_port))
print("Listening on the port " + str(server_listening_port))
client_requests = []
while True:
data, addr = sockfd.recvfrom(32)
client_requests.append(addr)
print("Connection from: " + str(addr))
if len(client_requests) == 2:
break
client_a_ip = client_requests[0][0]
client_a_port = client_requests[0][1]
client_b_ip = client_requests[1][0]
client_b_port = client_requests[1][1]
message = ": "
sockfd.sendto(str(client_a_ip).encode("utf-8") + message.encode("utf-8") + str(client_a_port).encode("utf-8"), client_requests[1])
sockfd.sendto(str(client_b_ip).encode("utf-8") + message.encode("utf-8") + str(client_b_port).encode("utf-8"), client_requests[0])
sockfd.close()
Above is the rendezvous server
ClientA.py
import socket
import struct
import sys
import time
master = ("Server_IP", Port)
#Create dgram udp socket
try:
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = "Hello"
sockfd.bind(('', 0))
sockfd.sendto(message.encode("utf-8"), master)
except socket.error:
print("Failed to create socket")
sys.exit()
# #Receiving peer info from server
peer_data, addr = sockfd.recvfrom(1024)
print (peer_data)
print("trying to communicate with peer")
peer_ip = peer_data.decode("utf-8").split(":")[0]
peer_port = int(peer_data.decode("utf-8").split(":")[1])
peer = (peer_ip, peer_port)
while 1:
message1 = input(str("You:>>"))
message = message.encode("utf-8")
sockfd.sendto(str(message).encode("utf-8"), peer)
incoming_msg, sendaddr = sockfd.recvfrom(1024)
incoming_msg = incoming_msg.decode("utf-8")
print("ClientB:>>",incoming_msg)
Above code is Client A
ClientB.py
import socket #For sockets
import struct
import sys #For exit
import time
master = ("Server_IP", port)
me = ("ClientB_IP", port)
#Create dgram udp socket
try:
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = "Hello"
sockfd.bind(('', 0))
sockfd.sendto(message.encode("utf-8"), master)
except socket.error:
print("Failed to create socket")
sys.exit()
# #Receiving peer info from server
peer_data, addr = sockfd.recvfrom(1024)
print (peer_data)
print("trying to communicate with peer")
peer_ip = peer_data.decode("utf-8").split(":")[0]
peer_port = int(peer_data.decode("utf-8").split(":")[1])
peer = (peer_ip, peer_port)
while 1:
incoming_msg, sendaddr = sockfd.recvfrom(1024)
incoming_msg = incoming_msg.decode("utf-8")
print("ClientA:>>", incoming_msg)
message = input(str("You :>>"))
message = message.encode("utf-8")
sockfd.sendto(str(message).encode("utf-8"), peer)
Above is client B
I am facing problem only in the IP address and port. So , please do help me with it to establish communication between 2 computers behind 2 different NATs
I'm currently on the same problem and I want to program it for myself. With some research I found a really good paper explaining the process in detail.
I let you know if I succeed.
https://bford.info/pub/net/p2pnat/
Consider the following example.
from socket import *
msg = input("Please enter your name ")
msg = msg.encode()
myHostIp = ''
myHostPort = 54040
socket_obj = socket(AF_INET, SOCK_STREAM)
socket_obj.bind((myHostIp,myHostPort))
socket_obj.listen(5)
while True:
connection,address = socket_obj.accept()
print("Client : ",address)
connection.send(msg)
print(socket.getsockname(socket_obj))
print(socket.getsockname(connection))
print(socket.getpeername(socket_obj))
print(socket.getpeername(connection))
while True:
data = connection.recv(1024)
print("This was received from ", data)
connection.close()
In this simple program the output of line print(socket.getsockname(socket_obj)) was (0.0.0.0)
and of the print(socket.getpeername(socket_obj)) was socket_obj is not connected.
My question is: once connection,address = socket_obj.accept() is executed, is the control from socket_obj transferred to connection?
The socket you call .listen() on is a server socket; about all you can do with it is to call .accept() to produce a client socket, which handles all actual communications. One server socket can produce any number of client sockets over its lifetime.
I am trying to make a group chat program, where an unlimited amount of clients may join the server using the same script, it'll work by the server receiving the clients message and sending it to all the connected clients including the sender. I have only managed to make it so that the sender only gets his own message back, but not what another client sends.
I was thinking of storing all the connected client IP's in a list, and sending it to each IP, but I do not know how to change the recpient of socket.send
Server code:
from threading import *
import socket
s = socket.socket()
host = socket.gethostbyname(socket.gethostname())
port = 1337
s.bind((host, port))
s.listen(5)
print("Server host is", host)
def getMainThread():
for thread in enumerate():
if thread.name == 'MainThread':
return thread
return None
class client(Thread):
def __init__(self, socket, address):
Thread.__init__(self)
self.socket = socket
self.address = address
self.start()
def run(self):
main = getMainThread()
while main and main.isAlive():
print(self.address, "has connected!")
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
self.socket.close()
while True:
c, addr = s.accept()
client(c, addr)
clients = [] #list for all client IP's
clients.append(addr)
Also, is there a way so that the client can establish a connection with the server so it doesn't keep poping up on the server.py that client has connected each time it sends a message?
Client code:
import socket
import os
import sys
host = '25.154.84.23'
print("""
=======================================================
=Welcome to Coder77's local internet messaging service=
=======================================================
The current soon to be encrypted server is {0}
You can enter the command /help for a list of commands available
""".format(host))
#username = input("Enter username: ")
username = 'Smile'
print("Now connecting to {0}....".format(host))
def printhelp():
print("""
The following commands are in the current version of this program:
/clear to clear the screen
/username to change your username
/exit to exit
/help for a list of commands
""")
def main():
global username
global host
sock = socket.socket()
try:
sock.connect((host, 1337))
while True:
message2 = input("{0}: ".format(username))
message = ('{0}: {1}'.format(username,message2))
if '/quit' in message:
sys.exit()
if '/clear' in message:
os.system('cls')
if '/help' in message:
printhelp()
if '/username' in message:
username = input("What would you like as your new username? ")
sock.send(bytes(message, 'UTF-8'))
received = sock.recv(8192).decode('utf-8')
print(received)
except socket.error:
print("Host is unreachable")
if __name__ == '__main__':
main()
#
Corrected Server code:
import sys
print(sys.version)
from threading import *
import socket
s = socket.socket()
host = socket.gethostbyname(socket.gethostname())
port = 1337
s.bind((host, port))
s.listen(5)
print("Server host is", host)
def getMainThread():
for thread in enumerate(): # Imported from threading
if thread.name == 'MainThread':
return thread
return None
class Client(Thread):
def __init__(self, socket, address):
Thread.__init__(self)
self.socket = socket
self.address = address
self.start()
def run(self):
main = getMainThread()
print(self.address, "has connected!")
while main and main.isAlive():
message = self.socket.recv(8192).decode('utf-8')
print(message)
self.socket.send(bytes(message, 'UTF-8'))
for each_client in clients:
each_client.socket.send(bytes(message, 'UTF-8'))
while True:
c, addr = s.accept()
this_client = Client(c, addr)
clients = []
clients.append(this_client)
The new code, adapted by gravetii is causing a lot of format errors. What happens now, is the user gets back what he sent, he does not get back what other users send and the user gets back what his previous message was, its terribly confusing. Please run the code, and you'll see as it's very hard to explain.
Example
In your server code, you are doing only a self.socket.send(bytes(message, 'UTF-8')). How can you then expect the server to send the message to all the clients? To do that you would need to iterate through the list of clients and call the send() method on each of their sockets.
while True:
c, addr = s.accept()
client(c, addr)
clients = [] #list for all client IP's
clients.append(addr)
In this code, you are creating a client object but never adding it to the list, then what's the point of creating one?
I think what you want is this:
while True:
c, addr = s.accept()
this_client = client(c, addr)
clients = [] #list for all client IP's
clients.append(this_client)
Then, you can send the message to all the clients by modifying the relevant part of your server code:
def run(self):
main = getMainThread()
while main and main.isAlive():
print(self.address, "has connected!")
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
for each_client in clients:
each_client.socket.send(bytes(message, 'UTF-8'))
Also, why are you closing the connection after sending just one message? I believe your intention is to send more than one message to the server, and in that case, you don't need to close the connection.
Also, it's a better idea to create a class with its name starting with an upper case letter. So you may want to use Client instead of client for the class name.
Now coming to the issue of the message popping up everytime a client says something in your server.py, look at the run() method for the client thread:
def run(self):
main = getMainThread()
while main and main.isAlive():
print(self.address, "has connected!")
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
The thread starts executing as soon as you create the client object, and so the first time when it connects to the server, it is right in showing that message. But then it's incorrect to place the print(self.address, "has connected!") in the while loop. So everytime the client says something, the server sends it back to the client and then the loop runs again, thus displaying the message back again. You need to modify it like so:
def run(self):
print(self.address, "has connected!")
main = getMainThread()
while main and main.isAlive():
message = self.socket.recv(8192).decode('utf-8')
self.socket.send(bytes(message, 'UTF-8'))
Hope this helps!