Trying to have multiple people connected to a Socket in python [duplicate] - python

This question already has answers here:
Python Socket Multiple Clients
(6 answers)
Closed 5 years ago.
Im trying to have multiple clients be able to connect to a server however when a second user connects it kicks the other client off the server as you cant have 2 clients connected to a socket and i was wondering if there was anything around this.
server.py
import socket
def Main():
host = '10.41.13.228'
port = 5000
s = socket.socket()
s.bind((host,port))
s.listen(1)
name = input("Please Enter your name - ")
while True:
c, addr = s.accept()
print("Connection from: " + str(addr))
data = c.recv(1024).decode('utf-8')
print(data)
c.close()
if __name__ == '__main__':
Main()
Client.py
import socket
def Main():
host = '10.41.13.228'
port = 5000
s = socket.socket()
s.connect((host, port))
name = input("Please enter your name - ")
message = input("-> ")
while True:
while message != 'q':
ToSend = (str(name) + " - " + str(message))
s.sendall(ToSend.encode('utf-8'))
message = input("-> ")
s.close()
if __name__ == '__main__':
Main()

The problem i noticed in your code is s.listen method. Where you are listening to only one client connection. You could increase the amount to have more clients connected to the server.

As described in the example from the docs:
Note that a server must perform the sequence socket(), bind(), listen(), accept() (possibly repeating the accept() to service more than one client), while a client only needs the sequence socket(), connect(). Also note that the server does not sendall()/recv() on the socket it is listening on but on the new socket returned by accept().
Therefore, there are two missing parts in your code.
As already commented, your listen() method should take a 2 as argument (as you intend to have 2 clients).
You should call accept() twice. Remember to keep in mind the comment of the example. Quoting again:
note that the server does not sendall()/recv() on the socket it is
listening on but on the new socket returned by accept()
Out of topic. It is indeed a good practice to have a keyword such as 'exit' to break from the loop of your server once recieved.

Related

Client Timeout Error Using Python Sockets ONLY on another computer [duplicate]

This question already has answers here:
Server and client on two different networks with python?
(1 answer)
Java socket client to server on different network.
(2 answers)
Connecting 2 devices on different networks using Python Sockets
(1 answer)
Closed 4 months ago.
I need someone to explain what is happening here. I have two code files; a server.py and a client.py. I basically created an area where I can send messages from clients to other connected clients to that server. It works when I run different instances on my own computer, but when I sent it to my partner to test it out with me (he is about an hour drive away from me; different WiFi networks), when he ran it while I had the server running, he received a timeout error.
What is going on here, where I can connect to it from my own device and someone else can't?
Here is my code, followed by the error screenshot my partner received:
server.py
import socket
import threading
serverSocket = socket.socket()
print("Server created")
ip = socket.gethostname()
port = 5737
serverSocket.bind((ip, port))
print(f"Server bound with ip {ip} port {port}")
serverSocket.listen()
clients = []
def broadcast(data, clientc): #new...
for client in clients:
if client == clientc:
pass
else:
print("Sending ", data.decode(), "to ", client)
client.send(data)
def threaded_client(clientConnection):
clientConnection.send(str.encode(f"Hello, {clientAddress}")) #on connection, thread start
while True: #while thread running
try:
data = clientConnection.recv(2048) #receiving info
if not data: #received empty info
print("Disconnected")
break
else: #received actual data
print("Received ", data.decode(), "from ", clientAddress)
broadcast(data, clientConnection) #new
except:
break
print("Lost connection from ", clientAddress)
clientConnection.close() #thread end, on disconnection
while(True): #accepts client connection
(clientConnection, clientAddress) = serverSocket.accept()
print("Connected to ", clientAddress)
clients.append(clientConnection) #new
thread = threading.Thread(target=threaded_client, args=(clientConnection,)) #new
thread.start() #new
client.py
import socket
import threading
socketObject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socketObject.connect((socket.gethostname(), 5737))
print("Connected to server")
def client_receive():
while(True):
try:
data = socketObject.recv(2048)
print(data.decode())
except:
print("Error... Disconnecting...")
socketObject.close()
break
def client_send():
while(True):
data = input("")
socketObject.send(data.encode())
receive_thread = threading.Thread(target=client_receive)
receive_thread.start()
send_thread = threading.Thread(target=client_send)
send_thread.start()
Screenshot of error: screenshot

