I have created a chat system using sockets , select and tkinter , however if the client restarts , it would fail to connect again until i restart the server.
This is the client code:
from tkinter import *
import tkinter.scrolledtext as tkst
import socket
import threading
import time
class myChatProgram(threading.Thread):
def __init__(self):
self.root = Tk()
self.myFrame = Frame(master = self.root)
self.myFrame.pack(fill='both', expand='yes')
self.chatArea = tkst.ScrolledText(
master = self.myFrame,
wrap = WORD,
width = 50,
height = 20
)
self.chatArea.configure(state='disabled')
self.chatArea.pack(padx=10, pady=10, fill=BOTH, expand=True)
self.bottomFrame = Frame(master = self.root)
self.bottomFrame.pack(fill='x', expand='yes' , side = "bottom")
self.entryBox = Entry(self.bottomFrame)
self.entryBox.pack(padx=10, pady=10,fill='x', expand='yes' , side = "left")
self.entryButton = Button(self.bottomFrame, text = "Send", command = self.sendText ,padx=10)
self.entryButton.pack(side = "right")
self.host = '192.168.1.150'
self.port = 50003
threading.Thread.__init__(self)
self.start()
self.root.mainloop()
def addText(self,message):
if message != "":
self.chatArea.configure(state='normal')
self.chatArea.insert(END, str(message) + "\n")
self.chatArea.configure(state='disabled')
def sendText(self):
message = bytes(self.entryBox.get(),"utf-8")
self.addText(self.entryBox.get())
if message:
self.mySocket.send(message)
self.entryBox.delete(0, END)
def run(self):
self.mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.mySocket.connect((self.host,self.port))
time.sleep(1)
while True:
self.addText(str(self.mySocket.recv(4096).decode("utf-8")))
myClass = myChatProgram()
And the server code is:
import socket
import select
import threading
class server(threading.Thread):
def __init__(self):
host = '192.168.1.150'
port = 50003
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((host,port))
print("Your ip is {0} ".format(socket.gethostbyname(socket.gethostname())))
self.server.listen(5)
self.sockets = []
self.sockets.append(self.server)
def main(self):
while True:
inputList , outputList , errorList = select.select(self.sockets, [], self.sockets)
for mySocket in inputList:
if mySocket == self.server:
newClient , addr = self.server.accept()
print(addr , " Connected")
self.sockets.append(newClient)
else:
try:
data = mySocket.recv(4096)
if data:
self.broadcast(mySocket,data)
else:
print("Connection sent 0 bytes.")
mySocket.close()
self.sockets.remove(mySocket)
except socket.error , e:
print("Client disconnected while sending message.")
mySocket.close()
self.sockets.remove(mySocket)
continue
for mySocket in errorList:
print("Select flagged client as crashed.")
mySocket.close()
self.sockets.remove(mySocket)
continue
def broadcast(self,mySocket,message):
print("sending {0}".format(message))
for client in self.sockets:
if client != mySocket and socket != self.server:
try:
client.send(bytes(message,"utf-8"))
except:
client.close()
self.sockets.remove(client)
continue
myClass = server()
myClass.main()
The the error is :
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Python33\lib\threading.py", line 901, in _bootstrap_inner
self.run()
File "F:\Sockets\chatClient.py", line 49, in run
self.mySocket.connect((self.host,self.port))
ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it
However it works fine unless the client restarts , then it crashes. Any ideas what is wrong?
I guess you have to set SO_REUSEADDR:
socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Related
I developed a simple chat app that allows users to connect to a server and chat. This is the code for the server.
import os
from datetime import datetime
def log_file_name():
n = str(datetime.now())
m = n.replace(':','_').replace(' ','---')
l = m.split('.')[0]
l = f'{l}.txt'
return l
HOST = '127.0.0.1'
PORT = 10001
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#print(type(server_socket))
server.bind((HOST, PORT))
server.listen()
print('server_initiated...')
logfile = os.path.join('logs', log_file_name())
with open(logfile, 'w') as fh:
fh.write(f'{str(datetime.now())} log file initiated!\n\n')
clients = []
nicknames = []
#broadcast
def broadcast(message):
#print(clients)
for client in clients:
client.send(message)
def handle(client):
while True:
try:
message = client.recv(1024)
#print(f'{nicknames[clients.index(client)]} says {message}')
broadcast(message)
with open(logfile, 'a') as fh:
fh.write(f"{nicknames[clients.index(client)]} says {message.decode('utf-8')}\n")
except Exception:
index = clients.index(client)
clients.remove(client)
client.close()
left_nick = nicknames[index]
leaving_notes = f"{left_nick} just left the chat!! -- time {str(datetime.now()).split(' ')[1].split('.')[0]}"
broadcast(leaving_notes.encode('utf-8'))
with open(logfile,'a') as fh:
fh.write(f'{leaving_notes}\n')
nicknames.remove(left_nick)
break
#recieve message
def recieve():
while True:
client, address = server.accept()
#print(dir(client))
#print(client._io_refs)
#print(f'Connected with {str(address)}!!')
with open(logfile, 'a') as fh:
fh.write(f'Connected with {str(address)}!!\n')
client.send('Nickname'.encode('utf-8'))
nickname = client.recv(512).decode('utf-8')
nicknames.append(nickname)
clients.append(client)
#print(f'from 1st step clients are {clients}')
#print(f'Nickname of the client is {nickname}')
with open(logfile, 'a') as fh:
fh.write(f'Nickname of the client is {nickname}\n')
broadcast(f'Notice from server!! {nickname} just connected to the server!\n'.encode('utf-8'))
client.send(f'Connected to the server as {nickname}\n'.encode('utf-8'))
thread = threading.Thread(target=handle, args=(client,))
thread.start()
recieve()
Then I exposed that port to public using ngrok (ngrok http 10001), and it generated a public ip for me.
I coded a simple client gui using tkinter and I tried to connect the socket in to to above ngrok server I got. But I cannot broadcast messages. When I try to broadcast a message the GUI automatically closes. The reason is it meets a ConnectionAbortError.
Code for client-
import threading
import tkinter
import socket
import tkinter.simpledialog
import tkinter.scrolledtext
import os
#HOST = socket.gethostbyname(socket.gethostname())
#PORT = 10001
HOST = '3a7d-2402-4000-1245-cd8a-5d64-9c3f-25a3-79a4.in.ngrok.io'
PORT = 443 #or 80
class Client:
def __init__(self,host,port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port))
msg = tkinter.Tk()
msg.withdraw()
self.nickname = tkinter.simpledialog.askstring('Nickname', 'Please enter a nickname', parent=msg)
self.gui_done = False
self.running = True
gui_t=threading.Thread(target=self.gui_loop)
recieve_t=threading.Thread(target=self.recieve)
gui_t.start()
recieve_t.start()
def gui_loop(self):
self.win = tkinter.Tk()
self.win.configure(bg='lightgray')
self.chat_label = tkinter.Label(self.win, text='Chat:', bg='lightgray')
self.chat_label.config(font=('Arial',12))
self.chat_label.pack(padx=20, pady=5)
self.text_area = tkinter.scrolledtext.ScrolledText(self.win)
self.text_area.pack(padx=20,pady=5)
#self.text_area.config(state='disabled')
self.msg_label = tkinter.Label(self.win, text='Message:', bg='lightgray')
self.msg_label.config(font=('Arial', 12))
self.msg_label.pack(padx=20, pady=5)
self.input_area = tkinter.Text(self.win, height=3)
self.input_area.pack(padx=20, pady=5)
self.send_button = tkinter.Button(self.win, text='Send', command=self.write)
self.send_button.config(font=('Arial', 12))
self.send_button.pack(padx=20, pady=5)
self.gui_done = True
self.win.protocol('WM_DELETE_WINDOW', self.stop)
self.win.mainloop()
def write(self):
message = f"{self.nickname}: {self.input_area.get('1.0', 'end')}"
#print(message)
self.sock.send(message.encode('utf-8'))
self.input_area.delete('1.0','end')
def recieve(self):
while self.running:
try:
message = self.sock.recv(1024).decode('utf-8')
print(message)
if message == 'Nickname':
self.sock.send(self.nickname.encode('utf-8'))
else:
if self.gui_done:
#print('I hit here often')
self.text_area.config(state='normal')
self.text_area.insert('end', message)
self.text_area.yview('end')
#self.text_area.config(status='disabled') #never use this inside a loop, disabled blocks the code....
except ConnectionAbortedError: #when closed the window I come here..
print('Am i here?')
#break
os._exit(0)
#break
except:
break
print('Error')
self.sock.close()
break
def stop(self):
self.running = False
self.win.destroy()
self.sock.close()
exit(0)
client = Client(HOST, PORT)
If you run this please make sure to create a directory called logs to save the log of communication. Or else it will give an error(You can try it on localhost). I think I am doing something that doesn't work here. Can you point it out to me? or can you give me an alternative way of doing it?
from tkinter import *
import _thread
from typing import TextIO
#页面代码
root = Tk()
root.title('TPC服务端')
root.geometry('640x480')
label_1 = Label(root,relief=GROOVE,text='从客户端接收的数据')
label_1.place(relx=0.1,rely=0.0,relwidth=0.4,relheight=0.1)
Operate = Text(root,relief=GROOVE)
Operate.place(relx=0.1,rely=0.1,relwidth=0.4,relheight=0.4)
Tips = Text(root,relief=GROOVE)
Tips.place(relx=0.1,rely=0.5,relwidth=0.4,relheight=0.4)
Tips.insert(END,'当前状态:\n')
Tips.configure(state=DISABLED)
root.mainloop()
class TCPServer():
def __init__(self):
self.HOST = '192.0.0.1'
self.PORT = 8080
self.BUFSIZ = 1024
self.ADDRESS = (self.HOST,self.PORT)
self.tcpServerSocket = socket(AF_INET, SOCK_STREAM)
self.tcpServerSocket.bind(self.ADDRESS) #IP地址和固定端口信息
self.tcpServerSocket.listen(5)
def try_connect(self):
global var
var='服务器正在运行,等待客户端连接...\n'
Tips.insert(END,var)
Tips.configure(state=DISABLED)
while True:
var='服务器正在运行,等待客户端连接...\n'
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
self.client_socket, self.client_address = self.tcpServerSocket.accept()
var='客户端{}已连接!\n'.format(self.client_address)
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
while True:
self.data = self.client_socket.recv(self.BUFSIZ)
if(self.data):
var='接收到消息 {}({} bytes) 来自 {}\n'.format(self.data.decode('utf-8'), len(self.data), self.client_address)
# 返回响应数据,接受的数据不做处理即返回
self.client_socket.send(self.data)
var='发送消息 {} 至 {}\n'.format(self.data.decode('utf-8'), self.client_address)
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
Operate.configure(state=NORMAL)
Operate.insert(END,self.data)
Operate.configure(state=DISABLED)
else:
var='客户端 {} 已断开!\n'.format(self.client_address)
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
break
# 关闭socket
self.client_socket.close()
# 取消监听socket,自此拒绝响应其它客户端
self.tcpServerSocket.close()
Server = TCPServer()
_thread.start_new_thread(Server.try_connect)
root.mainloop()
![text](enter image description here
I want to know why such a mistake happened.I am using python to make a simple network communication program.It contains both the client and server sides.This is my client program code.
I am a beginner.
The server connects to the client .i use vscode to write the code.I use python 3.10
I Was working a tcp chat room and when i run it it shows nicknames and who joined but the message doesnt go through and after trying to send a message the 4th time it show the error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\f\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "besthost.py", line 59, in write
self.sock.send(message.encode('utf-8'))
ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine
The host file is
import socket
import threading
import tkinter
import tkinter.scrolledtext
from tkinter import simpledialog
HOST = '127.0.0.1'
PORT = 9091
class Client:
def __init__(self, host, port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port))
msg = tkinter.Tk()
msg.withdraw()
self.nickname = simpledialog.askstring("nickname", "please choose a nickname", parent=msg)
self.gui_done = False
self.running = True
gui_thread = threading.Thread(target=self.gui_loop)
receive_thread = threading.Thread(target=self.receive)
gui_thread.start()
receive_thread.start()
def gui_loop(self):
self.win = tkinter.Tk()
self.win.configure(bg="darkolivegreen")
self.chat_label = tkinter.Label(self.win, text="chat", bg="lightgray")
self.chat_label.config(font=("arial", 12))
self.chat_label.pack(padx=20, pady=5)
self.text_area = tkinter.scrolledtext.ScrolledText(self.win)
self.text_area.pack(padx=20, pady=5)
self.text_area.config(state='disabled')
self.msg_label = tkinter.Label(self.win, text="message", bg="lightgray")
self.msg_label.config(font=("arial", 12))
self.msg_label.pack(padx=20, pady=5)
self.input_area = tkinter.Text(self.win, height =3)
self.input_area.pack(padx=20,pady=5)
self.send_button = tkinter.Button(self.win, text="send", command=self.write)
self.send_button.config(font=("arial", 12))
self.send_button.pack(padx=20, pady=5)
self.gui_done = True
self.win.protocol("WM_DELETE_WINDOW", self.stop)
self.win.mainloop()
def write(self):
message = f"{self.nickname}:{self.input_area.get('1.0', 'end')}"
self.sock.send(message.encode('utf-8'))
self.input_area.delete('1.0', 'end')
def stop(self):
self.running = False
self.win.destroy()
self.sock.close()
exit(0)
def receive(self):
while self.running:
try:
message = self.sock.recv(1024).decode('utf-8')
if message=='NICK':
self.sock.send(self.nickname.encode('utf-8'))
else:
if self.gui_done:
self.text_area.config(state='normal')
self.text_area.insert('end', message)
self.text_area.yview('end')
self.text_area.config(state='disabled')
except ConnectionAbortedError:
break
except:
print("error")
self.sock.close()
break
client = Client(HOST, PORT)
The server file is
import socket
import threading
HOST = '127.0.0.1'
PORT = 9091
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen()
clients = []
nicknames = []
def broadcast(message):
for client in clients:
client.send(message)
def handle(client):
while True:
try:
message = client.recv(1024).decode('utf-8')
print(f"{nicknames[client.index(client)]})")
broadcast(message)
except:
index = clients.index(client)
clients.remove(client)
client.close()
nickname = nicknames[index]
nicknames.remove(nickname)
break
def receive():
while True:
client, address = server.accept()
print(f"connected with {str(address)}!")
client.send("NICK".encode('utf-8'))
nickname = client.recv(1024)
nicknames.append(nickname)
clients.append(client)
print(f"nicknames of client is {nickname}")
broadcast(f"{nickname} connected successfully!\n".encode('utf-8'))
client.send("connected to the server".encode('utf-8'))
thread = threading.Thread(target=handle, args=(client,))
thread.start()
print("server running")
receive()
I tried searching but i didnt see anyone with this issue and i dont think there are any programming errors
If anyone has an idea whats wrong please let me know .
Thanks
Your the biggest mistake is except: without printing information about problem.
I use
except Exception as ex:
print (ex)
# ... rest ...
and first it shows me
'socket' object has no attribute 'index'
because you forgot s in word clients in line
print(f"{nicknames[clients.index(client)]})")
Next it shows me
a bytes-like object is required, not 'str'
because you send str instead of bytes in next line - and you need encode()
broadcast(message.encode('utf-8'))
After these changes it start working for me.
def handle(client):
while True:
try:
message = client.recv(1024).decode('utf-8') # convert bytes to string
print(f"{nicknames[clients.index(client)]})") # forgot `s`
broadcast(message.encode('utf-8')) # convert string back to bytes
except Exception as ex:
print('Exception:', ex)
index = clients.index(client)
clients.remove(client)
client.close()
nickname = nicknames[index]
nicknames.remove(nickname)
break
I wanted to write a server which will be able to handle multiple clients sing select and threads.
I know how to write a server only with threads, or only with select. Now, I wanted to mix them together. But it seems I failed.
Server:
import threading
import socket
import select
class Client(threading.Thread):
'''
Multi-threading clients.
'''
def __init__(self):
threading.Thread.__init__(self)
pass
def setup(self, __client, __address):
self.client = __client
self.address = __address
def connect(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def run(self):
print self.client.__class__
data = self.client.recv(1024)
if data:
self.client.sendall(data)
else:
print "Error:", self.address
class Server(object):
'''
Threading server
'''
def __init__(self):
'''
Constructor
'''
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('127.0.0.1', 9876))
def run(self):
self.server.listen(10)
inputs = [self.server]
while 1:
inready, outready, excready = select.select(inputs, [], []);
for s in inready:
if s == self.server:
sock, address = self.server.accept();
client = Client()
client.setup(sock, address)
client.start()
self.server.close()
if __name__ == '__main__':
server = Server()
server.run()
Client:
import socket
import sys
port = 9876
size = 1024
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
s.connect(('127.0.0.1', port))
except socket.error, (value, message):
if s:
s.close()
print "Could not open socket: " + message
sys.exit(1)
while True:
data = raw_input('> ')
s.sendall(data)
data = s.recv(size)
print "Server sent: %s " % data
s.close()
Client sends and receives only one message and then Im not able to send any more messages. Why? How to improve this? Thank you.
Edit: One error found. There was a loop missing in Client's run method:
import threading import socket import select
class Client(threading.Thread):
'''
Multi-threading clients.
'''
def __init__(self):
threading.Thread.__init__(self)
pass
def setup(self, __client, __address):
self.client = __client
self.address = __address
def connect(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def run(self):
while True:
print self.client.__class__
data = self.client.recv(1024)
if data:
self.client.sendall(data)
else:
print "Error:", self.address
self.client.close()
class Server(object):
'''
Threading server
'''
def __init__(self):
'''
Constructor
'''
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('127.0.0.1', 9876))
def run(self):
self.server.listen(10)
inputs = [self.server]
while 1:
inready, outready, excready = select.select(inputs, [], []);
for s in inready:
if s == self.server:
sock, address = self.server.accept();
client = Client()
client.setup(sock, address)
client.start()
self.server.close()
if __name__ == '__main__':
server = Server()
server.run()
But now, when client disconnects, I have an error on server's side. How to handle this error?
Exception in thread Thread-4:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "/home/testserver.py", line 25, in run
data = self.client.recv(1024)
File "/usr/lib/python2.7/socket.py", line 174, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: [Errno 9] Bad file descriptor
I am writing a multi-chat which consists of the Client handler, the server and chat record. The Client should allow multiple chats. At the moment it doesn't allow for even one chat, I get an error message after the name has been entered.
This is the Client handler file
from socket import*
from codecs import decode
from chatrecord import ChatRecord
from threading import Thread
from time import ctime
class ClientHandler (Thread):
def __init__(self, client, record):
Thread.__init__(self)
self._client = client
self._record = record
def run(self):
self._client.send(str('Welcome to the chatroom!'))
self._name = decode(self._client.recv(BUFSIZE),CODE)
self._client.send(str(self._record),CODE)
while True:
message = decode(self._client.recv(BUFSIZE),CODE)
if not message:
print('Client disconnected')
self._client.close()
break
else:
message=self._name +'' + \
ctime()+'\n'+message
self._record.add(message)
self._client.send(str(self._record),CODE)
HOST ='localhost'
PORT = 5000
ADDRESS = (HOST,PORT)
BUFSIZE = 1024
CODE = 'ascii'
record = ChatRecord()
server = socket(AF_INET,SOCK_STREAM)
server.bind(ADDRESS)
server.listen(5)
while True:
print ('Waiting for connection...')
client,address = server.accept()
print ('...connected from:',address)
handler = ClientHandler(client,record)
handler.start()
This is the server file
from socket import *
from codecs import decode
HOST ='localhost'
PORT = 5000
BUFSIZE = 1024
ADDRESS = (HOST,PORT)
CODE = 'ascii'
server = socket(AF_INET,SOCK_STREAM)
server.connect(ADDRESS)
print (decode(server.recv(BUFSIZE), CODE))
name = raw_input('Enter your name:')
server.send(name)
while True:
record = server.recv(BUFSIZE)
if not record:
print ('Server disconnected')
break
print (record)
message = raw_input('>')
if not message:
print ('Server disconnected')
break
server.send(message, CODE)
server.close()
This is the Chartrecord
class ChatRecord(object):
def __init__(self):
self.data=[]
def add(self,s):
self.data.append(s)
def __str__(self):
if len(self.data)==0:
return 'No messages yet!'
else:
return'\n'.join(self.data)
This is the error message I get when running the chat record. I can enter a name then after that I get the error message below
Waiting for connection...
('...connected from:', ('127.0.0.1', 51774))
Waiting for connection...
Exception in thread Thread-1:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threadin g.py", line 532, in __bootstrap_inner
self.run()
File "/Users/basetsanamanele/Documents/workspace/HAAAAAAAFF/ClientHandler", line 17, in run
self._client.send(str(self._record),CODE)
TypeError: an integer is required
Please assist
Edit: Also, your server isn't accepting/listing for connections
You should make the server multithreaded so that it can send and receive at the same time. Here's how it might look:
import socket
import os
from threading import Thread
import thread
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host,port))
s.listen(10)
serverThreads = []
while True:
print "Server is listening for connections..."
client, address = s.accept()
serverThreads.append(Thread(target=runserver, args=(client,address)).start())
s.close()
def runserver(client, address):
clients = set()
lockClients = threading.Lock()
print ("Connection from: " + address)
with lockClients:
clients.add(client)
try:
while True:
data = client.recv(1024)
if data:
print data.decode()
with lockClients:
for c in clients:
c.sendall(data)
else:
break
finally:
with lockClients:
clients.remove(client)
client.close()
When you send a string over TCP you need to encode it to bytes. So your client file should look like this instead:
def run(self):
self._client.send(('Welcome to the chatroom!').encode())
self._name = self._client.recv(BUFSIZE).decode()
self._client.send(str(self._record),CODE)
while True:
message = self._client.recv(BUFSIZE).decode()
if not message:
print('Client disconnected')
self._client.close()
break
else:
message=self._name +'' + \
ctime()+'\n'+message
self._record.add(message)
self._client.send(self._record.encode())
I much prefer the .encode() and .decode() syntax as it makes the code a little more readable IMO.