I am currently working on a simple school project on game server. I have one conceptual problem for dealing with the modified data in the TCPserver.
I have created a simple program for the issues. The clients will first type a number and the number will send to the server. This number will be processed in the server by adding 1 and the server will send the modified number back to clients. In addition, the modified number will also save to the totalNum, which store all the sum of clients modified number.
I expect the server will "store" the clients number. For instance, when client A send number 5, the totalNum would be 6. After that, when client B send number 8, client A data should still maintain in the sever, I expect the totalNum to be 6+9 = 15.
Server.py
#!/usr/bin/python3
import socket
import threading
class ServerThread(threading.Thread):
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
def run(self):
connectionSocket, addr = self.client
totalNum = 0
number = connectionSocket.recv(1024)
newNumber = number.decode()
newNumber = int(newNumber)
editedNumber = newNumber + 1
#add the edited number to the totalNum in the server
totalNum = editedNumber + totalNum
print (totalNum)
#change back to str for sending
editedNumber = str(editedNumber)
connectionSocket.send(editedNumber.encode())
connectionSocket.close()
class ServerMain:
def server_run(self):
serverPort = 12000
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.bind( ("", serverPort) )
serverSocket.listen(5)
print("The server is ready to receive")
while True:
client = serverSocket.accept()
t = ServerThread(client)
t.start()
if __name__ == '__main__':
server = ServerMain()
server.server_run()
Client.py
#!/usr/bin/python3
import socket
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverName = "localhost"
serverPort = 12000
clientSocket.connect( (serverName, serverPort) )
# Get input for sending
number = input("Input a number:")
clientSocket.send(number.encode())
modifiednumber = clientSocket.recv(1024)
print("From Server:", modifiednumber.decode())
clientSocket.close()
updated server code(changing the totalNum to class variable):
#!/usr/bin/python3
import socket
import threading
class ServerThread(threading.Thread):
totalNum = 0
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
def run(self):
connectionSocket, addr = self.client
number = connectionSocket.recv(1024)
newNumber = number.decode()
newNumber = int(newNumber)
editedNumber = newNumber + 1
#add the edited number to the totalNum in the server
self.totalNum = editedNumber + self.totalNum
print (self.totalNum)
#change back to str for sending
editedNumber = str(editedNumber)
connectionSocket.send(editedNumber.encode())
connectionSocket.close()
class ServerMain:
def server_run(self):
serverPort = 12000
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.bind( ("", serverPort) )
serverSocket.listen(5)
print("The server is ready to receive")
while True:
client = serverSocket.accept()
t = ServerThread(client)
t.start()
if __name__ == '__main__':
server = ServerMain()
server.server_run()
Youre pretty close. move totalNum = 0 to be a class level variable, i.e.
class ServerThread(threading.Thread):
totalNum = 0
def __init__(self, client):
and anywhere you use totalNum use it as ServerThread.totalNum
The problem was that you had totalNum as a scoped variable so it would get lost every time you finished run(). the issue arises if we made totalNum an instance variable. when run() ends the thread ends and the value gets lost again! by making it a class variable its always there and always shared. you can also access it from outside the class with ServerThread.totalNum
Related
I'm trying to learn python, doing exercise watching video online. I'm currently trying to do a multi-threading chat server but I think I have trouble understanding the way threads work.
Here's my code:
<!-- language: lang-py -->
import socket
import threading
class ThreadedServer(threading.Thread):
def __init__(self, host, port, buff_size):
threading.Thread.__init__(self)
self.host = host
self.port = port
self.buff_size = buff_size
self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.host, self.port))
def listen(self):
self.sock.listen(5)
while True:
print('Waiting for a connexion...')
self.c, self.addr = self.sock.accept()
listening = listenToClient(self.c, self.addr)
class listenToClient(threading.Thread):
def __init__(self, client, address):
threading.Thread.__init__(self)
global socks
socks.append(client)
client.send(("Bienvenue sur le serveur, entre ton pseudo: ").encode('utf-8'))
self.name = client.recv(buff_size).decode('utf-8')
welcome = "Welcome " + self.name + "! If ever you want to quit, type {quit}"
client.send(welcome.encode('utf-8'))
msg = self.name + " has joined the chat"
broadcast(msg)
while True:
msg = client.recv(buff_size).decode('utf-8')
if msg != '{quit}':
broadcast(msg, self.name + ": ")
print('TEST6')
else:
client.close()
leave_msg = self.name + " has left"
broadcast(leave_msg)
break
def broadcast(msg, prefix=''):
for sck in socks:
content = prefix + msg
sck.send(content.encode('utf-8'))
def Main():
ThreadedServer(host,port,buff_size).listen()
host = '127.0.0.1'
port = 10000
buff_size = 1024
socks = []
if __name__ == '__main__':
Main()
The code is working ok for only one user.
I think the problem is in my comprehension of threads.
In my mind, when I type
listening = listenToClient(self.c, self.addr)
a new thread is created running in parallel with the first one.
What I see in reality is that my new thread is indeed executing and ends in the infinite loop in here(managing whatever my first user connected is typing):
while True:
msg = client.recv(buff_size).decode('utf-8')
if msg != '{quit}':
broadcast(msg, self.name + ": ")
print('TEST6')
But I thought as the original thread would have continued looping here:
while True:
print('Waiting for a connexion...')
self.c, self.addr = self.sock.accept()
listening = listenToClient(self.c, self.addr)
Printing 'Waiting for a connection' and waiting for a second connection.
But it is not the case.
I'm pretty sure it's a misunderstanding on my side, I spent hours trying and trying changing everything etc, at some point the listenToClient() thread was just a method in the ThreadedServer(). But now I'm very lost and I don't know what to do, so I came here for help.
Thanks in advance,
Vincent
I would like to ask you a question about a trouble i found while i was coding to improve my skill in TCP communication. Basically i first learn about socket and how to open server/client socket and communication. So i wrote one class for server and one for client, i test it and i found they work very fine for what i care: this is the server
class server_class:
def __init__(self, sock=None):
if sock is None:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else:
self.sock = sock
def bind(self,host, port):
self.sock.bind((host, port))
def listen(self,client):
self.sock.listen(client)
def close_client(self):
self.client.close()
def accept(self):
self.client,self.c_addr=self.sock.accept()
def invia(self,MSGLEN,msg):
totalsent = 0
if(len(msg)<MSGLEN):
while(len(msg)<MSGLEN):
msg=msg+'*'
while totalsent < MSGLEN:
sent = self.client.send(msg[totalsent:].encode('ascii'))
if sent == 0:
raise RuntimeError
totalsent = totalsent + sent
def ricevi(self,MSGLEN):
msg = ''
while len(msg) < MSGLEN:
chunk = self.client.recv(MSGLEN-len(msg)).decode('ascii')
if chunk == '':
raise RuntimeError
msg = msg + chunk
i=0
messaggio=''
while(i<MSGLEN):
if(msg[i]!='*'):
mess=msg[i]
messaggio=messaggio+mess
else:
pass
i+=1
return messaggio
and this is the client:
class client_class:
def __init__(self, sock=None):
if sock is None:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else:
self.sock = sock
def connect(self,host,port):
self.sock.connect((host, port))
def invia(self,MSGLEN,msg):
totalsent = 0
if(len(msg)<MSGLEN):
while(len(msg)<MSGLEN):
msg=msg+'*'
while totalsent < MSGLEN:
sent = self.sock.send(msg[totalsent:].encode('ascii'))
if sent == 0:
raise RuntimeError
totalsent = totalsent + sent
def ricevi(self,MSGLEN):
msg = ''
while len(msg) < MSGLEN:
chunk = self.sock.recv(MSGLEN-len(msg)).decode('ascii')
if chunk == '':
raise RuntimeError
msg = msg + chunk
i=0
messaggio=''
while(i<MSGLEN):
if(msg[i]!='*'):
mess=msg[i]
else:
pass
messaggio=messaggio+mess
i+=1
return messaggio
So no at all problem at this time. Next step i tried to do was to write a program that while doing some math or GUI or both, keep a server on for communicate to a client the information it work out in math for example. The only way i found to do this is use threading module.
I write 2 function, one for server and one for math (increase x value in while loop) and add in main a GUI. Then i pass each function to a thread.
This is the server function (that use server class defined before):
def server():
global Stop,x
server=server_class()
ip='192.168.1.134'
port=8033
server.bind(ip,port)
Stop=True
client=0
c_addr=0
while(Stop):
server.listen(1)
print("* inizio ascolto su",ip,":",port)
server.accept()
print("* mi sono connesso con",server.c_addr[0],":",server.c_addr[1])
while(Stop):
data=server.ricevi(100)
print(data)
if(data=="disconnetti"):
msg="bye bye"
server.invia(100,msg)
server.close_client()
print("*disconnetto il client")
break
if(data=="inviami x"):
msg=str(x)
server.invia(100,msg)
if(data=="chiudi server"):
print("*chiudo server")
server.close_client()
Stop=False
else:
msg="come?"
server.invia(100,msg)
This is the math function named 'go':
def go():
global x
while(x<10000):
x+=1
time.sleep(1)
Finally the main function is:
finestra=Tk()
finestra.geometry('800x800+300+300')
finestra.title('Prova threading')
testo_0=Label(finestra,text="Valore attuale:").grid(sticky=W,row=0,column=0)
gobutton=Button(finestra,text='Acquisisci',command=lambda: leggi())
gobutton.grid(row=2, column=1)
goo=threading.Thread(target=go)
serv=threading.Thread(target=server)
goo.start()
serv.start()
finestra.mainloop()
So go function increase continuously x value, server function keep a server listening and main thread keep a GUI within which a user can see x value by pressing a botton.
The server knows just 3 commands from client:
1)pass x to client
2)close client
3)close server
for other message it answer as unknown command.
What happen is that the communication doesn't work good; for example when from a client (running on other machine using the client class defined before) ask to server to pass x value (that go function is constantly increasing) happen 2 wrong things:
1) After the first communication other seems delayed, for example the second time i ask x value the server answer as unknown command, third time i send a request for x value it give me a value. Next time its going to answer as unknown and next give me a value and so on.
2) After the first communication too, the values that server pass to client are delayed, so for example if at the same time i send to server request for x and push the botton in GUI for read x value, these are going to be appreciably different.
This is the client script i use:
import time
import socket
class client_class:
def __init__(self, sock=None):
if sock is None:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else:
self.sock = sock
def connect(self,host,port):
self.sock.connect((host, port))
def invia(self,MSGLEN,msg):
totalsent = 0
if(len(msg)<MSGLEN):
while(len(msg)<MSGLEN):
msg=msg+'*'
while totalsent < MSGLEN:
sent = self.sock.send(msg[totalsent:].encode('ascii'))
if sent == 0:
raise RuntimeError
totalsent = totalsent + sent
def ricevi(self,MSGLEN):
msg = ''
while len(msg) < MSGLEN:
chunk = self.sock.recv(MSGLEN-len(msg)).decode('ascii')
if chunk == '':
raise RuntimeError
msg = msg + chunk
i=0
messaggio=''
while(i<MSGLEN):
if(msg[i]!='*'):
mess=msg[i]
else:
pass
messaggio=messaggio+mess
i+=1
return messaggio
client=mysocket()
ip='192.168.1.134'
port=8033
client.connect(ip,port)
while(True):
print("inserire comando da inviare (max 100 Bytes)")
msg=input().encode('ascii')
client.invia(100,msg)
print(client.ricevi(100).decode('ascii'))
Any help will be appreciate, thank you very much and sorry for bad english
UPDATE
I found that if i close socket on each communication, so for example everytime i need to send a message i open a client socket and close it after send it, could solve the problem, no error and no delay.
Can't explain me why, so if anyone could answer it will be appreciated.
Thank you very much
From my understanding python can only run 1 thread at a time so if I were to do something like this
import socket, select
from threading import Thread
import config
class Source(Thread):
def __init__(self):
self._wait = False
self._host = (config.HOST, config.PORT + 1)
self._socket = socket.socket()
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._sock = None
self._connections = []
self._mount = "None"
self._writers = []
self._createServer()
Thread.__init__(self)
def _createServer(self):
self._socket.bind(self._host)
self._socket.listen(2)
self._connections.append(self._socket)
self._audioPackets=[]
def _addPacket(self, packet):
self._audioPackets.append(packet)
def _removePacket(self, packet):
self._audioPackets.remove(packet)
def _getPacket(self):
if len(self._audioPackets) > 0:
return self._audioPackets[0]
else:
return None
def _sendOK(self, sock):
sock.send("OK")
def _sendDenied(self, sock):
sock.send("DENIED")
def _sendMount(self, sock):
sock.send("mount:{0}".format(self._mount))
def _sendBufPacket(self, sock, packet):
packet = "buffer:%s" % packet
sock.send(packet)
def recv(self, sock, data):
data = data.split(":", 1)
if data[0] == "WAIT": self._wait = True
elif data[0] == "STOP_WAITING": self._wait = False
elif data[0] == "LOGIN":
if data[1] == config.SOURCE_AUTH:
self._source = sock
self._sendOK(sock)
else:
self._sendClose(sock)
elif data[0] == "MOUNT":
if self._source == sock:
self._mount = data[1]
else:
self._sendClose(sock)
elif data[0] == "CLIENT":
self._sendMount(sock)
self._writers.append(sock)
def _sendCloseAll(self):
for sock in self._connections:
sock.send("CLOSE")
sock.close()
def _sendClose(self, sock):
sock.send("CLOSE")
sock.close()
def main(self):
while True:
rl, wl, xl = select.select(self._connections, self._writers, [], 0.2)
for sock in rl:
if sock == self._socket:
con, ip = sock.accept()
self._connections.append(con)
else:
data = sock.recv(config.BUFFER)
if data:
self.recv(sock, data)
else:
if sock in self._writers:
self._writers.remove(sock)
if sock in self._connections:
self._connections.remove(sock)
for sock in wl:
packet = self._getPacket()
if packet != None:
self._sendBufPacket(sock, packet)
def run(self):
self.main()
class writeThread(Thread):
def __init__(self):
self.running = False
def make(self, client):
self.client = client
self.running = True
def run(self):
host = (config.HOST, config.PORT+1)
sock = socket.socket()
sock.connect(host)
sock.send("CLIENT")
sock.send("MOUNT:mountpoint")
while self.running:
data = sock.recv(config.BUFFER)
if data:
data = data.split(":", 1)
if data[0] == "buffer":
self.client.send(data[1])
elif data[0] == "CLOSE":
self.client.close()
break
if __name__=="__main__":
source = Source()
source.start()
webserver = WebServer()
webserver.runloop()
if I need to build the webserver part I will. But, I'll explain it.
Okay, so basically when someone connects to the websever under the mountpoint that was set, They will get there own personal thread that then grabs the data from Source() and sends it to them. Now say another person connects to the mount point and the last client as well as the source is still going. Wouldn't the new client be blocked from getting the Source data considering there are two active threads?
Your understanding of how Threads work in Python seems to be incorrect, based on the question you are asking. If used correctly, threads will not be blocking: you can instantiate multiple thread with Python. The limitation is that, due to the Global Interpreter Lock (GIL), you cannot get the full parallelism expected in thread programming (e.g. simultaneous execution and thus, reduced runtime).
What is going to happen in your case is that the two threads will take, together, the same amount of time that they would take if they were executed sequentially (although that is not necessarily what happens in practice).
Okay, I have copy and pasted some Python3 code that I have already written for a project that I am currently working on. With modification, you can make this code serve your purposes.
The code uses multiprocessing and multithreading. For my purposes, I am using multiprocessing so that sockets will run on one processor, and I can run a GUI program on another processor. You can remove the multiprocessor part if you prefer. The code below runs a socket message server. The server will listen for clients one at a time. Once a client has connected, a new thread will be initiated to handle all the communications between the server and each client. The server will then continue to search for for clients. At the moment however, the server only listens to data being sent from each client, and then it prints it to the terminal. With a small amount of effort, you can modify my code to send information from the server to each client individually.
import multiprocessing
import threading
from threading import Thread
class ThreadedServer(object):
def __init__(self, host, port):
self.host = host
self.port = port
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
self.sock.bind((self.host, self.port))
def listen(self):
self.sock.listen(3) #Allow 3 Clients to connect to this server
while True:
#The program will search for one client at a time
print("Searching for Client")
client, address = self.sock.accept()
print(address, " is connected")
#client.settimeout(60)
#Once a client has been found, start a individual client thread
d = threading.Thread(target = self.listenToClient, args=(client, address))
d.daemon = True
d.start()
def listenToClient(self, client, address):
size = 1024
while True:
try:
data = client.recv(size)
if not data:
break
if data:
print(data)
#client.send(response)
else:
raise error('Client disconnected')
except:
client.close()
return False
def dataSharingHost():
#Using Sockets to send information between Processes
#This is the server Function
#ThreadServer(Host_IP, Port_Number), for LocalHost use ''
ThreadedServer('', 8000).listen()
def Main():
commServer = multiprocessing.Process(target=dataSharingHost, args=())
commServer.daemon = True
commServer.start()
if __name__== '__main__':
Main()
And to be fair, my code is modified from https://www.youtube.com/watch?v=qELZAi4yra8 . The client code is covered in those videos. I think the 3rd video covers the multiple client connects.
When one client sends an input, in the second client it's not printed the message (i.e. message not received).
When second client sends an input, in the first client it's not printed the message (i.e. message not received).
I learned that client.send(message) in the server script, it sends the message to all the clients connected to the server so you don't need to specify to who to send it.
Client
import socket
import threading
class sendThread(threading.Thread):
def __init__(self, ip, port, client):
threading.Thread.__init__(self)
self.port = port
self.ip = ip
self.client = client
print "[+] New send thread started for "+ip+":"+str(port) + "...Everything went successful!"
def run(self):
while True:
data = raw_input("Enter command:")
self.client.send(data)
class receiveThread(threading.Thread):
def __init__(self, ip, port, client):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.client = client
print "[+] New receive thread started for "+ip+":"+str(port) + "...Everything went successful!"
def run(self):
print "Entered run method"
size = 1024
while True:
data = self.client.recv(size)
if data != "" or data:
print data
def client():
port = 1724
ip = '127.0.0.1'
print "Connection from : "+ip+":"+str(port)
client = socket.socket()
client.connect((ip, port))
receive = receiveThread(ip, port, client)
print "b1"
receive.start()
print "b2"
send = sendThread(ip, port, client)
print "b3"
send.start()
while send.isAlive() and receive.isAlive():
continue
print "-----END of While TRUE------"
print "Client disconnected..."
client()
Server
import socket
import threading
global num
num = 0
class serverThread(threading.Thread):
def __init__(self, client, address):
global num
num = num + 1
self.id = num
threading.Thread.__init__(self)
self.client = client
self.address = address
print "serverThread init finished-" + str(self.id)
def run(self):
print "r1 num-" + str(self.id)
size = 1024
while True:
print "r2-" + str(self.id)
data = self.client.recv(size)
print "r3..... " + data
print "r4-" + str(self.id)
if data:
print "r5-" + str(self.id)
response = data
self.client.send(response)
print "r6-" + str(self.id)
else:
print "r7-" + str(self.id)
raise Exception('Client disconnected-' + str(self.id) )
def create(ipHost, port):
server = socket.socket()
server.bind((ipHost, port))
print "The server was created successfully."
return server
def listen(server):
server.listen(5)
client, address = server.accept()
c1 = serverThread(client, address)
c1.start()
client, address = server.accept()
c2 = serverThread(client, address)
c2.start()
print "finished both threads created"
while c1.isAlive() and c2.isAlive():
continue
server = create("0.0.0.0", 1724)
listen(server)
Both clients have the following output:
Connection from : 127.0.0.1:1720
[+] New receive thread started for 127.0.0.1:1720...Everything went successful!
b1
Entered run method
b2
[+] New send thread started for 127.0.0.1:1720...Everything went successful!
b3
Enter command:Hello
Enter command:Hello
This is the case when both clients send the same message (Thus both will have Enter command:Hello in their output.
If client 1 sends "Hello" and client 2 sends "Hi": Client 1 will have as output Hello and client 2 will have as output Hi, instead of both receiving and printing in the output Hi and Hello.
Note: Will be much more grateful if you show me my mistake, instead of showing me a new version.
Your mistake is, that you only send the text from the client to the same client back:
self.client.send(response)
I.e. Client c1 and client c2 are independent and don't know of each other.
How do I connect with multiple clients? Once connected with multiple clients how do I receive individual data from each? From what I know I need to use something called "Asyncore". How do I implement this?
Client Class
import socket
class Client():
def __init__(self):
self.host = 'localhost'
self.port = 5000
self.s = socket.socket()
self.s.connect((self.host, self.port))
self.s.send(str.encode(input("What is your name ")))
x = Client()
Host Class
import socket
class Host():
def __init__(self):
self.host = 'localhost'
self.port = 5000
self.s = socket.socket()
self.s.bind((self.host, self.port))
self.s.listen(5)
self.c, self.addr = self.s.accept()
print("User from " + str(self.addr) + " has connected")
while True:
data = self.c.recv(1024)
if not data:
break
print(str(self.addr) +" name is " + data.decode("utf-8"))
#c.send(str.encode(whatever))
self.c.close()
x = Host()
Are you looking for this one? asyncore
Here is the link where you can find all the info related to async socket handler
https://docs.python.org/2/library/asyncore.html
EDIT: dano comment is great too
Enjoy