Keep console input line below output - python

[EDIT:]
I'm currently trying to make a small tcp chat application. Sending and receiving messages already works fine... But the problem is:
When i start typing a message while i receive one... it appears after the text I'm writing
Screenshot: http://s7.directupload.net/images/140816/6svxo5ui.png
[User sent > "hello", then I started writing "i am writing..." then user wrote " i sent a..." before i sent my message... so it has been placed after my input...
I want the incoming message always to be before my input !
this is my current code:
Client.py
con = connect.User()
server = raw_input("Type in the server adress \n[leave blank to use xyr.no-ip.info]\n>:")
nick =""
while nick == "":
nick = raw_input("Type in your nickname\n>:")
con.connect(server, nick)
def sender():
print("Sender started")
while 1:
msg = raw_input()
if msg == "q":
break
con.send(msg)
con.disconnect()
def receiver(server):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if server == "":
server="xyr.no-ip.info"
sock.connect((server, 8000))
sock.send("%pyreceiver\n")
print("Receiver started")
while 1:
msg_in = sock.recv(1024)
if not str(msg_in).startswith("[py]" + nick):
if str(msg_in).startswith("/ua"):
print(str(msg_in)[3:])
elif str(msg_in).startswith("/u "):
print(str(msg_in)[2:])
else:
print(str(msg_in[:-1]))
#
if nick == "":
nick = "guest"
print("Name changed to ""guest""")
time.sleep(.5)
thread.start_new_thread(receiver, (server, ))
time.sleep(.5)
thread.start_new_thread(sender())
Connect.py
import socket
import time
class User():
nickel =""
def connect(self, server="xyr.no-ip.info", nick="guest"):
nickel = nick
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if server == "":
server="xyr.no-ip.info"
print("server changed to xyr.no-ip.info")
time.sleep(.5)
print("Connecting...")
self.sock.connect((server, 8000))
print("Connected")
time.sleep(.4)
self.sock.send("[py]" + nick + "\n")
self.sock.send(nick + " connected with a python client\n")
print("registered as " + nick)
time.sleep(.3)
def send(self, msg):
self.sock.send(msg + "\n")
def disconnect(self):
self.sock.close()
print("disconnected")

Your code writes everything to stdout. Whenever something arrives to either of your sender/receiver threads, it prints to stdout. The issue with that is, due to the fundamental nature of output streams, you cannot accomplish the following :
place incoming messages above the stuff currently being typed/echoed.
Things happen strictly in the order of occurrence. The moment something comes in, wherever the cursor is, the print statement dumps that data over there. You cannot modify that behaviour without using fancier / more powerful constructs.
In order to do what you want, I would use ncurses. You seem to be using python on Windows, so you're going to have to do some digging on how to get equivalent functionality. Check out this thread : Curses alternative for windows

I had a similar problem and I found that a simpler solution (for me) was to get input via readchar (https://github.com/magmax/python-readchar/tree/master/readchar).
Using readchar, I would buffer each keystroke (checking for key.BACKSPACE and CR - see code snippet below).
All output I would prepend with "/033[1A" (make the cursor move up), print the output line, and then a "/n"...
after each output line, I move the cursor to the beginning and re-print the self.input_buff
while the user is doing input, this handles console input, displaying what they are typing:
keypress = readkey()
if keypress == key.BACKSPACE:
self.input_buff = self.input_buff[:-1]
print("\033[0A%s " % self.input_buff)
continue
if keypress != key.CR:
self.input_buff = "%s%s" % (self.input_buff, keypress)
print("\033[0A%s" % self.input_buff)
continue
This kept the input line at the bottom and all terminal output above it.
I know it comes a year late and if you are a wiz with curses, maybe that is the way to go...

Related

Chat server - can't receive message while getting input from user

Okay so I understood what I did wrong. But I have another problem now, where using multithreading doesn't work as intended. In the code below, in the main fucntion, I start the message_listener thread, which should work in the background while I get user input. But when I send something from another client, the message doesn't get printed (and it should) in the other client's terminal. Not even after the receiver client send a message himself. I dont understand what I got wrong.
import socket
import sys
import threading
from datetime import datetime
import errno
import msvcrt
HEADER_LENGTH = 10
HOST = "127.0.0.1" # Standard loopback interface address (localhost)
PORT = 50000 # Port to listen on (non-privileged ports are > 1023)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", PORT))
client.setblocking(False)
username = input("Choose a username: ")
username_header = f"{len(username.encode()):<{HEADER_LENGTH}}".encode()
client.send(username_header + username.encode())
def client_input(u_name):
print(f"{u_name}>", end="")
msg = ""
done = False
while not done:
if msvcrt.kbhit():
pressed = msvcrt.getch()
if pressed == b"\r":
print("")
done = True
else:
print(pressed.decode(), end="")
msg += pressed.decode()
return msg
def message_handler():
while True:
try:
uname_header = client.recv(HEADER_LENGTH)
if not len(uname_header):
print('Connection closed by the server')
sys.exit()
# actual username
sender_username_length = int(uname_header.decode().strip())
sender_username = client.recv(sender_username_length).decode()
sender_message_header = client.recv(HEADER_LENGTH)
sender_message_length = int(sender_message_header.decode().strip())
sender_msg = client.recv(sender_message_length).decode()
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print(f"<{current_time}> {sender_username}: {sender_msg}")
except IOError as error:
if error.errno != errno.EAGAIN and error.errno != errno.EWOULDBLOCK:
print(f"There was some error reading data: {str(error)}")
sys.exit()
# no data was received (everything's fine)
return
except Exception as error:
print(f"There was some error reading data: {str(error)}")
sys.exit()
def main():
msg_listener = threading.Thread(target=message_handler)
msg_listener.start()
while True:
message = client_input(username)
# if message is not empty, send it.
if message:
# Encode message to bytes, prepare header and convert to bytes, like for username above, then send
message = message.encode()
message_header = f"{len(message):<{HEADER_LENGTH}}".encode()
client.send(message_header + message)
if __name__ == "__main__":
main()
It seems like the message_handler doesn't receive anything... Because if for example, I put a print statement after the uname_header = client.recv(HEADER_LENGTH) line , it doesn't get printed. So it means the thread isn't working in the background? i dont get it.
For some reason, after one iteration over the while True loop in the main function, the msg_listener thread stops. Why does it stop tho? it has a while True loop in it!
This is because the program waits for input from the user before receiving new messages. Your actions do not work in parallel.
I suggest making the following changes
import threading
def message_handler():
while True:
# open listening and print new msg
if __name__ == "__main__":
msg_listener = threading.Thread(target=message_handler)
msg_listener.start()
while True:
message = client_input(username)
# send msg
In this case, you will always listen to messages in the background. And in the main thread to process user input

How to take inputs and outputs properly from the same console as a client (connected to a remote server)?

I am currently working on a program in Python that works as a client, and needs to connect remotely to a server using the TCP/IP protocol. After the server receives the client's username, the client can send messages to other clients by typing "#<username> <message>", and this input will be further processed and the message that will be sent to the server will be constructed as "SEND <username> <message>", and this will be actually recognized by the server. Then the server will send back an acknowledgement to the sending client, and the actual message to the destination client.
My approach is to use a main function named chat_run(), used for input and constructing the message that will be sent to the server, and in parallel to run a function named OutputRecvMsg() in a different thread that will receive messages from the server and output them in the console.
The problem is, I want the beginning of all the input lines to start with username >, and the messages received from the server to be output immediately on a new line, and the client to wait for a new input.
My current implementation problem seems to be in receiving messages (the OutputRecvMsg() function). After it outputs a message to the console, I need to press Enter to ask for input, because it remains stuck.
For me, there are two questions regarding the current problem, maybe two threads try to access the same resource (console), maybe I made a mistake regarding the construction of the received message (because I know that sock.recv(4096) is blocking and I tried to avoid a blocking state).
import socket
import time
import re
import threading as th
SERVER_REPLY_1 = 'HELLO'
SERVER_REPLY_2 = 'IN-USE'
AT_SYMBOL = '#'
host_port = ('remote_server_add', 5378)
def build_loggin_msg(msg):
return 'HELLO-FROM ' + msg + ' \n'
def chat_run(sock, currentUser):
while True:
rawInput = input(currentUser + '> ')
if rawInput == '!who':
sock.sendall('WHO\n'.encode())
elif rawInput == '!quit':
sock.close()
break
else:
splittedMsg = re.split(r'\s', rawInput, maxsplit = 1)
if len(splittedMsg) > 1 and splittedMsg[0].startswith(AT_SYMBOL):
userNameToSend = splittedMsg[0][1:]
message = 'SEND ' + userNameToSend + ' ' + splittedMsg[1] + ' \n'
sock.sendall(message.encode())
def OutputRecvMsg(sock, currentUser):
OutMsg =''
chunk = ''
while True:
try:
chunk = sock.recv(4096).decode()
if not chunk:
pass
else:
OutMsg += chunk
except BlockingIOError as e:
if OutMsg:
print(OutMsg)
OutMsg = ''
if __name__ == '__main__':
loggedIn = False
currentUser = None
_data = ''
while not loggedIn:
currentUser = input('Add a username please: ')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(host_port)
sock.sendall(build_loggin_msg(currentUser).encode())
data = sock.recv(4096).decode()
print(data)
if data.startswith(SERVER_REPLY_1):
loggedIn = True
else:
print('Close connection for retry another username')
sock.close()
time.sleep(1)
sock.setblocking(0)
th.Thread(target=OutputRecvMsg, args=(sock, currentUser)).start()
chat_run(sock, currentUser)
As an example:
Add a username please: Nickname
HELLO Nickname
Nickname> #Nickname hello man -> send to me
Nickname> DELIVERY Nickname hello man
SEND-OK -> here I have to press enter to get the next lines
Nickname>

Python while loop is pausing

I am trying to do multiple while loops but somehow they don't work. I already searched the internet but none of the problems I found has the same issue.
So here is the code containing only the necessary information. I am basically opening a socket, giving an in input (i\n) and receiving the output in the first step. I want to continue receiving the output until I have some specific characters xxx in the output. Then I want to go to the elif statement in the next loop.
def netcat(h, p):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((h,p))
i = 0
a = True
while a == True:
socket_list=[sys.stdin, s]
r,w,x = select.select(socket_list, [], [])
if i==0:
time.sleep(1)
message = s.recv(1024)
print(message)
s.send("i\n")
sys.stdout.flush()
while "xxx" not in message:
message = s.recv(1024)
print(message)
i+=1
elif i==1:
print("+++++++++++++++++++++++++++")
i+=1
print("hello")
server.close()
What I would expect the code to do is to print the message from the if statement, then print hello, then the message from the elif statement and then hello over and over again because the while loop is still active. So in summary this is the expected output:
message
hello
+++++++++++++++++++++++++++
hello
hello
hello
hello...
What it really prints is
message
hello
and then it finishes.
What I found out is that if I comment out the following lines:
while "xxx" not in message:
message = s.recv(1024)
print(message)
it works as expected. The hello at the end of the code gets printed to the screen over and over again. I just don't get it why this second while loop has anything to do with it. I would really appreciate help here.
Since the working code was requested, here is also the full code. The hostname and port are from a CTF which is still working so you will be interacting with the CTF-server:
#!/usr/bin/env python
import socket
import time
import select
import sys
base64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ ="
hostname = "18.188.70.152"
port = 36150
def netcat(h, p):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((h,p))
i = 0
a = True
b = True
while a == True:
socket_list=[sys.stdin, s]
r,w,x = select.select(socket_list, [], [])
if i==0:
time.sleep(1)
message = s.recv(1024)
print(message)
s.send("i\n")
sys.stdout.flush()
while "flag" not in message:
message = s.recv(1024)
print(message)
txtfile = message[9:38]
print(txtfile)
i+=1
elif i==1:
print("+++++++++++++++++++++++++++")
i+=1
print("hello")
server.close()
netcat(hostname, port)
You're mixing event-based code (select.select()) with blocking synchronous code (your small while loop with the s.recv()).
If you want your code not to block, every recv() needs to be paired up with a preceding select().
Not only that, but you must also check the returned values from select(). Only s.recv() if s was in the first returned list. If you s.recv() in any other case, the code will also block on the receive call.
Update:
Try something along the lines of:
not_done = True
while not_done:
read_sockets, _, _ = select.select([sys.stdin, s], [], [])
if s in read_sockets:
message = s.recv(1024)
print(message)
... more code ...
if 'flag' in message:
... react on flag ...
if 'quit' in message:
not_done = False
... processing of other sockets or file descriptors ...
The important point being that there is only this one s.recv() in the if branch that checks for whether select detected something was received.
The outer while will just come back to the same if branch later when additional data was received.
Note that processing stdin alongside socket code is tricky and will likely also block at some point. You will likely have to put the terminal into raw mode or something first and then be ready to process partial lines yourself as well as maybe also echoing the input back to the user.
Update:
If you want to do something while no message was received, you can give a timeout to select() and then do other processing if there was nothing received on the socket. Something like this:
say_hello_from_now_on = False
not_done = True
while not_done:
read_sockets, _, _ = select.select([s], [], [], 1)
if s in read_sockets:
message = s.recv(1024)
print(message)
say_hello_from_now_on = True
elif say_hello_from_now_on:
print("hello")
I'd check your indentation, try installing and running autopep8 on your code and see if that fixes any of your issues.
[edit] user has updated their question and it's clear that this isn't the answer.

How to add visuals to python chat program

I have written a basic python socket based chat program (My second one) and i would like to add some visuals to make it a little more user friendly.
Should I layer the visuals over the existing program or should i make a new program around the visuals
What python module should i use for the visuals (i know pygame is that suitable)
Can i have some form of general idea on how to write this (Code examples maybe?)
Here is my existing code:
Client:
import socket, threading
#Setup The Variables
WindowTitle = 'Chat 2.0 - Client'
s = socket.socket()
host = raw_input("Please Enter External IP adress Here: ")
print
host = socket.gethostname()
port = 8008
#Attempted To Connect
print "Conecting..."
print
while True:
try:
s.connect((host, port))
break
except:
pass
print "Connected To " + str(host) + " " + str(port)
print
#Check For Incomming Data
def check_for_data():
while True:
data = s.recv(1024)
if data:
print
print "Other: " + str(data)
print
print "You: "
else:
print "Client closed connection"
s.close()
break
#Send Data
def send_data():
while True:
user_input = raw_input("You: ")
print
s.sendall(user_input)
#Start Threads \ Main Loop
t = threading.Thread(target=send_data)
t.daemon = True
t.start() #1
check_for_data()
s.close
Server:
import socket, threading
#Setup The Variables
WindowTitle = 'Chat 2.0 - Client'
host = socket.gethostname()
port = 8008
s = socket.socket()
s.bind((host, port))
print "Awaiting Connection..."
print
s.listen(5)
c, addr = s.accept()
print "Connection From: " + str(addr)
print
def check_for_data(c):
while True:
data = c.recv(1024)
if data:
print
print "Other: " + str(data)
print
print "You: "
else:
print "Client closed connection"
c.close()
break
def send_data():
while True:
message = raw_input("You: ")
print
c.sendall(message)
#Start Threads \ Main Loop
t = threading.Thread(target=send_data)
t.daemon = True
t.start() #1
check_for_data(c)
c.close()
Have to agree that tkinter is probably the better way to go here. For a chat program, pygame's sprites/rects/surfaces all have little use. However, tkinter has buttons and labels and other similar things built in that would suit your needs a bit better. Btw, to make your life with socket easier, look into the makefile method. The makefile method allows for much easier use. I recommend looking at Socket.error [Error 10060] for a description of how to use it and its uses. It's not necessary, just makes life easier :)
Edit:
"Should I layer the visuals over the existing program or should i make a new program around the visuals"
Not quite sure what you mean here by "existing program." When it comes to what you should code, split up your logic and user interface stuff. So have a module that handles the sending and receiving of messages, and another that handles the display.
"What python module should i use for the visuals (i know pygame is that suitable)"
Probably tkinter. I only have experience in tkinter and pygame, but of the two, you probably want tkinter for this. I explained why in the first paragraph.
"Can i have some form of general idea on how to write this (Code examples maybe?)"
Assuming you use tkinter, look into stringvars, they may or may not be useful for you. As for the structure of your program, I'm not exactly sure what you want so I can't help you there. But do start simple. For example, get messages to send between programs, and print them out. Then have the messages show up on a window.. Make a way for the user to type in message via GUI (look into tkinter entry widget!). Best of luck you!
Edit 2: To answer your question about how to position button. Here is some code from my last project where I had to use a button to do something.
button_frame = tkinter.Frame(master = self.option_window)
button_frame.grid(row = 5, column = 1)
ok_button = tkinter.Button(master = button_frame, text = 'OK',
font = DEFAULT_FONT, command = self.on_ok_clicked)
The position of the button is based off of where I did the button_frame.grid(....). To organize your tkinter window, I recommend using grid and not pack.
Oh, and self.option_window was a tkinter.Tk() object in my case.

Python: socket.recv() doesn't receive push messages

Python: socket.recv() doesn't receive push messages
Hello,
I'm coding a socket based IMAP client in Python3 which successfully establishes a connection to the server, succussfully transmits the IDLE command but then fails to receive incoming data from the server.
If you are wondering why I do not use libimap or sth., the answer is easy: I just want to implement an IDLE command-supporting python client which must be written without that library.
An extract:
import socket
def runIMAPPeek():
#socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(29 * 60)
#connection
s.connect((IMAP_SERVER , 443))
#login
data = b"a1 LOGIN " + USER + b" " + PASSWORD + b"\n"
s.sendall(data)
reply = read(s)
#Idle loop
#As in RFC 3501 connection will be reinitialized every 29 minutes
while True:
# Idle command
print("#Sending IDLE...")
data = b"a2 IDLE\n"
s.sendall(data)
reply = read(s)
if reply.startswith("+ idling"):
print(" #idling.")
else:
print(" #Unexpected answer: {}".format(reply))
#sys.exit()
# waiting for incoming mails ----------------------------------
try:
push_msg = read(s)
# got push message = new message arrived
getNewEnvelope(s, push_msg)
except socket.timeout:
# timeout
print(" #timeout. Reinitializing IDLE...")
#TODO: except (KeyboardInterrupt, SystemExit)
# Quit Idle
data = b"DONE\n"
write(s, data)
reply = read(s)
if reply.startswith(prefix_str + " OK"):
print(" #quit idling.")
else:
print(" #Unexpected answer: {}".format(reply))
#sys.exit()
def read(s):
"""Read socket data, print it, convert to string, replace new lines
and return it.
"""
print("#Receiving...", end=" ")
reply = s.recv(4096)
reply = str(reply)[2:-1] #convert and remove byte indicators
reply = reply.replace("\\r\\n", "\n")
print(reply)
return reply
The problem is marked with the "----". Although messages are received in the mailbox, python does not react but remains in the idling/receiving state. In fact, the print line above the s.recv() command isn't even printed.
I tried everything successfully with Telnet, so there is no server problem.
In addition to my comment above, you have never selected INBOX. You will not receive any push messages, because you haven't told it what folder you want. Technically, IDLE is not valid in the unselected state.
Constructs like this one:
if reply.startswith("+ idling"):
are completely non-compliant. The IDLE RFC specifies that the client shall expect a continuation request, not this particular string (which also happens to be a continuation request).

Categories