Send data using a already open socket in python - python

I built a small program that listen to arbitrary connections and processes their requests, saving the events and connections information to a sqlite database. Once the connection is closed and opened again, it updates the source port.
This is working like a charm, but now I want to be able to randomly send those hosts messages, and I can't open new connections to them because they may be behind a NAT.
Is there a way to utilize the already open connections to transmit data? I mean, I could write a function in the thread_cliente's while True, that checks if there is a message in the database to be delivered to it, but I figured that there must be an easier or a more elegant way to do it.
Below is my code for a socket server. I translated my comments from Portuguese to English. Unfortunately my co-workers insist on using Portuguese variables, so this may cause some confusion.
#!/usr/bin/env python3
import socket
import sys
import logging
from _thread import start_new_thread
from MyPack import all_my_stuff
def print_help():
print(f'{sys.argv[0]} [TCP Port] [Someting that my program do]')
sys.exit()
def start_server(porta):
servico = socket.socket()
try:
servico.bind(('', porta))
except socket.error as e:
logging.error(e)
servico.listen(1024) # Is 1024 the max simultaneous client number?
while True:
cliente, endereco = servico.accept()
logging.debug(
f'New connection {endereco[0]}:{endereco[1]}'
)
try:
start_new_thread(thread_cliente, (cliente, endereco, ))
except Exception as e:
logging.error(e)
servico.close()
def thread_cliente(conexao, endereco):
while True:
data = conexao.recv(128)
if data:
resposta = None
sibulla_db.insere_conexao(endereco[0], endereco[1], sys.argv[1])
resposta = all_my_stuff.do_something(data, sys.argv[1], endereco)
if resposta:
logging.debug(f'Sending data: {resposta}')
conexao.sendall(bytes.fromhex(resposta))
else:
# Close connection
logging.debug(
f'Connection closed {endereco[0]}:{endereco[1]}'
)
break
conexao.close()
def main():
if len(sys.argv) == 1 or sys.argv[1] == 'help':
# are you running it right, or asking for help
print_help()
# Log
logging.basicConfig(
format='%(asctime)s %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
filename='sibulla.log',
level=logging.DEBUG
)
# Start it
start_server(int(sys.argv[1]))
if __name__ == '__main__':
main()

Sending back on the original socket is probably the best way to talk to the clients.
If you're not concerned about the number of clients getting too high, you could keep the client connection open until you need it, and save the socket somewhere for later use.

Related

Looking for help in making my socket messenger send instantaneously in Python

Im sure there are easier ways with particular python modules, but for an assignment I need to create a program that can act as a client/server. As of right now I have it working to the point of only being able to send a message if the reciever has responded. I need it to just send and appear on the respective client/server terminal when enter is pressed. Any help would be greatly appreciated!
These are pictures of what happens as of now
https://i.stack.imgur.com/T9CsJ.png
import sys
import socket
import getopt
def usage(script_name):
print('Usage: py' + script_name + '-l' +' <port number>' + '[<server>]')
def sockObj():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
return sock
def serversockConn(serversocket,port):
serversocket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# bind the serversocket to address
serversocket.bind(('',int(port)))
# enable a server to accept connections
serversocket.listen(5)
# wait for a connection and accept it
sock,addr = serversocket.accept()
return sock
def connToServersock(sock,server, port):
# connect to a serversocket
if server:
sock.connect((server, int(port)))
else:
sock.connect(('localhost', int(port)))
return sock
if __name__ == '__main__':
## get the command line arguments
try:
options, non_option_args = getopt.getopt(sys.argv[1:],'l')
except getopt.GetoptError as err:
print(err)
sys.exit(2)
#check if '-l' is present in command line agrument
serverSide = False
for opt,arg in options:
if opt == "-l":
serverSide = True
# port number
port = non_option_args[0]
# server address
server = None
hostLen = len(non_option_args)
if hostLen == 2:
server = non_option_args[1]
# create a communicator object, and make a connection between server and client
# server
if serverSide:
serversocket = sockObj()
sock = serversockConn(serversocket,port)
# client
else:
sock = sockObj()
sock = connToServersock(sock,server,port)
while True:
## read a message from standard input
message = sys.stdin.readline().encode()
if len(message) != 0:
sock.send(message)
return_msg = sock.recv( 1024 )
if return_msg:
print("Message recieved: " + return_msg.decode())
else:
print("Other side shut down")
else:
try:
sock.shutdown(socket.SHUT_WR)
sock.close()
except:
pass
I think your issue is that there are two places in your event loop where you block:
message = sys.stdin.readline().encode()
Here, you block until the user has pressed return -- during this time, your program is unable to respond to any data received over the network, because it is blocked waiting for data from stdin.
... and:
return_msg = sock.recv( 1024 )
Here, you are waiting for data to be received from the network -- during this time, your program is unable to respond to any data received from stdin, because it is blocked waiting for data from the network.
The behavior you'd ideally like to have is for your program to wait for both stdin and network traffic simultaneously -- i.e. have it block until either the user has pressed return, or some network data has been received, whichever comes first.
The easiest way to achieve that behavior is to use select(); its purpose is to block until at least one of several file descriptors is ready to be acted on. (Note, however, that Windows does not support using select() on stdin, so if your program needs to run under Windows you will probably have to spawn a second thread instead).
To implement the event loop using select(), add import select to the top of your script, then replace your event loop with something like this instead:
while True:
## block here until either sock or sys.stdin has data ready for us
readable, writable, exceptional = select.select([sock, sys.stdin], [], [])
if sys.stdin in readable:
## read a message from standard input
message = sys.stdin.readline().encode()
if len(message) != 0:
sock.send(message)
if sock in readable:
## read a message from the network
try:
return_msg = sock.recv( 1024 )
if (return_msg):
print("Message received: " + return_msg.decode())
else:
print("Other side shut down")
break
except:
print("recv() threw an exception")
break