error 10054 in a multithreaded server program in python

I am trying to design a multithreaded server. client1 send data to server and server forwards data to client2 for processing. client2 sends processed data to server and then server forwards it to client1.
Data from client1 is received by client2 through server while execution, but my server program after sending data to client2 terminates with error
[10054] An existing connection was forcibly closed by remote host.
client1.py
from socket import *
import thread
ip='127.1.1.2'
port=5554
s=socket(AF_INET, SOCK_STREAM)
s.connect((ip,port))
data=raw_input("Input lowercase :: ")
while data!="#close":
s.send(data)
data=raw_input("Input next word or type #close to exit: ")
s.close()
client2.py
from socket import *
import thread
ip='127.1.1.2'
port=5554
s=socket(AF_INET, SOCK_STREAM)
s.connect((ip,port))
m=s.recv(1024)
k=m.upper()
print "Uppercase of ",m," in System--> ",k
s.close()
server.py
from socket import *
import thread
ip='127.1.1.2'
port=5554
data=''
def multi_threading(c,a):
while True:
global data
print "Inside client 1 thread"
data= c.recv(1024)
if not data:
break
if "close"==data.rstrip():
break
c.close()
print a,"--terminates connection"
def multi_threading2(c,a):
while True:
print "Inside client 2 thread"
c.send(data)
if not data:
break
if "close"==data.rstrip():
break
c.close()
print a,"--terminates connection"
if __name__=='__main__':
s=socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)
s.bind((ip,port))
s.listen(4)
count=0
while True:
print "Server is listening on port number ",port
c,a=s.accept()
count=count+1
print "--------------------------------------------------"
print "Server connected to system having ip address --> ",a
if(count==1):
thread.start_new_thread(multi_threading,(c,a))
else:
thread.start_new_thread(multi_threading2,(c,a))
What i might be doing wrong?
I'm trying to help by changing your code as little as possible. It isn't how I would write it but should allow you to follow along. This is also not very idomatic Python. I just want you to see how to get the basic interaction going.
Some comments about my changes:
Remove the indent in client1.py so you don't always close the
socket with s.close(). I suspect this was a typo in your post?
In server.py it isn't safe to access the global data between two
threads. At the very least you need a semaphore to protect it.
Every time around multi_threading loop you destroy the last value of
data. You need to append the incoming bytes to a data structure
like a list. I renamed the threads to make it easier to understand.
You are expecting to see close in the server but you don't ever send this because your loop terminates when you type #close. You need to adjust for this.
Your code is order dependent because both client 'sender' and client 'receiver' use the same port. You need to put something into the protocol to distinguish between these two roles so you can start them in either order. This makes the system more robust.
Due to the sender not necessarily having any data I do a crude sleep. This can be avoided by using a shared queue between the threads which would block one end
You need some framing because you can't guarantee the speed at which the independent processes send and receive to the socket. I use \n for this.
There is an edge case if you sent very large strings (and removed the 1024 read) you could not get a full word with the \n through. I ignore this case.
I adjust the loopback address
I wrote this in PyCharm CE. I recommend using an IDE (PyCharm is excellent) with breakpoints. You can step through all the processes bit by bit. Put breakpoints in the threads and step through the actions.
Result:
server.py
from socket import *
import thread
from threading import Lock
import time
ip = '127.0.0.1'
port = 5554
data = []
data_lock = Lock()
def multi_threading_recv(sock, a):
should_run = True
while should_run:
global data
print "Inside client 1 thread"
_data = sock.recv(1024)
if _data:
with data_lock:
for word in _data.split('\n'):
data.append(word)
if "#close" == word:
should_run = False
break
else:
break
sock.close()
print a, "--terminates connection"
def multi_threading_send(sock, a):
while True:
print "Inside client 2 thread"
with data_lock:
if len(data) == 0:
time.sleep(1) # Not ideal. Ideally block on a shared queue
continue
else:
_data = data.pop(0)
if _data == "#close":
break
sock.send(_data + '\n')
c.close()
print a, "--terminates connection"
if __name__ == '__main__':
s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)
s.bind((ip,port))
s.listen(4)
count = 0
while True:
print "Server is listening on port number ", port
c, a = s.accept()
print "--------------------------------------------------"
print "Server connected to system having ip address --> ", a
role = c.recv(4)
if role == 'PUSH':
thread.start_new_thread(multi_threading_recv, (c, a))
elif role == 'PULL':
thread.start_new_thread(multi_threading_send, (c,a))
else:
print('invalid role: ' + role)
c.close()
client1.py
from socket import *
ip = '127.0.0.1'
port = 5554
s = socket(AF_INET, SOCK_STREAM)
s.connect((ip,port))
s.send('PUSH')
while True:
data = raw_input("Input word or type #close to exit: ")
s.send(data + '\n')
if data == "#close":
break
s.close()
client2.py
from socket import *
ip = '127.0.0.1'
port = 5554
s = socket(AF_INET, SOCK_STREAM)
s.connect((ip, port))
s.send('PULL')
while True:
data = s.recv(1024)
if data:
words = data.split('\n')
for w in words:
print(w)
else:
break
s.close()
Output:
client1
Input word or type #close to exit: hello
Input word or type #close to exit: world
Input word or type #close to exit: #close
client2:
hello
world
server:
Server is listening on port number 5554
--------------------------------------------------
Server connected to system having ip address --> ('127.0.0.1', 62605)
Server is listening on port number 5554
Inside client 1 thread
('127.0.0.1', 62605) --terminates connection
--------------------------------------------------
Server connected to system having ip address --> ('127.0.0.1', 62614)
Server is listening on port number 5554
Inside client 2 thread
Inside client 2 thread
Inside client 2 thread
('127.0.0.1', 62614) --terminates connection
There is one final comment that the server will go around the loop again and keep on accepting clients. The code doesn't cope with this and I leave it as an exercise for you. You can multiplex multiple senders and receivers if you add a channel number to the PUSH and PULL initiation of the protocol (e.g. PUSH3, PULL3). This will then allow you to store the incoming data in a dictionary keyed by this integer and then send it out to the correct client.

