How can I send to a specific client? TCP Server can send to all clients
I store the sendip and sendport, obtained using this code:
clientssendback = []
clientsocket, address = s.accept()
clientssendback.append(address)
for x in clientssendback:
sendip = str(x[0])
sendport = str(x[1])
clientsocket.sendall(bytes("hello", "utf-8"), (sendip, sendport))
I'll have so I can send it to all client there are loged into the server.
Can some one help me with that? Am I doing the right thing?
i have a few notes:
the accept() function returns a tuple. the first is the connection made between the server and client. it is not the client's socket.
this means that you establish a unique connection between every client and the server when you accept him. the transfering of data happens through this coonection.
first you need to save these coonections in a list, and loop between them, and send the message to each client
import socket
connections = []
server_socket = socket.socket() # by default it's TCP/IP
server_socket.bind((ip_address, port)
server_socket.listen(20)
conn, addr = server_socket.accept()
connections.append(conn)
now you now how to establish a connection and save it in a list. now lets send a message which is "hello" to all clients
for connection in connections:
connection.send(b"hello") # the b changes the string to bytes
Related
So far I managed to get the server to communicate with multiple clients but I need to pass a value from one client to another.
Server code
import socket
from _thread import *
ServerSideSocket = socket.socket()
host = ''
port = 2004
ThreadCount = 0
try:
ServerSideSocket.bind((host, port))
except socket.error as e:
print(str(e))
print('Socket is listening..')
ServerSideSocket.listen(5)
def multi_threaded_client(connection):
connection.send(str.encode('Server is working:'))
while True:
data = connection.recv(2048)
response = data.decode('utf-8')
if not data:
break
connection.sendall(str.encode(response))
connection.close()
while True:
Client, address = ServerSideSocket.accept()
print('Connected to: ' + address[0] + ':' + str(address[1]))
start_new_thread(multi_threaded_client, (Client,))
ThreadCount += 1
print('Thread Number: ' + str(ThreadCount))
ServerSideSocket.close()
Client code
import socket
ClientMultiSocket = socket.socket()
host = '127.0.0.1'
port = 2004
print('Waiting for connection response')
try:
ClientMultiSocket.connect((host, port))
except socket.error as e:
print(str(e))
res = ClientMultiSocket.recv(1024)
while True:
Input = input('Hey there: ')
ClientMultiSocket.send(str.encode(Input))
res = ClientMultiSocket.recv(1024)
print(res.decode('utf-8'))
ClientMultiSocket.close()
I need the value from client1 to be passed to client2 and so forth, is there any way I can do that in python?
A TCP connection can only established to a socket which is a) bound to the specific address one is trying to connect to and b) is listening on this address for new connections which it then will c) accept to create a new socket which will then be used to communicate on this connection. Any communication outside such established connection is not possible in TCP.
The combination of a, b and c basically describes what a TCP server does. This also means that it is not possible to communicate between two TCP clients only which only called connect to each other. In order for a connect to succeed there must be something bound to the socket, listening to it and accepting connections on it (i.e. a, b and c - a TCP server).
A common way to implement the use cases of passing messages between different client (like in a chat server) is to send the message and intended client id to the server and let the server forward it to the intended client.
In a TCP connection, always, one is the server and the other are the client.
A simple use case:
client1 need to send a msg to client2, the pipeline could be:
client1 send a msg in JSON format to server "{'msg': 'abc',
'receiver':'client2'}"
server will store this info in a dict (or another structure you want)
client2 will ask for the server if there is any message to it
the server send the message stored to client2
It's more or less in this way that all the communications apps works.
I searched documentations and tutorials but no one talked about this, for example this is server script
import socket
server = socket.socket()
print("socket created")
server.bind(("localhost", 9999))
server.listen(3)
print("waiting for connection")
while True:
client, addr = server.accept()
print(client)
print(addr)
name = client.recv(1024).decode()
print("connected with", addr, client, name)
client.send(b"welcome bro")
client.close()
When printing client, I get this:
proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 36182)
And addr variable :
('127.0.0.1', 36182)
Why these two variable defined by one and got two different output?
What is the logic behind scene?
From the documentation of the socked module:
socket.accept()
Accept a connection. The socket must be bound to an
address and listening for connections. The return value is a pair
(conn, address) where conn is a new socket object usable to send and
receive data on the connection, and address is the address bound to
the socket on the other end of the connection.
The script does not answer this by itself, however, I assume laddr=('127.0.0.1', 9999) is the listening address of the server-side app. That's where connections are established. the raddr is the connection port the request comes from. When you listen to a port with a server, the client uses any non-reserved port >1024 to connect to the server and this is totally random, as long as it is defined in the client-app.
So you have to different connection points for one established connection. The one port and address as the sender-side (described as raddr) and the one as the receiver side (here described as laddr - for listen)
That's basically the logic behind any TCP-related connection.
accept() function returns a socket descriptor that is connected to your TCP server. In this case, it returns a tuple of objects.
The first parameter, conn, is a socket object that you can use to send data to and receive data from the client that is connected.
The second parameter, addr, contains address information about the client that is connected(e.g., IP address and remote part).
I'm writing a toy meeting-point/relay server listening on port 5555 for two clients "A" and "B".
It works like this: every byte received by the server from the firstly-connected client A will be sent to the secondly-connected client B, even if A and B don't know their respective IP:
A -----------> server <----------- B # they both connect the server first
A --"hello"--> server # A sends a message to server
server --"hello"--> B # the server sends the message to B
This code is currently working:
# server.py
import socket, time
from threading import Thread
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.bind(('', 5555))
socket.listen(5)
buf = ''
i = 0
def handler(client, i):
global buf
print 'Hello!', client, i
if i == 0: # client A, who sends data to server
while True:
req = client.recv(1000)
buf = str(req).strip() # removes end of line
print 'Received from Client A: %s' % buf
elif i == 1: # client B, who receives data sent to server by client A
while True:
if buf != '':
client.send(buf)
buf = ''
time.sleep(0.1)
while True: # very simple concurrency: accept new clients and create a Thread for each one
client, address = socket.accept()
print "{} connected".format(address)
Thread(target=handler, args=(client, i)).start()
i += 1
and you can test it by launching it on a server, and do two netcat connections to it: nc <SERVER_IP> 5555.
How can I then pass the information to the clients A and B that they can talk directly to each other without making the bytes transit via the server?
There are 2 cases:
General case, i.e. even if A and B are not in the same local network
Particular case where these two clients are in the same local network (example: using the same home router), this will be displayed on the server when the 2 clients will connect to the server on port 5555:
('203.0.113.0', 50340) connected # client A, router translated port to 50340
('203.0.113.0', 52750) connected # same public IP, client B, router translated port to 52750
Remark: a previous unsuccesful attempt here: UDP or TCP hole punching to connect two peers (each one behind a router)
and UDP hole punching with a third party
Since the server knows the addresses of both clients, it can send that information to them and so they would know each others adress. There are many ways the server can send this data - pickled, json-encoded, raw bytes. I think the best option is to convert the address to bytes, because the client will know exactly how many bytes to read: 4 for the IP (integer) and 2 for the port (unsigned short). We can convert an address to bytes and back with the functions below.
import socket
import struct
def addr_to_bytes(addr):
return socket.inet_aton(addr[0]) + struct.pack('H', addr[1])
def bytes_to_addr(addr):
return (socket.inet_ntoa(addr[:4]), struct.unpack('H', addr[4:])[0])
When the clients receive and decode the address, they no longer need the server, and they can establish a new connection between them.
Now we have two main otions, as far as I know.
One client acts as a server. This client would close the connection to the server and would start listening on the same port. The problem with this method is that it will only work if both clients are on the same local network, or if that port is open for incoming connections.
Hole punching. Both clients start sending and accepting data from each other simultaneously. The clients must accept data on the same address they used to connect to the rendezvous server, which is knwn to each other. That would punch a hole in the client's nat and the clients would be able to communicate directly even if they are on different networks. This proccess is expleined in detail in this article Peer-to-Peer Communication Across Network Address Translators, section 3.4 Peers Behind Different NATs.
A Python example for UDP Hole Punching:
Server:
import socket
def udp_server(addr):
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
soc.bind(addr)
_, client_a = soc.recvfrom(0)
_, client_b = soc.recvfrom(0)
soc.sendto(addr_to_bytes(client_b), client_a)
soc.sendto(addr_to_bytes(client_a), client_b)
addr = ('0.0.0.0', 4000)
udp_server(addr)
Client:
import socket
from threading import Thread
def udp_client(server):
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
soc.sendto(b'', server)
data, _ = soc.recvfrom(6)
peer = bytes_to_addr(data)
print('peer:', *peer)
Thread(target=soc.sendto, args=(b'hello', peer)).start()
data, addr = soc.recvfrom(1024)
print('{}:{} says {}'.format(*addr, data))
server_addr = ('server_ip', 4000) # the server's public address
udp_client(server_addr)
This code requires for the rendezvous server to have a port open (4000 in this case), and be accessible by both clients. The clients can be on the same or on different local networks. The code was tested on Windows and it works well, either with a local or a public IP.
I have experimented with TCP hole punching but I had limited success (sometimes it seems that it works, sometimes it doesn't). I can include the code if someone wants to experiment. The concept is more or less the same, both clients start sending and receiving simultaneously, and it is described in detail in Peer-to-Peer Communication Across Network Address Translators, section 4, TCP Hole Punching.
If both clients are on the same network, it will be much easier to communicate with each other. They would have to choose somehow which one will be a server, then they can create a normal server-client connection. The only problem here is that the clients must detect if they are on the same network. Again, the server can help with this problem, as it knows the public address of both clients. For example:
def tcp_server(addr):
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.bind(addr)
soc.listen()
client_a, addr_a = soc.accept()
client_b, addr_b = soc.accept()
client_a.send(addr_to_bytes(addr_b) + addr_to_bytes(addr_a))
client_b.send(addr_to_bytes(addr_a) + addr_to_bytes(addr_b))
def tcp_client(server):
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect(server)
data = soc.recv(12)
peer_addr = bytes_to_addr(data[:6])
my_addr = bytes_to_addr(data[6:])
if my_addr[0] == peer_addr[0]:
local_addr = (soc.getsockname()[0], peer_addr[1])
... connect to local address ...
Here the server sends two addresses to each client, the peer's public address and the client's own public address. The clients compare the two IPs, if they match then they must be on the same local network.
The accepted answer gives the solution. Here is some additional information in the case "Client A and Client B are in the same local network".
This situation can indeed be detected by the server if it notices that both clients have the same public IP.
Then the server can choose Client A as "local server", and Client B as "local client".
The server will then ask Client A for its "local network IP". Client A can find it with:
import socket
localip = socket.gethostbyname(socket.gethostname()) # example: 192.168.1.21
and then send it back to the server. The server will communicate this "local network IP" to Client B.
Then Client A will then run a "local server":
import socket
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
soc.bind(('0.0.0.0', 4000))
data, client = soc.recvfrom(1024)
print("Connected client:", client)
print("Received message:", data)
soc.sendto(b"I am the server", client)
and Client B will run as a "local client":
import socket
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server = ('192.168.1.21', 4000) # this "local network IP" has been sent Client A => server => Client B
soc.sendto("I am the client", server)
data, client = soc.recvfrom(1024)
print("Received message:", data)
def send_Button():
try:
myMsg = "ME: " + text.get()
msg = text.get()
conn.send(msg) ###
textBox.insert(END, myMsg + "\n")
textEntry.delete(0, END)
textBox.yview_pickplace("end")
except NameError:
myMsg = "ME: " + text.get()
msg = text.get()
conn.send(msg) ###
textBox.insert(END, myMsg + "\n")
textEntry.delete(0, END)
textBox.yview_pickplace("end")
This program uses the tkinter module with socket in python2.7. My program allows for you to either connect to a server to chat with or host a server for others to connect to you, but whenever I try and test it out then the lines with the '###' on always bring up an error and it doesn't work, the error which comes up is: "NameError: global name 'conn' is not defined" OR "error: [Errno 10057] A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied".
Any help please?
I think that you are trying to get the program to act as a Client or as a Server just changing s.send() to conn.send() saddly it isn't that simple.
Socket Initializzation
The socket have to be initialized before sending or receiving data.
For a client usually it's something like this.
send_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # Create the socket
send_socket.connect((serverIp, serverPort)) # Connect to the server
send_socket.send(data) # Send the data to the server
And like this for a Server:
listen_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # Create the socket
listen_socket.bind(("0.0.0.0", port)) # Set the socket to accept client from every interface on the port port
listen_socket.listen(1) # Put the server on listen on the port setted before
accept_socket, addr = self.listen_socket.accept() # when a client connect return the socket to talk with it
data = self.accept_socket.recv(buffer_size) # Receive data form the client of max size buffer_size
Docs examples
From your question I guess that with s.send() and conn.send() you are talking about
this example from the python 2.7 socket docs
Here are four minimal example programs using the TCP/IP protocol: a server that echoes all data that it receives back (servicing only one client), and a client using it. 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().
Client
Echo client program
import socket
HOST = 'daring.cwi.nl' # The remote host
PORT = 50007 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)
the client is pretty stright forward, it create the socket s and then after using s.connect() it just send data through it.
Server
The server one is where there there are both s and conn
Echo server program
import socket
HOST = '' # Symbolic name meaning all available interfaces
PORT = 50007 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
conn.close()
in this one first of all we create a socket s on which the server will listen and then using s.accept() it will wait till a client connect to the server and then return the conn which is the socket of the connected client.
So to receive or send data to the client you have to use conn.
Notes
As said in the documentation in these two example the server accept only one client. So if you want to deal with multiple clients you have to repeat the accept step and possibly generate a new Thread for each client so that other clients don't have to wait for each others.
I'm writing a simple client server app in python, where the client is listening every type of data entering in the specific port, and I want to when receiving a data flow, send back to the connected client (which have a dinamic ip) a string, in this case "001". But when I try to send the message, it fails!
#!/usr/bin/env python
import socket
TCP_IP = '192.168.1.115'
TCP_PORT = 55001
BUFFER_SIZE = 1024
MESSAGE = '01'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print ('Connection address:', addr)
while 1:
data = conn.recv(BUFFER_SIZE)
if not data: break
print ('received data:', data)
conn.send(data) # echo
print ('Sending data to client...')
addr change every connection .. i cannot manage this!
s.connect((addr, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()
(Connected stream) sockets are bidirectional, so there's no need to call connect to get a connection to the client—you already have one.
But you want to know why your code fails. And there are at least three problems with it.
First, after you call listen or connect on a socket, you can't call connect again; you will get an exception (EISCONN on POSIX, something equivalent on Windows). You will have to create a new socket.
Second, is client actually binded and listening for a connection on the same port as the server? If not, your connect can't work. If so, the bind will fail if the client and server are on the same machine.
Third, the addr you get back from accept is a (host, port) pair, not just a host. So, as written, you're trying to connect((('192.168.1.115', 12345), 55001)), which obviously isn't going to work.
You are trying to reply to the client using the server listening socket (s). This is only possible in UDP Servers. Since this is a TCP Server you have to use the conn which is crated using s.accept() to communication with remote client.