I want to test a complex class, which wraps some methods of the socket module: connect, sendall and recv. Especially, I want to test the recv method of this class.
The working example code below shows how I could do that (in a basic, underlying form to keep it simple, testsocket would correspond to the complex wrapper class):
import socket
# This is just a socket for testing purposes, binds to the loopback device
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("127.0.0.1", 1234))
sock.listen(5)
# This is the socket later part of the complex socket wrapper.
# It just contains calls to connect, sendall and recv
testsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
testsocket.connect(("127.0.0.1", 1234))
testsocket.sendall("test_send")
# The testing socket connects to a client
(client, adr) = sock.accept()
print client.recv(1024)
# Now I can do the actual test: Test the receive method of the socket
# wrapped in the complex class
client.sendall("test_recv")
print testsocket.recv(1024) # <-- This is what I want to test !!
# close everything
testsocket.close()
client.close()
sock.close()
But in order to test testsocket.recv I need to use testsocket.sendall before.
Is it possible to modify this code in a simple way (without forks or threads) in order to test testsocket.recv without using the method testsocket.sendall?
How about using socket.socketpair? :
import socket
client, testsocket = socket.socketpair()
client.sendall("test_recv")
print testsocket.recv(1024)
testsocket.close()
client.close()
NOTE only available in Unix.
Using mock
import mock
testsocket = mock.Mock()
testsocket.configure_mock(**{'recv.return_value': 'test_recv'})
print testsocket.recv(1024)
You can't run the client and the server socket in the same process/thread since the server.recv() is a blocking call
my routine :
import socket, threading
# Protocols supported
TCP = (0x00)
UDP = (0x01)
UDP_Multicast = (0x02)
# Client/ Server mode
CLIENT = (0x00)
SERVER = (0x01)
# Data to be sent
data = 'Test. Please Ignore'
# Server processing
def simple_processing(data):
print "messsage : ", data
def start_socket( protocol, client, processing_callback):
# switch on protocol
if protocol == TCP:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else:
return
# switch on client
if client == SERVER:
# Server mode = listening to incoming datas
sock.bind(("127.0.0.1", 1234))
sock.listen(5)
(sock, adr) = sock.accept()
processing_callback( sock.recv(1024) ) # processing data
elif client == CLIENT:
# Client mode : connecting and sending data
sock.connect(("127.0.0.1", 1234))
sock.sendall(data)
else:
return
sock.close()
def test():
# Thread creations
server = threading.Thread( target = start_socket,
args=( TCP,
SERVER,
simple_processing, )
)
client = threading.Thread( target= start_socket,
args=( TCP,
CLIENT,
None)
)
server.start()
client.start()
# Join : wait on every thread to finish
client.join()
server.join()
if __name__ == '__main__':
# Launch the test
test()
Related
I have searched a lot on the forums for implementing TCP server using Python. All that I could find is a multi-threaded approach to TCP Server Implementation on a single port for interacting with clients.
I am looking for Server sample code for creating sockets using different ports.I have clients with distinct port numbers.For example, One socket binding IP and portNo-2000 and second one binding IP and another port No. 3000 and so on. Could anybody provide pointers?
import socket
from typing import Union
SERVERS_PORT_LIST = []
def start_server(port) -> Union[socket.socket, None]:
server_socket = socket.socket()
try:
server_socket.bind(("0.0.0.0", port))
server_socket.listen()
return server_socket
except (socket.error, OSError): # in case the port is taken
return None
def accept_client(server_sock: socket.socket) -> Union[tuple[socket.socket, tuple[str, str]], tuple[None, None]]:
# wait 5 seconds for a client to connect, if no one
# tries to connect within 5 seconds it will return None
server_sock.settimeout(5)
try:
client_socket, client_addr = server_sock.accept()
return client_socket, client_addr
except (socket.error, ConnectionRefusedError):
return None, None
def main():
servers_client_sockets = {}
# create all the servers
for port in SERVERS_PORT_LIST:
server_sock = start_server(port)
if server_sock is not None:
servers_client_sockets[(server_sock, port)] = []
else:
print(f"Port {port} is taken.")
# accept 1 client for each server and append the client to the list inside the dict
# that contains {server_sock: [list of clients], server_sock2: [list of clients], ...}
for server_sock, port in servers_client_sockets.keys():
client_socket, client_addr = accept_client(server_sock)
if client_socket is not None: # someone connected
servers_client_sockets[(server_sock, port)] = servers_client_sockets[server_sock].append(client_socket)
else:
print(f"No client on port {port}.")
# handle the client in each server sock
if __name__ == '__main__':
main()
answer to your question:
I have made a little change to the code.
to receive a client data you need to go through the dict servers_client_sockets keys, and search for the port you want to receive from.
the dict is built like the following: {(server_sock, port): [clients], (server_2_sock, port): [clients], ..., (server_x_sock, port): [clients]}
example:
for server_sock, port in servers_client_sockets.keys():
if port == the_port_you_want:
client_list = servers_client_sockets[(server_sock, port)]
now client_list should contains all the clients that connected to the port you were looking for.
if there is only one client in each port you can do this:
data clients_list[1].recv(a_number_that_says_how_many_bytes_to_recv).decode()
# with out '.decode()' if you want to keep the data as bytes
if there is more than one client you will need to do a loop on the code from above (receive the data for each one of the clients)
NOTE:
if you want to accept more than 1 client you will need to add more code, and insert the client socket to the dict servers_client_sockets (I explained above how the dict is built).
use this code to accept 1 more client for each server port
# loops on all the server sockets
for server_sock, port in servers_client_sockets.keys():
# tries to accept a client for each server socket
client_socket, client_addr = accept_client(server_sock)
# if there is a new client it adds it to the dict servers_client_sockets
if client_socket is not None: # someone connected
servers_client_sockets[(server_sock, port)] = servers_client_sockets[server_sock].append(client_socket)
# if there is no new client, it prints no client on port x
else:
print(f"No client on port {port}.")
Using asyncio you can write elegant multi-servers and clients without having to use more than one thread or having to invert the control flow for efficient single-threaded non-blocking I/O.
asyncio allows one to:
write I/O code in straight-forward sequential/blocking manner, which in the past required using one thread per connection;
execute this code using only one thread of execution in non-blocking fashion, which in the past required inverting the control flow of your sequential blocking I/O code into callback-hell based programming model.
You can find example TCP server and client in Python Standard library documentation, asyncio examples.
Server:
import asyncio
class EchoServerProtocol(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('Connection from {}'.format(peername))
self.transport = transport
def data_received(self, data):
message = data.decode()
print('Data received: {!r}'.format(message))
print('Send: {!r}'.format(message))
self.transport.write(data)
print('Close the client socket')
self.transport.close()
async def main():
# Get a reference to the event loop as we plan to use
# low-level APIs.
loop = asyncio.get_running_loop()
server = await loop.create_server(
lambda: EchoServerProtocol(),
'127.0.0.1', 8888)
async with server:
await server.serve_forever()
asyncio.run(main())
I'm trying to create a persistent socket connection between a Lua client and Python server. Effectively a script that'll constantly ping the server with keepalive messages
My current issue is that the socket closes after each connection without a means to reopen it for transmission.
Lua Client:
local HOST, PORT = "localhost", 9999
local socket = require('socket')
-- Create the client and initial connection
client, err = socket.connect(HOST, PORT)
client:setoption('keepalive', true)
-- Attempt to ping the server once a second
start = os.time()
while true do
now = os.time()
if os.difftime(now, start) >= 1 then
data = client:send("Hello World")
-- Receive data from the server and print out everything
s, status, partial = client:receive()
print(data, s, status, partial)
start = now
end
end
Python Server:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print("{} wrote".format(self.client_address[0]))
print(self.data)
print(self.client_address)
# Send back some arbitrary data
self.request.sendall(b'1')
if __name__ == '__main__':
HOST, PORT = "localhost", 9999
# Create a socketserver and serve is forever
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
server.serve_forever()
The expected result is a keepalive ping every second to ensure the client is still connected to the server.
I ended up finding a solution.
The problem seems to have been with the socketserver library in Python. I switched it to raw sockets and things began working how I wanted them to. From there I created threads to handle the back and forth in the background
Python Server:
import socket, threading
HOST, PORT = "localhost", 9999
# Ensures the connection is still active
def keepalive(conn, addr):
print("Client connected")
with conn:
conn.settimeout(3)
while True:
try:
data = conn.recv(1024)
if not data: break
message = data.split(b',')
if message[0] == b'ping':
conn.sendall(b'pong' + b'\n')
except Exception as e:
break
print("Client disconnected")
# Listens for connections to the server and starts a new keepalive thread
def listenForConnections():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind((HOST, PORT))
while True:
sock.listen()
conn, addr = sock.accept()
t = threading.Thread(target=keepalive, args=(conn, addr))
t.start()
if __name__ == '__main__':
# Starts up the socket server
SERVER = threading.Thread(target=listenForConnections)
SERVER.start()
# Run whatever code after this
The Lua client didn't change in this scenario
There is something i can't get my head arround. I've created a unix datagram socket with:
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
self.socket.bind(SOCKET_FILE)
Later in the code i receive messages written to the socket with
data, addr = self.socket.recvfrom(4096)
But addr appears to be None all the time. But i need it to send back a response.
How can i achieve writing back to the sender with unix datagram sockets?
Thank you for your answers
Suppose we have a server:
# server.py
import os
import socket
SOCKET_FILE = "mysocket-server"
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
s.bind(SOCKET_FILE)
data, addr = s.recvfrom(4096)
s.close()
os.unlink(SOCKET_FILE)
print(data, addr)
If a client connects and sends a message without binding its own name to the socket, like so:
# client.py
import socket
SOCKET_FILE = "mysocket-server"
sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sk.sendto("hello", SOCKET_FILE)
sk.close()
then the message will be sent anonymously with no address bound on the client side (i.e., with addr == None). Note that this is different from IP datagram sockets which are automatically bound to a fresh address (i.e., host address and port number) as soon as you send data.
For such anonymous messages over Unix datagram sockets, the client has no assigned address, and there is no mechanism by which the server can send return data to the sender.
The simplest solution is for the client to bind it's own private name to the socket:
# client2.py
import os
import socket
SERVER_FILE = "mysocket-server"
CLIENT_FILE = "mysocket-client"
sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sk.bind(CLIENT_FILE)
sk.sendto("hello", SERVER_FILE)
data, addr = sk.recvfrom(4096)
print(data,addr)
sk.close()
os.unlink(CLIENT_FILE)
Then, using the following modified server:
# server2.py
import os
import socket
SOCKET_FILE = "mysocket-server"
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
s.bind(SOCKET_FILE)
data, addr = s.recvfrom(4096)
if addr is not None:
s.sendto("world", addr)
print(data, addr)
s.close()
os.unlink(SOCKET_FILE)
you can see that two-way communication is possible.
On Linux, there's an "abstract namespace" extension (see the unix(7) manpage) which means the client can also bind to an empty name with sk.bind(""), like so:
# client3.py
import socket
SERVER_FILE = "mysocket-server"
sk = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sk.bind("")
sk.sendto("hello", SERVER_FILE)
data, addr = sk.recvfrom(4096)
print(data,addr)
sk.close()
This automatically binds the client to a fresh "abstract socket address", which sort of emulates what IP datagram sockets already do.
As an alternative approach, you can use SOCK_SEQPACKET in place of SOCK_DGRAM. This automatically constructs a two-way connection (like SOCK_STREAM) but preserves the message boundaries (like SOCK_DATAGRAM). Here's a server that accepts connections from clients in a loop, receiving and responding to two packets from each client.
# server4.py
import os
import socket
SOCKET_FILE = "mysocket-server"
s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
s.bind(SOCKET_FILE)
s.listen(5)
try:
while True:
(t, _) = s.accept()
print(t.recv(4096))
t.send("sun")
print(t.recv(4096))
t.send("moon")
t.close()
finally:
os.unlink(SOCKET_FILE)
The following client demonstrates that the response packets are kept separate:
# client4.py
import socket
SERVER_FILE = "mysocket-server"
sk = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
sk.connect(SERVER_FILE)
sk.send("hello")
sk.send("goodbye")
print(sk.recv(4096))
print(sk.recv(4096))
sk.close()
Here, server4.py isn't a great server design, since a badly behaved client can block, preventing the server from serving any other clients. A real server might use separate worker threads to keep running in the face of slow clients.
You need to bind (to a different socket) on both sides.
A socket isn't automatically created for you, hence the server program doesn't get a name to respond to.
>>> from socket import *
>>> sk=socket(AF_UNIX,SOCK_DGRAM,0)
>>> sk.bind('/tmp/abc')
>>> sk.recvfrom(1024)
('hello', '/tmp/abc1')
>>> from socket import *
>>> sk=socket(AF_UNIX,SOCK_DGRAM,0)
>>> sk.bind('/tmp/abc1')
>>> sk.sendto('hello','/tmp/abc')
5
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
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()