How could I improve this "step based" python chat server and client so it supports more than two instances?

Here is the server:
import socket
HOST = '127.0.0.1'
PORT = 4444
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
conn, addr = s.accept()
print addr, 'connected.'
while True:
data = conn.recv(1024)
print '> ', data
reply = raw_input(">> ")
conn.sendall(reply)
if reply == 'bye':
break
conn.close()
And here is the client:
import socket
HOST = '127.0.0.1'
PORT = 4444
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
message = raw_input("> ")
s.send(message)
reply = s.recv(1024)
print ">> ", repr(reply)
if message == 'bye':
break
s.close()
What is the most efficient way to upgrade this code to support multiple instances, and more importantly: how could I assign names (nicknames) to all clients and make it so clients could type whenever they want? I tried to use Threading, but that was a huge disaster .
Multiple client
For multiple client connection, you need to have some non-linear execution.
You can either use coroutines or threads to achieve this.
Python 3 comes with asyncio, library that uses coroutines (and lot of other features) to emulate multithreading. I suggest you look into this.
You can also use threads, it's not that complicated. Whenever a client connects to your server, you just have to create a new thread to execute your function. Python official documentation should help you achieve that.
Nickname
Whenever a client connects to your server, you could instantiate a User object in the newly created thread with your client information, so that each thread has its own User instance.

s.sendall doesn't work inside a thread in python