Publishing data via socket after connection is aborted

I have a Python (2.7) script which is reading realtime data from a file and publishing it (via network) to a server living on a different computer. This server, in particular, is a Carbon server part of graphite.
The relevant part of the code is as follows:
import socket
CARBON_HOST = 'COMPUTER-NAME'
CARBON-PORT = 2003
CARBON_PATH = 'folder.name.meaurement'
s = socket.socket()
s.connect((CARBON_HOST, CARBON_PORT))
while True:
if s:
s.send('%s %s %s\n'%(CARBON_PATH, str(data), int(time.time())))
time.sleep(WAIT)
where data is the latest entry imported from my file, and time is the usual.
When I switch off the computer COMPUTER-NAME where the Carbon server lives, this error appears:
s.send('%s %s %s\n'%(CARBON_PATH, str(data), int(time.time())))
socket.error: [Errno 10053] An established connection was aborted by the software in your host machine
When I restart the host machine (COMPUTER-NAME), I have to restart the Python script for data to be sent over again.
Is there a way I can tell the socket to pause if it sees it's disconnected, or to keep trying until the connection is open again?
You can't use the same socket after a socket.error exception, the connection is broken. However, you can catch the exception, create a new connection, and use that to send the data.
About your last question, you can tell your program to keep trying until the data is sent with a while loop. A basic example,
import socket
import time
CARBON_HOST = 'COMPUTER-NAME'
CARBON_PORT = 2003
CARBON_PATH = 'folder.name.meaurement'
WAIT = 10
s = socket.socket()
s.connect((CARBON_HOST, CARBON_PORT))
data = 'my data'
while True:
packet = '%s %s %s'%(CARBON_PATH, str(data), int(time.time()))
while True:
try:
s.send(packet)
break
except socket.error as e:
print(e)
s.close()
s = socket.socket()
s.connect_ex((CARBON_HOST, CARBON_PORT))
time.sleep(WAIT)
s.close()
I recommend to read about socket.timeout("seconds to sleep") which will give you idea of using it. In your case, use socket.settimeout() right before making a socket connection, and socket.settimeout(None) soon after socket.connection().
In this way, you can delay establishing connection. But if timeout value goes beyond system/server down time, then script will eventually come out with same timeout error.
connect_timeout = 100 #in seconds
socket.settimeout(connect_timeout)
socket.connect()
socket.settimeout(None)
Check if that works?

Will TCP Socket Server client connection fd cause memory leak?

