I would like to write a very simple rock-paper-scissors game in Python. I would like to have 1 server and 2 clients. After connecting to the server, the server should send a message to each client to choose (r/p/s). After this, each client should send back their choice and the server then decides about the winner, and then it informs the clients about the winner.
This is what I have so far:
This is the server.py
import socket
from threading import Thread
class ClientThread(Thread):
def __init__(self, ip, port):
Thread.__init__(self)
self.ip = ip
self.port = port
print("[+] New server socket thread started for " + ip + ":" + str(port))
def run(self):
while True:
data = conn.recv(1024).decode()
print("Server received data:", data)
MESSAGE = input("Server response: ")
if MESSAGE == 'exit':
break
conn.send(MESSAGE.encode())
TCP_IP = '0.0.0.0'
TCP_PORT = 2004
BUFFER_SIZE = 1024
tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpServer.bind((TCP_IP, TCP_PORT))
threads = []
while True:
tcpServer.listen(4)
print("Multithreaded Python server : Waiting for connections from TCP clients...")
(conn, (ip, port)) = tcpServer.accept()
newthread = ClientThread(ip, port)
newthread.start()
threads.append(newthread)
for t in threads:
t.join()
This is clientA.py:
import socket
host = socket.gethostname()
port = 2004
BUFFER_SIZE = 1024
MESSAGE = input("tcpClientA:")
tcpClientA = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpClientA.connect((host, port))
while MESSAGE != 'exit':
tcpClientA.send(MESSAGE.encode())
data = tcpClientA.recv(BUFFER_SIZE).decode()
print(" ClientA received data:", data)
MESSAGE = input("tcpClientA:")
tcpClientA.close()
This is clientB.py:
import socket
host = socket.gethostname()
port = 2004
BUFFER_SIZE = 1024
MESSAGE = input("tcpClientB:")
tcpClientB = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpClientB.connect((host, port))
while MESSAGE != 'exit':
tcpClientB.send(MESSAGE.encode())
data = tcpClientB.recv(BUFFER_SIZE).decode()
print(" ClientB received data:", data)
MESSAGE = input("tcpClientB:")
tcpClientB.close()
In this code, however, I can only send messages to the server and it can respond. Unfortunately, I don't know, how I can send messages to a specific client in this case. (I need to send different messages to the winner and to the loser).
First, this is not a trivial exercise and it will require a decent understanding of how threads work and how to work with them in Python in particular. What you seem to be missing at the moment is a way of orchestrating the communication from the 2 clients and of keeping track of what each client played so you can decide which client wins.
What currently happens is that when tcpClientA.connect((host, port)) runs on client A, (conn, (ip, port)) = tcpServer.accept() gets unblocked in the server. This creates a new instance of your ClientThread class and the new thread starts listening for data to be sent by client A on the connection. When client B runs, the same happens and you now have 2 threads running and listening for data but no way of knowing which plays what.
What I would suggest is to introduce a new class that will represent the game itself. That class will get the messages from both client and respond to each accordingly. What you will also need is a way of waiting for both clients to have played their turn before being able to decide who wins and what to send to each.
One way to do it would be with Barrier objects.
Related
I'm trying to do a client server exercise in Python and it has to be concurrent, so basically I got a main server and 3 other download servers. These download servers connect just fine to the main one but whenever I want to interact with them I just can't. I tried to cycle through the threads and execute a simple function that sends a different message from the main server to each of the download ones. The problem is that it only sends the message to the last one it cycles through, and it doesn't even send the right message, it sends the message meant to be delivered to the first server to the last one.
So here's my code:
import socket
from threading import Thread
from SocketServer import ThreadingMixIn
import logging
# Multithreaded Python server : TCP Server Socket Thread Pool
class ClientThread(Thread):
def __init__(self,ip,port):
Thread.__init__(self)
self.ip = ip
self.port = port
print "[+] New server socket thread started for " + ip + ":" + str(port)
def run(self):
while True :
data = conn.recv(2048)
print "Server received data:", data
#MESSAGE = raw_input("Multithreaded Python server : Enter Response from Server/Enter exit:")
if not data:
break
if data == 'exit':
break
#conn.send(MESSAGE) # echo
class DServerThread(Thread):
def __init__(self,ip,port):
Thread.__init__(self)
self.ip = ip
self.port = port
def run(self):
while True :
data = conn.recv(2048)
print "Server received data:", data
if not data:
break
if data == 'exit':
break
#conn.send(MESSAGE) # echo
def sendmsg(self,str):
conn.send(str)
# Multithreaded Python server : TCP Server Socket Program Stub
TCP_IP = '0.0.0.0'
TCP_PORT = 2004
sport = 5000
BUFFER_SIZE = 20
dServer=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
dServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
dServer.bind((TCP_IP, sport))
tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpServer.bind((TCP_IP, TCP_PORT))
cthreads = [] #client threads
sthreads = [] #download server threads
controlthreads = [] #Threads to manage the background processes
for i in range(1, 4):
dServer.listen(3)
(conn, (ip,port)) = dServer.accept()
newthread = DServerThread(ip,port)
newthread.start()
sthreads.append(newthread)
for t in sthreads:
print t.getName()
t.sendmsg(t.getName())
The problem to me is on that last loop, but I don't quite get why.
TL;DR: Last loop isn't working as intended, it seems to only execute the function on the last iteration and send the message meant for the first thread.
I am building a simple network chat in Python using UDP, however, when I run the server code on one machine and the client on another, no message is received by the server and no message is sent back to the client by the server script. Here is my code:
Server:
import socket, sys
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', 9997)) #need higher port
while True:
x = raw_input("Enter your message: ")
sent = sock.sendto(x, ('', 9997))
data, address = sock.recvfrom(4096)
print data, " ", address
sock.close()
Client:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
print "Waiting to receive"
data, server = sock.recvfrom(4096)
print data
x = raw_input("Enter message: ")
sent = sock.sendto(x, server)
sock.close()
Does anyone know what I am doing wrong here? Is is possible that code is fine, but the UDP is not reliable enough and is dropping the message?
As I said, since your code seems a little unclear (to me, at least), I'm posting you a very similar working example.
Here's the Server:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('localhost', 1932)
sock.bind(server_address)
BUFFER_SIZE = 4096
try:
while True:
data, address = sock.recvfrom(BUFFER_SIZE)
print "Client sends: ", data
reply = raw_input("Your response:\n")
sock.sendto(reply,address)
except KeyboardInterrupt:
sock.close()
The server creates a socket and binds it to its address and the port it's listening to, 1932 in our case. He waits for an incoming message, asks for a reply, then sends it back to the sender.
Here's the Client:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_address = ('localhost', 1931)
server_address = ('localhost', 1932)
sock.bind(client_address)
BUFFER_SIZE = 4096
try:
first_msg = raw_input("Your first message:\n")
sock.sendto(first_msg,server_address)
while True:
data, address = sock.recvfrom(BUFFER_SIZE)
print "Client sends: ", data
reply = raw_input("Your response:\n")
sock.sendto(reply,address)
except KeyboardInterrupt:
sock.close()
It's very similar to the server, the only difference is that it sends a message before the while loop, in order to start the conversation. Then it just enters the receive/reply loop, just as the server does. It has the server address too, that is different (different port, since I'm on localhost)
The try/catch block is here just to close gracefully the whole process.
I used localhost and different ports on my computer and tested it, and it works. You should just change the addresses to get it working over LAN, and you could keep the same port if the addresses are different, it should work.
I have started to make my own TCP server and client. I was able to get the server and the client to connect over my LAN network. But when I try to have another client connect to make a three way connection, it does not work. What will happen is only when the first connected client has terminated the connection between, the server and the client, can the other client connect and start the chat session. I do not understand why this happens. I have tried threading, loops, and everything else I can think of. I would appreciate any advice. I feel like there is just one small thing i am missing and I can not figure out what it is.
Here is my server:
import socket
from threading import Thread
def whatBeip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 0))
local_ip_address = s.getsockname()[0]
print('Current Local ip: ' + str(local_ip_address))
def clietConnect():
conn, addr = s.accept()
print 'Connection address:', addr
i = True
while i == True:
data = conn.recv(BUFFER_SIZE)
if not data:
break
print('IM Recieved: ' + data)
conn.sendall(data) # echo
whatBeip()
TCP_IP = ''
TCP_PORT = 5005
BUFFER_SIZE = 1024
peopleIn = 4
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(peopleIn)
for client in range(peopleIn):
Thread(target=clietConnect()).start()
conn.close()
Here is my client
import socket
TCP_IP = '10.255.255.3'
TCP_PORT = 5005
BUFFER_SIZE = 1024
MESSAGE = "Hello, World!"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
i = True
while i == True:
s.sendall(raw_input('Type IM: '))
data = s.recv(BUFFER_SIZE)
s.close()
This is your main problem: Thread(target=clietConnect()).start() executes the function clientConnect and uses it's return value as the Thread function (which is None, so the Thread does nothing)
Also have a look at:
1) You should wait for all connections to close instead of conn.close() in the end of the server:
threads = list()
for client in range(peopleIn):
t = Thread(target=clietConnect)
t.start()
threads.append(t)
for t in threads: t.join()
and to close the connection when no data is received:
if not data:
conn.close()
return
2) You probably want to use SO_REUSEADDR [ Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems? , Python: Binding Socket: "Address already in use" ]
3) And have a look at asyncio for python
So I have created a socket program for both client and server as a basic chat. I made it so the server accepts multiple clients with threading, so that is not the problem. I am having trouble sending messages to each client that is connected to the server. I am not trying to have the server send a message it created but rather have client1 sending a message to client2 by going through the server. For some reason it will only send it back to client1.
For example, client1 will say hello and the server will send the same message back to client1 but nothing to client2. I fixed this slightly by making sure the client doesn't receive its own message but client2 is still not receiving the message from the client1.
Any help will be appreciated.
I have tried multiple changes and nothing seems to work. You can look at my code for specifics on how I did things but ask if there are any questions.
Also, there is a question where someone has asked that is similar and I thought it would give me an answer but the responses stopped going through and a solution was never fully given, so please don't just refer me to that question. that is located here: Python 3: Socket server send to multiple clients with sendto() function.
Here's the code:
CLIENT:
import socket
import sys
import thread
#Create a socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Enter username to identify self to others
name = raw_input("Enter username: ") + ": "
#Connect socket to ip and port
host = socket.gethostname()
#host = '192.168.1.10'
server_address = (host, 4441)
sock.connect(server_address)
#function waiting to receive and print a message
def receive(nothing):
while True:
data = sock.recv(1024)
if message != data:
print data
# Send messages
while True:
#arbitrary variable allowing us to have a thread
nothing = (0, 1)
message = name + raw_input("> ")
sock.sendall(message)
#thread to receive a message
thread.start_new_thread(receive, (nothing,))
SERVER:
import socket
import sys
import thread
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the port
host = socket.gethostname()
server_address = (host, 4441)
sock.bind(server_address)
#Listen for incoming connections
sock.listen(5)
print "Waiting for connection..."
#Variable for the number of connections
numbOfConn = 0
#Name of list used for connections
addressList = []
#Function that continuosly searches for connections
def clients(connection, addressList):
while True:
message = connection.recv(1024)
print message
#connection.sendall(message)
#for loop to send message to each
for i in range(0,numbOfConn - 1):
connection.sendto(message, addressList[i])
connection.close()
while True:
#accept a connection
connection, address = sock.accept()
print 'Got connection from', address
numbOfConn += 1
addressList.append((address))
#Thread that calls the function: clients and stores them in a tuple called connection
thread.start_new_thread(clients, (connection, addressList))
sock.close()
Please help me if you can!
EDIT:
I was able to fix it to a certain extent. It is still a little buggy but I am able to send messages back and forth now. I needed to specify the connection socket as well as the address. Here's the updated code:
SERVER
import socket
import sys
import thread
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the port
host = socket.gethostname()
server_address = (host, 4441)
sock.bind(server_address)
#Listen for incoming connections
sock.listen(5)
print "Waiting for connection..."
#Variable for the number of connections
numbOfConn = 0
#Name of list used for connections
addressList = []
connectionList = []
#Function that continuosly searches for connections
def clients(connectionList, addressList):
while True:
for j in range(0,numbOfConn):
message = connectionList[j].recv(1024)
print message
#for loop to send message to each
for i in range(0,numbOfConn):
connectionList[i].sendto(message, addressList[i])
connection.close()
while True:
#accept a connection
connection, address = sock.accept()
print 'Got connection from', address
numbOfConn += 1
addressList.append((address))
connectionList.append((connection))
#Thread that calls the function: clients and stores them in a tuple called connection
thread.start_new_thread(clients, (connectionList, addressList))
sock.close()
I'm am trying to write a client program in Python that can send and receive from the same socket, but it is always giving me the same error which address is already in use. Here is the function I'm trying to write.
def Login():
username=raw_input()
password=raw_input()
message=raw_input()
array=[username,password,message]
TCP_IP = '127.0.0.1'
TCP_PORT = 5563
BUFFER_SIZE = 1024 # Normally 1024, but we want fast response
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((TCP_IP, TCP_PORT))
array_string=pickle.dumps(array)
sock.send(array_string)
sock.close()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((TCP_IP, TCP_PORT))
sock.listen(1)
conn, info = sock.accept()
while 1:
data = serverSocket.recv(1024)
if not data:break
conn.send(data)
conn.close()
There is a bunch of truly newbie errors here.
You can't ever connect a TCP socket to itself. There must be two different sockets.
If you really want to get the data you sent earlier at a listening socket, this listening socket must be created, bound and configured to listen before the client side connects (or, at least, in parallel to this connect attempt, in a few seconds, so the connect attempt will try - but this very likely won't work on localhost).
You can't wait on connect and on accept in the same thread if both are blocking. The simplest approach is to separate the client side and the server side to 2 different programs and run them manually in parallel. Then, after successful debugging, you will be able to do this in different threads of the same process, or using an event-driven engine.
While you may not be able to connect a socket to itself to send and receive data, you might be able to learn from the following example inspired by your code that attempts to do something similar.
import _thread
import pickle
import socket
import time
def main():
"""Run a server in a thread and start a client to talk to it."""
_thread.start_new_thread(run_server, ('', 5563))
run_client('localhost', 5563)
def run_server(host, port):
"""Handle all incoming connections by spawning worker threads."""
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen(5)
while True:
_thread.start_new_thread(handle_connection, server.accept())
def handle_connection(client, address):
"""Answer an incoming question from the connected client."""
print('Incoming connection from', address)
client.settimeout(0.1)
data = recvall(client)
client.shutdown(socket.SHUT_RD)
question = pickle.loads(data)
answer = '''len(username) = {}
len(password) = {}
len(message) = {}'''.format(*map(len, question))
client.sendall(answer.encode())
client.shutdown(socket.SHUT_WR)
client.close()
print('Finished with', address)
def recvall(connection):
"""Receive all data from a socket and return as a bytes object."""
buffer = bytearray()
while True:
try:
data = connection.recv(1 << 12)
except socket.timeout:
pass
else:
if data:
buffer.extend(data)
else:
return bytes(buffer)
def run_client(host, port):
"""Collect information from question and display returned answer."""
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
time.sleep(0.1) # wait for server to start listening for clients
client.connect((host, port))
time.sleep(0.1) # wait for handler thread to display connection
username = input('Username: ')
password = input('Password: ')
message = input('Message: ')
question = pickle.dumps((username, password, message))
client.sendall(question)
client.shutdown(socket.SHUT_WR)
answer = recvall(client)
client.shutdown(socket.SHUT_RD)
client.close()
print(answer.decode())
time.sleep(0.1) # wait for handler to cleanly terminate execution
if __name__ == '__main__':
main()