I'm trying to develop a chat program in python. I want it to have multiple clients so I'm using threading to handle this. However when I try to send the message to all connected clients, the server only sends it to the client which sent the message. I'm not sure if I'm just missing something obvious but here is the code for the server:
import socket
from thread import *
host = '192.168.0.13'
port = 1024
users = int(input("enter number of users: "))
def clienthandler(conn):
while True:
data = conn.recv(1024)
if not data:
break
print data
conn.sendall(data)
conn.close()
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.bind((host, port))
serversock.listen(users)
for i in range(users):
conn, addr= serversock.accept()
print 'Connected by', addr
start_new_thread(clienthandler, (conn,))
And here is the code for the client:
import socket
host = '192.168.0.13'
port = 1024
usrname = raw_input("enter a username: ")
usrname = usrname + ": "
clientsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsock.connect((host, port))
while True:
x = raw_input('You: ')
x = usrname + x
clientsock.sendall(x)
data = clientsock.recv(1024)
print data
The "all" in sendall means that it sends all of the data you asked it to send. It doesn't mean it sends it on more than one connection. Such an interface would be totally impractical. For example, what would happen if another thread was in the middle of sending something else on one of the connections? What would happen if one of the connections had a full queue?
sendall: Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from string until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent. -- 17.2. socket
You can try by pulling up the list of users, and iterating through it, and doing an individual send of the same message, though, unless you are the administrator and want to broadcast a warning, this functionality would be pretty mundane.

Sending string via socket (python)

I have two scripts, Server.py and Client.py.
I have two objectives in mind:
To be able to send data again and again to server from client.
To be able to send data from Server to client.
here is my Server.py :
import socket
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "192.168.1.3"
port = 8000
print (host)
print (port)
serversocket.bind((host, port))
serversocket.listen(5)
print ('server started and listening')
while 1:
(clientsocket, address) = serversocket.accept()
print ("connection found!")
data = clientsocket.recv(1024).decode()
print (data)
r='REceieve'
clientsocket.send(r.encode())
and here is my client :
#! /usr/bin/python3
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host ="192.168.1.3"
port =8000
s.connect((host,port))
def ts(str):
s.send('e'.encode())
data = ''
data = s.recv(1024).decode()
print (data)
while 2:
r = input('enter')
ts(s)
s.close ()
The function works for the first time ('e' goes to the server and I get return message back), but how do I make it happen over and over again (something like a chat application) ?
The problem starts after the first time. The messages don't go after the first time.
what am I doing wrong?
I am new with python, so please be a little elaborate, and if you can, please give the source code of the whole thing.
import socket
from threading import *
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "192.168.1.3"
port = 8000
print (host)
print (port)
serversocket.bind((host, port))
class client(Thread):
def __init__(self, socket, address):
Thread.__init__(self)
self.sock = socket
self.addr = address
self.start()
def run(self):
while 1:
print('Client sent:', self.sock.recv(1024).decode())
self.sock.send(b'Oi you sent something to me')
serversocket.listen(5)
print ('server started and listening')
while 1:
clientsocket, address = serversocket.accept()
client(clientsocket, address)
This is a very VERY simple design for how you could solve it.
First of all, you need to either accept the client (server side) before going into your while 1 loop because in every loop you accept a new client, or you do as i describe, you toss the client into a separate thread which you handle on his own from now on.
client.py
import socket
s = socket.socket()
s.connect(('127.0.0.1',12345))
while True:
str = raw_input("S: ")
s.send(str.encode());
if(str == "Bye" or str == "bye"):
break
print "N:",s.recv(1024).decode()
s.close()
server.py
import socket
s = socket.socket()
port = 12345
s.bind(('', port))
s.listen(5)
c, addr = s.accept()
print "Socket Up and running with a connection from",addr
while True:
rcvdData = c.recv(1024).decode()
print "S:",rcvdData
sendData = raw_input("N: ")
c.send(sendData.encode())
if(sendData == "Bye" or sendData == "bye"):
break
c.close()
This should be the code for a small prototype for the chatting app you wanted.
Run both of them in separate terminals but then just check for the ports.
This piece of code is incorrect.
while 1:
(clientsocket, address) = serversocket.accept()
print ("connection found!")
data = clientsocket.recv(1024).decode()
print (data)
r='REceieve'
clientsocket.send(r.encode())
The call on accept() on the serversocket blocks until there's a client connection. When you first connect to the server from the client, it accepts the connection and receives data. However, when it enters the loop again, it is waiting for another connection and thus blocks as there are no other clients that are trying to connect.
That's the reason the recv works correct only the first time. What you should do is find out how you can handle the communication with a client that has been accepted - maybe by creating a new Thread to handle communication with that client and continue accepting new clients in the loop, handling them in the same way.
Tip: If you want to work on creating your own chat application, you should look at a networking engine like Twisted. It will help you understand the whole concept better too.

Categories