When creating a TCPServer from the socketserver module, a connection can be made and used continually for communication until one side execute a shutdown command. Having written the following programs, it is unclear to me how to continue communicating with the server if (for various reasons) a UDPServer is required for use. Should the client keep running the sendto and recv methods as needed and pretend that the server will receive the message and return a reply?
Client
import socket
import sys
def main():
host, port = 'localhost', 10000
data = ' '.join(sys.argv[1:])
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.sendto(data.encode(), (host, port))
received = client.recv(1 << 12).decode()
print('Sent: ', data)
print('Received:', received)
if __name__ == '__main__':
main()
Server
import socketserver
def main():
host, port = 'localhost', 10000
server = socketserver.UDPServer((host, port), UDPHandler)
server.serve_forever()
class UDPHandler(socketserver.DatagramRequestHandler):
def handle(self):
data = self.rfile.read().strip()
client, port = self.client_address
print(client, 'wrote:', data)
self.wfile.write(data.upper())
if __name__ == '__main__':
main()
Since no guaranteed connection exists when using datagrams, the client may continue sending messages to the server and attempt to receive replies. There is nothing special to do in terms of connecting, shutting down, or closing the socket. The server should use an appropriately written handler as before to receive and send client communications. The following examples demonstrate how to write such programs and include a command-line interface for specifying their parameters.
UDPClient.py
#! /usr/bin/env python3
import argparse
import socket
def main():
parser = argparse.ArgumentParser(description='Execute a UDP client demo.')
parser.add_argument('host', type=str, help='computer where data is sent')
parser.add_argument('port', type=int, help='location where server listens')
arguments = parser.parse_args()
address = arguments.host, arguments.port
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for _ in range(5):
message = input('>>> ').encode('ascii')
client.sendto(message, address)
response = client.recv(1 << 12).decode('ascii')
print(response)
if __name__ == '__main__':
main()
UDPServer.py
#! /usr/bin/env python3
import argparse
import socketserver
def main():
parser = argparse.ArgumentParser(description='Execute a UDP server demo.')
parser.add_argument('port', type=int, help='where the server will listen')
arguments = parser.parse_args()
host = socketserver.socket.gethostbyname(socketserver.socket.gethostname())
address = host, arguments.port
server = socketserver.ThreadingUDPServer(address, ClientRequestHandler)
server.serve_forever()
class ClientRequestHandler(socketserver.DatagramRequestHandler):
def handle(self):
message = self.rfile.read().decode('ascii')
print('Received:', message)
response = 'Received: {}'.format(message).encode('ascii')
self.wfile.write(response)
if __name__ == '__main__':
main()
Related
So I have a program using sockets, it only accept connections, the server.py file listen to a client.py file, but what if I want it to do it that both files can listen and connect.
For example:
here is my server.py
def main():
print("[STARTING] Server is starting...")
""" Starting a TCP socket """
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
server.listen()
print("[LISTENING] Server is listening...")
while True:
""" Accept the connection from the client """
conn, addr = server.accept()
addr = socket.gethostname()
print(f"[NEW CONNECTION] {addr} connected.")
and this is my client.py
def main():
""" Staring a TCP socket. """
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
""" Connecting to the server. """
client.connect(ADDR)
How can I do both of them in only one file like "Server-Client.py".
So if I want to use in one computer Server-Client.py as a client it can be use a a client,
and if I want to use Server-client.py as a server on another computer it can be use as a server and the other way around.
any ideas?
Put the server code in one function and the client code in another function. Then call the appropriate function based on whether the user asked for "client" or "server".
import sys
def client():
# client code here
def server():
# server code here
if __name__ == '__main__':
if sys.argv[1] == 'client':
client()
elif sys.argv[1] == 'server':
server()
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
These are my basic Python programs for testing TCP sockets.
server.py:
import socketserver as serv
class Handler(serv.BaseRequestHandler):
def handle(self):
received = self.request.recv(16)
print('received',received)
if __name__ == '__main__':
host, port = 'localhost', 2000
with serv.TCPServer((host,port), Handler) as serv:
serv.allow_reuse_address = True
serv.serve_forever()
client.py:
import socket as Socket
import time
if __name__ == "__main__":
host, port = 'localhost', 2000
with Socket.socket(Socket.AF_INET, Socket.SOCK_STREAM) as socket:
socket.connect((host, port))
socket.send(bytes('1','utf-8')) # First send
time.sleep(0.5)
socket.send(bytes('2','utf-8')) # Second send
The server is run first, followed by client. The server prints:
received b'1'
But I would like it to say this to show that the server received both sends:
received b'1'
received b'2'
Am I missing something that is required between each sends?
I need to use socketserver to build a tcp server. According to their document, I need to inherit the class TCPServer, and pass it a subclass of class BaseRequestHandler where I rewrite the method handle().
Right now I need to build two server on different port, is there a way that in handle() function, (otherwise I have to setup two almost identical handler class, which is not I want), I can get my own port number?
Don't do it in the handle() method, pass the port number in (from this https://docs.python.org/2/library/socketserver.html#socketserver-tcpserver-example):
#!/usr/bin/env python
import SocketServer, argparse
class MyTCPHandler(SocketServer.BaseRequestHandler):
"""
The RequestHandler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
# self.server.server_address is a tuple (IP, port) the server is listening on
(host, port) = self.server.server_address
print 'port # is: {}'.format(port)
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print "{} wrote:".format(self.client_address[0])
print self.data
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--port', required=True, help='the TCP port to listen on')
args = parser.parse_args()
HOST, PORT = "localhost", int(args.port)
# Create the server, binding to localhost on port 9999
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
In this example, you must provide the port number as an argument when you start the program using the -p command line switch.
I just found out a solution might be eligible, but it's still not good enough since I still have to change two port in the code to make this work:
import socket
import threading
import SocketServer
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
port = None
def handle(self):
while True:
data = self.request.recv(1024)
if not data:
break
print self.port,data
class ThreadedTCPRequestHandler1(ThreadedTCPRequestHandler):
port = 9999
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler1)
ip, port = server.server_address
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
#print "Server loop running in thread:", server_thread.name
try:
while True:
continue
finally:
print 'quitting server'
server.shutdown()
server.server_close()
I'm trying to adapt the Python Twisted - UDP examples to use UDP broadcast. I can send a message from the client and receive it on the server, however, it isn't sending a message back.
Client:
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
from socket import SOL_SOCKET, SO_BROADCAST
class EchoClientDatagramProtocol(DatagramProtocol):
strings = [
"Hello, world!",
"What a fine day it is.",
"Bye-bye!"
]
def startProtocol(self):
self.transport.socket.setsockopt(SOL_SOCKET, SO_BROADCAST, True)
self.transport.connect("255.255.255.255", 8000)
self.sendDatagram()
def sendDatagram(self):
if len(self.strings):
datagram = self.strings.pop(0)
self.transport.write(datagram)
else:
reactor.stop()
def datagramReceived(self, datagram, host):
print 'Datagram received: ', repr(datagram)
self.sendDatagram()
def main():
protocol = EchoClientDatagramProtocol()
#0 means any port
t = reactor.listenUDP(0, protocol)
reactor.run()
if __name__ == '__main__':
main()
Server:
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
class EchoUDP(DatagramProtocol):
def datagramReceived(self, datagram, address):
print "Received from address: " + str(address)
print str(datagram)
self.transport.write(datagram, address)
print "Finished sending reply."
print "Starting server."
reactor.listenUDP(8000, EchoUDP())
reactor.run()
Console Output:
Server:
Starting server.
Received from address ('192.168.1.137', 53737)
Hello, world!
Finished sending reply.
Client:
no output.
transport.connect creates a connected UDP socket
A connected UDP socket is slightly different from a standard one - it can only send and receive datagrams to/from a single address, but this does not in any way imply a connection. Datagrams may still arrive in any order, and the port on the other side may have no one listening. The benefit of the connected UDP socket is that it it may provide notification of undelivered packages. This depends on many factors, almost all of which are out of the control of the application, but it still presents certain benefits which occasionally make it useful.
I suspect that the response from the server is not being caught by the client as it is listening for responses from the broadcast address, not the specific address of the server.
Instead, just use the self.transport.write(data, (host, port)) form of write without initiating the connection first - this will allow the client to receive packets from any address.
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
from socket import SOL_SOCKET, SO_BROADCAST
class EchoClientDatagramProtocol(DatagramProtocol):
strings = [
"Hello, world!",
"What a fine day it is.",
"Bye-bye!"
]
def startProtocol(self):
self.transport.socket.setsockopt(SOL_SOCKET, SO_BROADCAST, True)
#self.transport.connect("255.255.255.255", 8000) <- not needed
self.sendDatagram()
def sendDatagram(self):
if len(self.strings):
datagram = self.strings.pop(0)
self.transport.write(datagram, ('255.255.255.255', 8000)) # <- write to broadcast address here
else:
reactor.stop()
def datagramReceived(self, datagram, host):
print 'Datagram received: ', repr(datagram)
self.sendDatagram()
def main():
protocol = EchoClientDatagramProtocol()
#0 means any port
t = reactor.listenUDP(0, protocol)
reactor.run()
if __name__ == '__main__':
main()