I don't if i need to close the client socket handle( conn ) such as "conn.close()" ?
If I run multithread to handler the client socket fd ( conn ). Does it cause memory leak if the server runs too long time?
Will the server not close the client socket fd if client no invokes conn.close()?
Following is my tcp-socket server code:
# coding: utf-8
import socket
import os, os.path
import time
sockfile = "./communicate.sock"
if os.path.exists( sockfile ):
os.remove( sockfile )
print "Opening socket..."
server = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
server.bind(sockfile)
server.listen(5)
print "Listening..."
while True:
conn, addr = server.accept()
print 'accepted connection'
while True:
data = conn.recv(1024)
if not data:
break
else:
print "-" * 20
print data
print "DONE" == data
if "DONE" == data:
# If I need to invoke conn.close() here?
break
print "-" * 20
print "Shutting down..."
server.close()
os.remove( sockfile )
print "Done"
According to the document, close is called when the socket is garbage collected. So if you didn't close it for whatever reason, your program would probably be fine. Provided your socket objects do get GCed.
However, as a standard practice, you must close the socket, or release whatever resource, when your code is done with it.
For managing socket objects in Python, check out
How to use socket in Python as a context manager?
One way to find out is to test it and see! Here is a little Python script that I ran on my Mac (OS X 10.11.5). If I un-comment the holdSockets.append() line, this script errors out ("socket.error: Too many open files") after creating 253 sockets. However, if I leave the holdSockets.append() line commented out, so that the sockets can be garbage collected, the script runs indefinitely without any errors.
#!/bin/python
import socket
import time
count = 0
holdSockets = []
while True:
count += 1
nextSock = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
#holdSockets.append(nextSock)
time.sleep(0.1)
print "Created %i sockets" % count

Python: Notification System?

