I'm trying to build a basic chat application and I need it to post messages from other users while waiting for input from the client.
Of course select() is not an option on Windows for this case.
What else can I do?
I tried this, but the program still waits for input and then shows other messages:
from socket import *
from threading import *
s=socket(AF_INET,SOCK_STREAM)
s.connect(('10.0.0.11',1340))
def sender():
while (True):
try:
s.send(raw_input(">> "))
except:
return
def geter():
while(True):
print s.recv(1000)
Thread(target=sender).start()
Thread(target=geter).start()
Related
Context:
I'm writing some Python scripts to build a realtime chat using sockets. I got to allow comunication, data flow and different message types and options. The problem is that the user needs a command input to operate the chat and everytime I call print() function, the text gets printed between the user input, nevertheless the user input keeps data integrity. What can I do to keep the user input clean all the time?
Client script:
def StablishConnection():
...
def SetConfiguration():
...
# This one is for listening
def ListenServer():
while True:
global SERVER_SOCKET, BUFFER_SIZE
try:
data = SERVER_SOCKET.recv(BUFFER_SIZE).decode('utf-8')
print(data)
except Exception:
pass
# This one is for writing
def ChatRoutine():
global SERVER_SOCKET, NICKNAME
while True:
try:
mensaje = input(f'#{NICKNAME}: ')
if not mensaje: continue
SERVER_SOCKET.sendall(f'{MessageType.CONTENIDO_TEXTO.value}\{NICKNAME}\{mensaje}'.encode('utf-8'))
except Exception:
pass
if __name__ == '__main__':
if len(sys.argv) > 1:
HOST = sys.argv[1]
StablishConnection()
SetConfiguration()
writing = threading.Thread(target=ChatRoutine)
writing.setDaemon(True)
writing.start()
listening = threading.Thread(target=ListenServer)
listening.setDaemon(True)
listening.start()
listening.join()
I hope you could give some clue or something, I've struggling with this for about 2 days. Thank you all!!!
I'm trying to understand why I'm not able to open multiple times the same serial port with Digi-Xbee (import digi.xbee) API for python while with Xbee (import xbee) API for python I can.
When I run the code bellow the exception digi.xbee.exception.InvalidOperatingModeException: Could not determine operating mode is raised.
from digi.xbee.devices import *
import time
import codecs
class start(object):
while True:
xbeeApi2 = DigiMeshDevice(port='/dev/ttyUSB0', baud_rate=9600)
xbeeApi2.open()
time.sleep(0.5)
message = xbeeApi2.read_data(timeout=None)
if message is not None:
print(codecs.decode(message.data, 'utf-8'))
time.sleep(1)
XBee module is a S2C (XB24C) set as Digimesh 2.4 TH, firmware 9002 (newest) with a USB Dongle.
Python is 3.7 & my host hardware is a Raspberry Pi 3 B+ running Debian.
Any help would be appreciated.
EDIT 1
Exception is raised when, for the second time, {xbeeApi2.open()} is executed.
In fact, my original code has multiple threads that import the class where the port is opened, many times before the previous thread had the chance to close it.
The 'original' piece of code, that runs fine is bellow:
from xbee import ZigBee
import serial
import time
class start(object):
while True:
ser = serial.Serial('/dev/ttyUSB0', 9600)
xbeeApi2 = ZigBee(ser, escaped=True) # S2 e S2C
time.sleep(0.5)
message = ''
try:
message = xbeeApi2.wait_read_frame(timeout=0.5)
except:
pass #timeout exception
if message is not None:
print(message)
time.sleep(1)
Well, you aren't closing it. Why not create the device and open it before your while True loop?
And you could configure it to just sleep for 0.1 seconds when message is None to reduce processor load. You'll be able to keep up with the message queue that way -- if there was a message, you want to immediately check for another queued message before doing any sleeping.
from digi.xbee.devices import *
import time
import codecs
class start(object):
xbeeApi2 = DigiMeshDevice(port='/dev/ttyUSB0', baud_rate=9600)
xbeeApi2.open()
while True:
message = xbeeApi2.read_data(timeout=None)
if message is not None:
print(codecs.decode(message.data, 'utf-8'))
else:
time.sleep(0.1)
I have a Python3 program that runs a "while True"-loop until stopped, which occasionally saves data to a MySQL database. I am creating an administrative website, separate from the Python program, where I will be able to observe this data.
I now want to be able to be notified, on the website, when changes have been made to the database. My thought was to set up a websocket connection, so that the Python program can send a message through the socket to all connected clients, i.e. all open browsers, if there has been any changes to the database table.
I have done something similar before, but in that case I had to wait for a websocket connection before the "while True"-loop would start. In the new scenario I want to be able to have multiple website clients at once, and let them connect at any time, as well as disconnect without interrupting the Python programs loop.
This is a simplified version of my previous code, which I now want to update to be able to run both with & without websocket clients.
import asyncio
import websockets
socket_server = websockets.serve(run, "127.0.0.1", 5055)
asyncio.get_event_loop().run_until_complete(socket_server)
console_log("Waiting for socket connection...")
asyncio.get_event_loop().run_forever()
async def run(ws):
while True:
db_has_updated = do_stuff()
if db_has_updated:
await ws.send(data)
I just can't seem to be able to come up with the right search terms to find a solution, so I'm asking here instead.
I figured it out, finally! Here is my solution with a websocket server running in a separate thread from the other logic. I'm probably changing some things to make it neater, but this does everything I need. Feel free to ask any questions.
Be aware that this blocks when messaging all the connected clients. That is the way I needed it to work, but you could always thread/subprocess the logic/data-gen part of the program if you want it to run completely asynchronously.
#!/usr/bin/env python3
import asyncio
import websockets
import threading
import time
import random
def gen_data():
print("Generating data...")
time.sleep(3)
data = random.randint(1, 10)
return data
async def send(client, data):
await client.send(data)
async def handler(client, path):
# Register.
print("Websocket Client Connected.", client)
clients.append(client)
while True:
try:
print("ping", client)
pong_waiter = await client.ping()
await pong_waiter
print("pong", client)
time.sleep(3)
except Exception as e:
clients.remove(client)
print("Websocket Client Disconnected", client)
break
clients = []
start_server = websockets.serve(handler, "localhost", 5555)
asyncio.get_event_loop().run_until_complete(start_server)
threading.Thread(target = asyncio.get_event_loop().run_forever).start()
print("Socket Server Running. Starting main loop.")
while True:
data = str(gen_data())
message_clients = clients.copy()
for client in message_clients:
print("Sending", data, "to", client)
try:
asyncio.run(send(client, data))
except:
# Clients might have disconnected during the messaging process,
# just ignore that, they will have been removed already.
pass
Hey so I decided to create an IRC ChatBot whose sole purpose it is to read incoming messages from Twitch Chat and if a giveaway is recognized by a keyword it's supposed to enter the giveaway by sending !enter in Chat.
I build the Bot upon this source: https://github.com/BadNidalee/ChatBot. I only changed things in the Run.py so thats the only Code I'm going to post. The unaltered ChatBot does work but it has no reconnect ability and regularly stops receiving data because the socket closes or other reasons.
All I wanted to change was make it so that the ChatBot is stable and can just stay in the IRC Chat constantly without disconnecting. I tried to achieve this by setting a timeout of 8 seconds for my socket and catching timeout exceptions that would occur and reconnect after they occur.
And all in all it does seem to work, my Bot does what it's supposed to even when alot of messages are coming in, it recognizes when a Giveaway starts and answers acordingly. IRC Server PING Messages are also handled and answered correctly. If there is no message in Chat for over 8 seconds the Exception gets thrown correctly and the Bot also reconnects correctly to IRC.
BUT heres my Problem: After seemingly random times the socket will literally just Stop working. What I find strange is it will sometimes work for 20 minutes and sometimes for an hour. It doesn't occur when special events, like lots of messages or something else happens in Chat, it really seems random. It will not timeout there's just nothing happening anymore. If I cancel the program with CTRL-C at this point the console sais the last call was "readbuffer = s.recv(1024)" But why is it not throwing a timeout exception at that point? If s.recv was called the socket should timeout if nothing is received after 8 seconds but the program just stops and there is no more output until you manually abort it.
Maybe I went about it the wrong way completely. I just want a stable 24/7-able ChatBot that scans for one simple keyword and answers with one simple !enter.
This is also my first Time programming in Python so If I broke any conventions or made any grave mistakes let me know.
The getUser Method returns the username of the line of chat that is scanned currently.
The getMessage Method returns the message of the line of chat that is scanned.
The openSocket Method opens the Socket and sends JOIN NICK PASS etc to the IRC
#!/usr/bin/python
import string
import socket
import datetime
import time
from Read import getUser, getMessage
from Socket import openSocket, sendMessage
from Initialize import joinRoom
connected = False
readbuffer = ""
def connect():
print "Establishing Connection..."
irc = openSocket()
joinRoom(irc)
global connected
connected = True
irc.settimeout(8.0)
print "Connection Established!"
return irc
while True:
s = connect()
s.settimeout(8.0)
while connected:
try:
readbuffer = s.recv(1024)
temp = string.split(readbuffer, "\n")
readbuffer = temp.pop()
for line in temp:
if "PING" in line:
s.send(line.replace("PING", "PONG"))
timern = str(datetime.datetime.now().time())
timern = timern[0:8]
print timern + " PING received"
break
user = getUser(line)
message = getMessage(line)
timern = str(datetime.datetime.now().time())
timern = timern[0:8]
print timern +" " + user + ": " + message
if "*** NEW" in message:
sendMessage(s, "!enter")
break
except socket.timeout:
connected = False
print "Socket Timed Out, Connection closed!"
break
except socket.error:
connected = False
print "Socket Error, Connection closed!"
break
I think you've missunderstood how timeout work on the socket.
s.settimeout(8.0)
Will only set s.connect(...) to timeout if it can't reach the destination host.
Further more, usually what you want to use instead if s.setblocking(0) however this alone won't help you either (probably).
Instead what you want to use is:
import select
ready = select.select([s], [], [], timeout_in_seconds)
if ready[0]:
data = s.recv(1024)
What select does is check the buffer to see if any incoming data is available, if there is you call recv() which in itself is a blocking operation. If there's nothing in the buffer select will return empty and you should avoid calling recv().
If you're running everything on *Nix you're also better off using epoll.
from select import epoll, EPOLLIN
poll = epoll()
poll.register(s.fileno(), EPOLLIN)
events = poll.poll(1) # 1 sec timeout
for fileno, event in events:
if event is EPOLLIN and fileno == s.fileno():
data = s.recv(1024)
This is a crude example of how epoll could be used.
But it's quite fun to play around with and you should read more about it
I'm trying to write a makefile that will replicate a client/server program I've written (which is really just two Python scripts, but that's not the real question of concern)...
test:
python server.py 7040 &
python subscriber.py localhost 7040 &
python client.py localhost 7040;
So I run make test
and I get the ability to enter a message from client.py:
python server.py 7040 &
python subscriber.py localhost 7040 &
python client.py localhost 7040;
Enter a message:
When the client enters an empty message, he closes the connection and quits successfully. Now, how can I automate the subscriber (who is just a "listener) of the chat room to close - which will in turn exit the server process.
I was trying to get the process IDs from these calls using pidof - but wasn't really sure if that was the correct route. I am no makefile expert; maybe I could just write a quick Python script that gets executed from my makefile to do the work for me? Any suggestions would be great.
EDIT:
I've gone writing the Python script route, and have the following:
import server
import client
import subscriber
#import subprocess
server.main(8092)
# child = subprocess.Popen("server.py",shell=False)
subscriber.main('localhost',8090)
client.main('localhost', 8090)
However, now I'm getting errors that my global variables are not defined ( I think its directly related to adding the main methods to my server (and subscriber and client, but I'm not getting that far yet:). This may deserve a separate question...
Here's my server code:
import socket
import select
import sys
import thread
import time
# initialize list to track all open_sockets/connected clients
open_sockets = []
# thread for each client that connects
def handle_client(this_client,sleeptime):
global message,client_count,message_lock,client_count_lock
while 1:
user_input = this_client.recv(100)
if user_input == '':
break
message_lock.acquire()
time.sleep(sleeptime)
message += user_input
message_lock.release()
message = message + '\n'
this_client.sendall(message)
# remove 'this_client' from open_sockets list
open_sockets.remove(this_client)
this_client.close()
client_count_lock.acquire()
client_count -= 1
client_count_lock.release()
def main(a):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = a
server.bind(('', port))
server.listen(5)
message = ''
message_lock = thread.allocate_lock()
client_count = 2
client_count_lock = thread.allocate_lock()
for i in range(client_count):
(client,address) = server.accept()
open_sockets.append(client)
thread.start_new_thread(handle_client,(client,2))
server.close()
while client_count > 0:
pass
print '************\nMessage log from all clients:\n%s\n************' % message
if __name__ == "__main__":
if sys.argv[1]:
main(int(sys.argv[1]))
else:
main(8070)
Use plain old bash in the script, get the PID and use kill.
Or, much much much much better, create a testing script that handles all that and call that from your Makefile. A single run_tests.py, say.
You want to keep as much logic as possible outside the Makefile.
related to 'global' issue => define handle_client inside main and remove the global message, client_count,... line