I wanted to write a server which will be able to handle multiple clients sing select and threads.
I know how to write a server only with threads, or only with select. Now, I wanted to mix them together. But it seems I failed.
Server:
import threading
import socket
import select
class Client(threading.Thread):
'''
Multi-threading clients.
'''
def __init__(self):
threading.Thread.__init__(self)
pass
def setup(self, __client, __address):
self.client = __client
self.address = __address
def connect(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def run(self):
print self.client.__class__
data = self.client.recv(1024)
if data:
self.client.sendall(data)
else:
print "Error:", self.address
class Server(object):
'''
Threading server
'''
def __init__(self):
'''
Constructor
'''
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('127.0.0.1', 9876))
def run(self):
self.server.listen(10)
inputs = [self.server]
while 1:
inready, outready, excready = select.select(inputs, [], []);
for s in inready:
if s == self.server:
sock, address = self.server.accept();
client = Client()
client.setup(sock, address)
client.start()
self.server.close()
if __name__ == '__main__':
server = Server()
server.run()
Client:
import socket
import sys
port = 9876
size = 1024
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
s.connect(('127.0.0.1', port))
except socket.error, (value, message):
if s:
s.close()
print "Could not open socket: " + message
sys.exit(1)
while True:
data = raw_input('> ')
s.sendall(data)
data = s.recv(size)
print "Server sent: %s " % data
s.close()
Client sends and receives only one message and then Im not able to send any more messages. Why? How to improve this? Thank you.
Edit: One error found. There was a loop missing in Client's run method:
import threading import socket import select
class Client(threading.Thread):
'''
Multi-threading clients.
'''
def __init__(self):
threading.Thread.__init__(self)
pass
def setup(self, __client, __address):
self.client = __client
self.address = __address
def connect(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def run(self):
while True:
print self.client.__class__
data = self.client.recv(1024)
if data:
self.client.sendall(data)
else:
print "Error:", self.address
self.client.close()
class Server(object):
'''
Threading server
'''
def __init__(self):
'''
Constructor
'''
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('127.0.0.1', 9876))
def run(self):
self.server.listen(10)
inputs = [self.server]
while 1:
inready, outready, excready = select.select(inputs, [], []);
for s in inready:
if s == self.server:
sock, address = self.server.accept();
client = Client()
client.setup(sock, address)
client.start()
self.server.close()
if __name__ == '__main__':
server = Server()
server.run()
But now, when client disconnects, I have an error on server's side. How to handle this error?
Exception in thread Thread-4:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "/home/testserver.py", line 25, in run
data = self.client.recv(1024)
File "/usr/lib/python2.7/socket.py", line 174, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: [Errno 9] Bad file descriptor
Related
I am trying to simply send messages from two client sockets to one server socket. When I start up the server socket, it works fine, and then when I start up each of the two client sockets (I'm using 3 different terminal windows) everything works fine. If I start up the server and one client, I can send messages from the client to the server. If I then start up the second client, I can send messages from the second client to the server, however now if I go back and try to send messages from the first client to the server, I get a " BlockingIOError: [Errno 11] Resource temporarily unavailable ". What is the problem?
clientclass.py:
from itertools import count
import select
import random
import threading
import time
class Client(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.host = "127.0.0.1"
self.port = 65432
def run(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.id = random.randrange(0, 1000)
print(f"client id {self.id} connected")
self.sock.connect((self.host, self.port))
while True:
text = input('m: ')
self.sock.sendall(text.encode())
server.py:
import socket
from itertools import count
import select
from _thread import start_new_thread
import threading
import time
from queue import Queue
class server(threading.Thread):
def __init__(self):
self.host = "127.0.0.1"
self.port = 65432
threading.Thread.__init__(self)
def run(self):
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))
self.sock.listen(5)
self.sock.setblocking(0)
print(f"server instantiated")
inputs = [self.sock]
outputs = []
message_queues = {}
while inputs:
inputready,outputready,exceptready = select.select(inputs,outputs,inputs)
for input_item in inputready:
if input_item is self.sock:
conn, addr = self.sock.accept()
print("New connection from: ", addr)
conn.setblocking(0)
inputs.append(conn)
else:
data = conn.recv(1024)
if data:
print("Client: " + data.decode())
#message_queues[input_item].put(data)
if input_item not in outputs:
outputs.append(input_item)
else:
if input_item in outputs:
outputs.remove(input_item)
inputs.remove(input_item)
input_item.close()
class Message_Sender(threading.Thread):
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
def run(self):
while True:
text = input('m: ')
server.conn.sendall(text.encode())
server = server()
server.start()
msg = Message_Sender(server)
msg.start()
client instances:
import clientclass
client = clientclass.Client()
client.start()
I saw your code and I think I have solved the problem you might have
I changed some code In a way It receives multiple clients at the same time
server.py:
import socket
from itertools import count
import select
from _thread import start_new_thread
import threading
import time
from queue import Queue
class server(threading.Thread):
def __init__(self):
self.host = "127.0.0.1"
self.port = 65432
threading.Thread.__init__(self)
def run(self):
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))
print(f"server instantiated")
inputs = [self.sock]
outputs = []
message_queues = {}
while True:
self.sock.listen(5)
conn, addr = self.sock.accept()
receive = threading.Thread(target=self.receive_data, args=(conn,))
receive.start()
inputs.append(conn)
def receive_data(self, conn):
while True:
data = conn.recv(1024)
if data:
print("Client: " + data.decode())
# do something
class Message_Sender(threading.Thread):
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
def run(self):
while True:
text = input('m: ')
server.conn.sendall(text.encode())
server = server()
server.start()
msg = Message_Sender(server)
msg.start()
I have made a loop inside the run function, when someone tries to connect it will make a new thread to receive their messages. it wasn't clear to me what you wanted to do with the variables inputs and input_item, ect. but I think you can get really far with this. If you still have any problems feel free to reach out.
I found online a simple multiclient echo server in Python using threads:
#!/usr/bin/env python
import socket
import sys
import threading
class Client(threading.Thread):
def __init__(self, ip, port, connection):
threading.Thread.__init__(self)
self.connection = connection
self.ip = ip
self.port = port
def run(self):
data = self.connection.recv(1024)
if data :
self.connection.sendall(data)
else :
self.connection.close()
class Server:
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.address = (self.ip, self.port)
self.server = None
self.clients = []
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(self.address)
except socket.error, e:
if self.server:
self.server.close()
sys.exit(1)
def run(self):
self.open_socket()
self.server.listen(5)
while True :
connection, (ip, port) = self.server.accept()
c = Client(ip, port, connection)
c.start()
self.clients.append(c)
self.server.close()
if __name__ == '__main__':
s = Server('127.0.0.1', 6666)
s.run()
And I wrote a client:
import socket
import sys
port = 6666
size = 1024
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
s.connect(('127.0.0.1', port))
except socket.error, (value, message):
if s:
s.close()
print "Could not open socket: " + message
sys.exit(1)
data = raw_input('> ')
s.sendall(data)
data = s.recv(size)
print "Server sent: %s " % data
s.close()
Everything works great but I wonder how could I add to the server the functionality of broadcast to be able to echo back the message to all connected clients? Or be able to send a message to a specific client?
I have all clients in server's class, but I do not know how to (and where in the server's code, in Client class or Server class place the code to broadcast and send private messages? Thank you.
EDIT:
New server:
#!/usr/bin/env python
import socket
import sys
import threading
class Client(threading.Thread):
def __init__(self, ip, port, connection):
threading.Thread.__init__(self)
self.connection = connection
self.ip = ip
self.port = port
def run(self):
while True:
data = self.connection.recv(1024)
if data :
self.connection.sendall(data)
else :
break
self.connection.close()
class Server:
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.address = (self.ip, self.port)
self.server = None
self.clients = []
def send_to_all_clients(self, msg):
for client in self.clients :
client.connection.send(msg)
def send_to_client(self, ip, port, msg):
for client in self.clients :
if client.ip == ip and client.port == port :
client.connection.send(msg)
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(self.address)
except socket.error, e:
if self.server:
self.server.close()
sys.exit(1)
def run(self):
self.open_socket()
self.server.listen(5)
while True :
connection, (ip, port) = self.server.accept()
c = Client(ip, port, connection)
c.start()
self.clients.append(c)
self.server.close()
if __name__ == '__main__':
s = Server('127.0.0.1', 6666)
s.run()
New client:
import socket
import sys
port = 6666
size = 1024
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
s.connect(('127.0.0.1', port))
except socket.error, (value, message):
if s:
s.close()
print "Could not open socket: " + message
sys.exit(1)
while True:
data = raw_input('> ')
s.sendall(data)
data = s.recv(size)
print "Server sent: %s " % data
s.close()
Since you already have a list of clients in your Server , you could use it in a function like that :
def send_to_all_clients(self, msg):
for client in self.clients :
client.connection.send(msg)
Then you can make a function to select a specific client :
def send_to_client(self, ip, port, msg):
for client in self.clients :
if client.ip == ip and client.port == port :
client.connection.send(msg)
Note
It's best to add a Send() method to Client and call it to send the msg instead of using client.connection.send(msg)
You could use those functions in the server's run method , if you modify it to handle events
( you could do that with select.select() , or with user input ( KeyboardInterrupt ) , etc , the design is up to you ) .
Also you should modify both client.py and Client , and make them more persistent , because now they close as soon as they sendall or recv .
I hope this gives you some ideas
I am writing a multi-chat which consists of the Client handler, the server and chat record. The Client should allow multiple chats. At the moment it doesn't allow for even one chat, I get an error message after the name has been entered.
This is the Client handler file
from socket import*
from codecs import decode
from chatrecord import ChatRecord
from threading import Thread
from time import ctime
class ClientHandler (Thread):
def __init__(self, client, record):
Thread.__init__(self)
self._client = client
self._record = record
def run(self):
self._client.send(str('Welcome to the chatroom!'))
self._name = decode(self._client.recv(BUFSIZE),CODE)
self._client.send(str(self._record),CODE)
while True:
message = decode(self._client.recv(BUFSIZE),CODE)
if not message:
print('Client disconnected')
self._client.close()
break
else:
message=self._name +'' + \
ctime()+'\n'+message
self._record.add(message)
self._client.send(str(self._record),CODE)
HOST ='localhost'
PORT = 5000
ADDRESS = (HOST,PORT)
BUFSIZE = 1024
CODE = 'ascii'
record = ChatRecord()
server = socket(AF_INET,SOCK_STREAM)
server.bind(ADDRESS)
server.listen(5)
while True:
print ('Waiting for connection...')
client,address = server.accept()
print ('...connected from:',address)
handler = ClientHandler(client,record)
handler.start()
This is the server file
from socket import *
from codecs import decode
HOST ='localhost'
PORT = 5000
BUFSIZE = 1024
ADDRESS = (HOST,PORT)
CODE = 'ascii'
server = socket(AF_INET,SOCK_STREAM)
server.connect(ADDRESS)
print (decode(server.recv(BUFSIZE), CODE))
name = raw_input('Enter your name:')
server.send(name)
while True:
record = server.recv(BUFSIZE)
if not record:
print ('Server disconnected')
break
print (record)
message = raw_input('>')
if not message:
print ('Server disconnected')
break
server.send(message, CODE)
server.close()
This is the Chartrecord
class ChatRecord(object):
def __init__(self):
self.data=[]
def add(self,s):
self.data.append(s)
def __str__(self):
if len(self.data)==0:
return 'No messages yet!'
else:
return'\n'.join(self.data)
This is the error message I get when running the chat record. I can enter a name then after that I get the error message below
Waiting for connection...
('...connected from:', ('127.0.0.1', 51774))
Waiting for connection...
Exception in thread Thread-1:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threadin g.py", line 532, in __bootstrap_inner
self.run()
File "/Users/basetsanamanele/Documents/workspace/HAAAAAAAFF/ClientHandler", line 17, in run
self._client.send(str(self._record),CODE)
TypeError: an integer is required
Please assist
Edit: Also, your server isn't accepting/listing for connections
You should make the server multithreaded so that it can send and receive at the same time. Here's how it might look:
import socket
import os
from threading import Thread
import thread
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host,port))
s.listen(10)
serverThreads = []
while True:
print "Server is listening for connections..."
client, address = s.accept()
serverThreads.append(Thread(target=runserver, args=(client,address)).start())
s.close()
def runserver(client, address):
clients = set()
lockClients = threading.Lock()
print ("Connection from: " + address)
with lockClients:
clients.add(client)
try:
while True:
data = client.recv(1024)
if data:
print data.decode()
with lockClients:
for c in clients:
c.sendall(data)
else:
break
finally:
with lockClients:
clients.remove(client)
client.close()
When you send a string over TCP you need to encode it to bytes. So your client file should look like this instead:
def run(self):
self._client.send(('Welcome to the chatroom!').encode())
self._name = self._client.recv(BUFSIZE).decode()
self._client.send(str(self._record),CODE)
while True:
message = self._client.recv(BUFSIZE).decode()
if not message:
print('Client disconnected')
self._client.close()
break
else:
message=self._name +'' + \
ctime()+'\n'+message
self._record.add(message)
self._client.send(self._record.encode())
I much prefer the .encode() and .decode() syntax as it makes the code a little more readable IMO.
so right now in order to receive your message you need to receive one
my teachers instructions are (in the main)"Modify the loop so that it only listens for keyboard input and then sends it to the server."
I did the rest but don't understand this, ... help?
import socket
import select
import sys
import threading
'''
Purpose: Driver
parameters: none
returns: none
'''
def main():
host = 'localhost'
port = 5000
size = 1024
#open a socket to the client.
try:
clientSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSock.connect((host,port))
#exit on error
except socket.error, (value,message):
if clientSock :
clientSock.close()
print "Could not make connection: " + message
sys.exit(1)
thread1 = ClientThread()
thread1.start()
while True:
#wait for keyboard input
line = raw_input()
#send the input to the server unless its only a newline
if line != "\n":
clientSock.send(line)
#wait to get something from the server and print it
data = clientSock.recv(size)
print data
class ClientThread(threading.Thread):
'''
Purpose: the constructor
parameters: the already created and connected client socket
returns: none
'''
def __init__(self, clientSocket):
super(ClientThread, self).__init__()
self.clientSocket = clientSocket
self.stopped = False
def run(self):
while not self.stopped:
self.data = self.clientSocket.recv(1024)
print self.data
main()
I assume your purpose is to create a program that starts two threads, one (client thread) receives keyboard input and sends to the other (server thread), the server thread prints out everything it received.
Based on my assumption, you first need to start a ServerThread listen to a port (it's not like what your 'ClientThread' did). Here's an example:
import socket
import threading
def main():
host = 'localhost'
port = 5000
size = 1024
thread1 = ServerThread(host, port, size)
thread1.start()
#open a socket for client
try:
clientSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSock.connect((host,port))
except socket.error, (value,message):
if clientSock:
clientSock.close()
print "Could not connect to server: " + message
sys.exit(1)
while True:
#wait for keyboard input
line = raw_input()
#send the input to the server unless its only a newline
if line != "\n":
clientSock.send(line)
# Is server supposed to send back any response?
#data = clientSock.recv(size)
#print data
if line == "Quit":
clientSock.close()
break
class ServerThread(threading.Thread):
def __init__(self, host, port, size):
super(ServerThread, self).__init__()
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.bind((host, port))
self.sock.listen(1)
self.data_size = size
self.stopped = False
def run(self):
conn, addr = self.sock.accept()
print 'Connected by', addr
while not self.stopped:
data = conn.recv(self.data_size)
if data == 'Quit':
print 'Client close the connection'
self.stopped = True
else:
print 'Server received data:', data
# Is server supposed to send back any response?
#conn.sendall('Server received data: ' + data)
conn.close()
if __name__ == '__main__':
main()
And these are the output:
Connected by ('127.0.0.1', 41153)
abc
Server received data: abc
def
Server received data: def
Quit
Client close the connection
You may check here for more details about Python socket: https://docs.python.org/2/library/socket.html?#example
I am building a chat server client system. a socket is created at the client side to connect to the server listening at a port. The problem is when I try to close the socket from the client side, the asyncore handle_close does not detect it.
Server code:
clients = {}
identity={}
class MainServerSocket(asyncore.dispatcher):
def __init__(self, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind(('',port))
self.listen(5)
def handle_accept(self):
newSocket, address = self.accept( )
clients[address] = newSocket
#try :
#hndl=newSocket.recv(1024)
#print "Connected from", address,hndl
#except :
#e = sys.exc_info()[1]
print "Connected from", address
handler=SecondaryServerSocket(newSocket)
handler.setAddr(address)
every = clients.values()
for one in every:
one.send('Server message:'+str(address)+' joined'+'\n')
class SecondaryServerSocket(asyncore.dispatcher_with_send):
def setAddr(self, addr):
self.addr = addr
def isid(self,txt):
pattern=re.compile('\*\$\$\$\*(.+)\*\$\$\$\*')
match=pattern.search(txt)
rv=0
if match :
self.hndl=match.group(1)
rv=1
if self.hndl not in identity.values():
identity[self.addr]=self.hndl
every=clients.values()
for one in every:
one.send('Server message:'+str(self.addr)+' is known as '+self.hndl+'\n')
else:
clients[self.addr].send("Server message: handle in use, retry. Disconnecting.")
self.close()
return rv #del clients[self.addr]
def handle_read(self):
receivedData = self.recv(8192)
if receivedData:
if not self.isid(receivedData):
#self.addr
every = clients.values()
for one in every:
one.send(receivedData)
else: self.close( )
def handle_close(self):
print "Disconnected from", self.getpeername( )
one = self.getpeername( )
del identity[one]
del clients[one]
every=clients.values()
try:
for one in every:
one.send('server message:'+str(self.addr)+' quit'+'\n')
except: pass
print "server started"
MainServerSocket(21567)
asyncore.loop( )
client code(relevant bits):
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.serv_ip=StringVar()
self.port=StringVar()
self.handle=StringVar()
self.serv_ip.set('127.0.0.1')
self.port.set('21567')
self.grid()
self.create_widgets()
self.lastname=""
self.currentname=""
self.delim=('-'*45)+'\n'
def connect(self):
if len(self.handle.get())>0 and not self.handle.get().isspace() :
HOST = self.serv_ip.get()
PORT = int(self.port.get())
#BUFSIZE = 1024
ADDR = (HOST, PORT)
try:
self.tcpCliSock = socket(AF_INET, SOCK_STREAM)
self.tcpCliSock.connect(ADDR)
idtext='*$$$*'+self.handle.get()+'*$$$*'
self.tcpCliSock.send(idtext)
self.add("sys message: CONNECTED TO "+str(HOST)+':'+str(PORT)+'\n')
self.socket()
self.con_button.configure(text="DISCONNECT!",command=self.disconnect)
self.entry_field.configure(state=NORMAL)
self.handle_entry.configure(state=DISABLED)
except:
e = sys.exc_info()[1]
self.add("sys message: ERROR CONNECTING to "+str(HOST)+':'+str(PORT)+" "+str(e)+'\n')
#pass
else : self.add("sys message: Enter handle/nickname and try again."+'\n')
def disconnect(self):
#try : self.tcpCliSock.shutdown(2)
#except: pass
#self.tcpCliSock.close()
self.tcpCliSock.send('')
self.tcpCliSock.close()
self.closeflag=1
self.add("sys message: Disconnected."+'\n')
self.con_button.configure(text="CONNECT!",command=self.connect)
self.entry_field.configure(state=DISABLED)
self.handle_entry.configure(state=NORMAL)
self.closeflag=0
#self.send_button.configure(state=DISABLED)
def socket(self):
def loop0():
while 1:
try:
if self.closeflag==0:
data = self.tcpCliSock.recv(BUFSIZE)
if data: self.add(data)
except:
e = sys.exc_info()[1]
self.add("ERROR: "+str(e)+' disconnecting...'+'\n')
self.con_button.configure(text="CONNECT!",command=self.connect)
self.entry_field.configure(state=DISABLED)
self.handle_entry.configure(state=NORMAL)
self.closeflag=0
thread.exit()
#self.disconnect()
break
if self.closeflag==1 :
self.tcpCliSock.close()
print "ping"
thread.exit()
thread.start_new_thread(loop0, ())
I know this is a lot of code but I am not sure which bit is causing the problems so decided to add all the parts I think could be responsible.when the disconnect function is called, the server doesnt detect a disconnection. And then after the disconnect, the connect function is called, then the client crashes.