I am working on a chat server that runs on my local network using socket, and then I have a client program running on all of the computers in my house, and this program allows all of the clients to talk to each other.
The problem is, you have to manually update the chat log by pressing enter.
The way I want it to work, maybe, is to check for a new message every few seconds, and if there is a new one, play a sound. Does anyone know how I can do this, I'll try to figure it out on my own, as I have done with most of this project, but any help is appreciated.
Here is the server:
import socket
import sys
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('192.168.1.80', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
sock.bind(server_address)
sock.listen(1)
print 'Waiting for user...'
convo='Welcome!'
while True:
# Find connections
connection, client_address = sock.accept()
try:
data = connection.recv(999)
if data=='EMPTY':
pass
else:
print data
convo=convo+'\n'+data
connection.sendall(convo)
except:
connection.close()
Here is the client:
import socket
import sys,os
name=raw_input("Enter name: ")
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('192.168.1.80', 10000)
print >>sys.stderr, 'connecting to %s port %s' % server_address
while True:
message=raw_input('Message: ')
try:
os.system('cls')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(server_address)
if message is not '':
sock.sendall(name+": "+message)
else:
sock.sendall('EMPTY')
if message=='quit':
break
x=sock.recv(999)
print x
except:
break
sock.close()
Thanks!
If you need two operations to happen at the same time (the client script needs to read input from the user and read new messages from the server), then you'd need to either use threads (one thread for reading user input, and one for reading messages from the server), or futures (since python3.2).
Here's question for playing audio in python: Play audio with Python
As for your client, why are you reconnecting to your server every single time? Anyway, if I understand the problem correctly you're blocking on user input, but also want to handle messages from the server.
Without getting complicated with threads, I would recommended using a recurring signal, which I believe could handle this. There's a function call setitimer(), which will break what you're doing and call a function every so often then return to where you were (user input). In your timer function, check for server messages, print any received, play your sound and return to user input. There's an setitimer() example enter link description here.
Might be a little ugly with the user typing, so you may need to reprint what they're currently typing, but haven't sent out (using something other than raw_input()).
For a slightly more complicated option, which may help you there's a function call select(), which can block while listening for socket input AND user input. Then you just distinguish which is which and keep it all in one loop.
while True:
# select on server socket and user input (blocks for either one)
# if here, either a message has been received or the user typed something
# if a message from server
...
playSound()
# else
# send to server

Client socket not receiving data correctly

I've tried looking about for an answer but I can't seem to find one that answers my specific problem.
Perhaps I don't know how to articulate the problem correctly.
I think I've pinpointed what it is, but the thing is I just don't know how to fix it.
EDIT: I was trying to use two clients on one TCP Socket. Can't do that. I'll have to think of another way. Solved, I guess.
So what I've got is are
1: Two Clients
2: One Server
The objective is this:
Have the server distribute new usernames to all the clients as they connect.
This is what happens when I run the program:
Server: Define Host, and Port, initialize it. Check
Client 1: Connects to the server. Check
Client 1: Once connected, sends a string to the server. Check
Server: Receives a string, checks if the string is in a list is created. If it is: Pass, if it's not, send to everyone the new string. Check
Client 1: [Now waiting to receive data] Recieves data, checks if the string received matches the one it sent. If it does, print("It's one of ours!"), else, make the new string = to Client 2 Username. Check
Client 2: Connects to server: Check
Server: [If it receives a string, prints it.] (Works) Checks if the new string is in the list. [It isn't] So It sends the new username to everyone, and then prints ("Sent to everyone") Check
But, when client 2 receives the string, it prints it. However, client 1 never recives the string.
And when running client one in IDLE, I noticed something went wrong as Client 1 tried to receive the data. (The while loop that the data = s.recv began looping real fast, instead of waiting)
I've asked around in chat, but it seems nobody's around right now. I've tried looking this up but I really can't find an answer. What I suspect is happening is that when my server sends to 'connection' the second time, it somehow overrides the original client connection.
Here's my server code:
from socket import *
import threading
import os
import csv
Username_List = []
host = input("Host: ")
port = input("Port: ")
ss = socket(AF_INET,SOCK_STREAM)
ss.bind((host,int(port)))
ss.listen(2)
while True:
try:
connection,address = ss.accept()
data = connection.recv(1024)
if data:
translated_data = data.decode()
print(translated_data)
if translated_data in Username_List:
pass
else:
Username_List.append(translated_data)
connection.sendall(translated_data.encode())
print("Sent new username to everyone")
except IOError:
connection.close()
print("An exception with a connected user occured")
break
And here is my client code: [The only difference between client 1 and 2 is I changed the username variable]
# Sample Username Client Service Handler.
from socket import *
import threading
import os
import csv
Username = ("Owatch")
host = input("Host: ")
port = input("Port: ")
try:
ss = socket(AF_INET,SOCK_STREAM)
ss.connect((host,int(port)))
except IOError:
print("Aw no man")
ss.send(Username.encode())
while True:
try:
print("Waiting to Recieve Data")
data = ss.recv(1024)
if data:
translated_data = data.decode()
print(translated_data)
if translated_data == Username:
print("It's one of ours!")
else:
Client_Username = translated_data
print (Client_Username)
except Exception as e:
print (vars(e))
If you could please help I'd be grateful.
If you know of an answer to my question that's already been asked, please tell me and I'll remove this post to avoid breaking rules. Thanks!
Right then I started with what you had then changed it till it worked what I've done is created a client class which starts a thread with each connection and adds it to a list of threads (please if I'm doing something horribly wrong smarter people correct me), the thread runs gets some data checks if that's in the list of user names if its not sends out a message to all the clients in the thread list with that name then the thread just chills out. Anyway on to the code.
SERVER!!!
import csv
class client(threading.Thread):
Username_List = []
def __init__(self, conn):
super(client, self).__init__()
self.conn = conn
def run(self):
print "Client thread started"
data = self.conn.recv(1024)
print "Received: {0}".format(data)
if data in client.Username_List:
self.send_msg("Welcome Back!")
else:
for cnt in threadz:
cnt.send_msg(data)
print("Sent new username to everyone")
client.Username_List.append(data)
while True:
# dont need nothing now
pass
def send_msg(self,msg):
self.conn.send(msg)
host = input("Host: ")
port = input("Port: ")
ss = socket() #AF_INET,SOCK_STREAM)
ss.bind((host,int(port)))
print "Server Opening on port: {0}".format(port)
ss.listen(2)
threadz = []
print "Begining Wait for connections"
while True:
try:
connection, address = ss.accept()
print "Got ONE!"
c = client(connection)
print "Recevied connection from:{0} On port:{1}".format(address[0],address[1])
c.start()
threadz.append(c)
print "Client appended to threadz, currently {0} threadz active".format(len(threadz))
except IOError,KeyboardInterrupt:
connection.close()
print("An exception with a connected user occured")
break
The CLIENT:
# Sample Username Client Service Handler.
from socket import *
import threading
import os
import csv
Username = ("ShyGuy")
host = input("Host: ")
port = input("Port: ")
try:
ss = socket() #AF_INET,SOCK_STREAM)
ss.connect((host,int(port))) #I was using ("localhost",1234) for testing
ss.send(Username)
except IOError:
print("Aw no man")
print("Waiting to Recieve Data")
while True:
try:
data = ss.recv(1024)
if data:
translated_data = data.decode()
print(translated_data)
if translated_data == Username:
print"Name: {0} has been registered on server!".format(translated_data)
else:
Client_Username = translated_data
print "New client name received: {0}".format(Client_Username)
except Exception as e:
print (vars(e))
That works on python 2.7 with two clients locally. Needs to use a semaphore to stop the threads printing at the same time as the main server loop prints: http://en.wikipedia.org/wiki/Semaphore_(programming)
This code does nothing graceful with client disconnects, but once you can work with the exceptions that a raised when that happens I'm sure you'll learn some more.

Categories

Resources