Combining PYQT GUI
ChatServer
# -*- coding: utf-8 -*-
import sys
import socket
import threading
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTextEdit, QPushButton, QLabel
class ChatServer(QWidget):
def __init__(self):
super(ChatServer, self).__init__()
self.initUI()
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('0.0.0.0', 8080))
self.server.listen(300)
self.clients = []
self.running = False
def initUI(self):
self.log_window = QTextEdit(self)
self.log_window.setReadOnly(True)
self.status_label = QLabel('Server is stopped', self)
self.start_button = QPushButton('Start Server', self)
self.start_button.clicked.connect(self.toggle_server)
layout = QVBoxLayout()
layout.addWidget(self.status_label)
layout.addWidget(self.log_window)
layout.addWidget(self.start_button)
self.setLayout(layout)
def closeEvent(self, event):
self.running = False
if self.server:
self.server.close()
event.accept()
def toggle_server(self):
if self.running:
# 서버 종료
self.running = False
self.server.close() # 이 부분에서 서버 종료 처리를 하면 좋습니다.
self.start_button.setText("Start Server")
self.log_window.append('Server stopped!')
self.status_label.setText('Server is stopped.')
else:
# 서버 시작
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('0.0.0.0', 8080))
self.server.listen(300)
self.running = True
self.thread = threading.Thread(target=self.accept_connections)
self.thread.daemon = True # 주 스레드가 종료될 때 함께 종료됩니다.
self.thread.start()
self.start_button.setText("Stop Server")
self.log_window.append('Server started!')
self.status_label.setText('Server is running...')
def accept_connections(self):
while self.running:
try:
client_socket, addr = self.server.accept()
self.clients.append(client_socket)
self.log_window.append('Accepted connection from {}'.format(addr))
thread = threading.Thread(target=self.handle_client, args=(client_socket,))
thread.start()
except:
pass
def handle_client(self, client_socket):
while self.running: # self.running을 추가하여 서버가 실행 중일 때만 클라이언트를 처리
try:
msg = client_socket.recv(1024).decode('utf-8')
if not msg: # 클라이언트가 연결을 끊었거나 메시지를 보내지 않은 경우
break
self.broadcast(msg, client_socket)
self.log_window.append('Received message: {}'.format(msg))
except:
self.clients.remove(client_socket)
client_socket.close()
break # 연결이 끊어진 경우 루프를 종료
def broadcast(self, msg, sending_client):
for client in self.clients:
if client != sending_client:
try:
client.send(msg.encode('utf-8'))
except:
client.close()
self.clients.remove(client)
app = QApplication(sys.argv)
server_window = ChatServer()
server_window.show()
sys.exit(app.exec_())
- Overview:
The
ChatServerclass is a simple chat server application. It provides a graphical user interface using PyQt and can communicate with multiple clients at the same time.- Server Initialization:
Socket Creation: It uses a TCP socket bound to the address ‘0.0.0.0’ and port 8080. This address sets up all available network interfaces to be in a listening state.
Concurrent Connections: It’s set to handle up to 300 simultaneous connections.
Client List: The
self.clientslist tracks the sockets of all currently connected clients.
- UI Components:
Log Window: Displays the server’s activities and client messages.
Status Label: Shows the server’s current status.
Server Start Button: It doesn’t actually start the server but is included for demonstration purposes.
- Multithreading:
The server uses the
threadingmodule to accept connections from clients and handle each client in separate threads. This ensures the server can handle multiple tasks at the same time and keeps the UI responsive.
- Message Broadcasting:
The
broadcastfunction sends messages to all connected clients.The message sender is excluded from the recipients’ list.
- Client Handling:
Connection Acceptance: The
accept_connectionsfunction waits for new client connections. Upon establishment, it prints the client’s information in the log window and starts a separate thread to handle the client.Communication with Clients: The
handle_clientfunction manages communication with connected clients. It receives messages from the client, logs them, and then sends the message to all other clients.
- Error Handling:
Exception Handling: Most network operations can raise exceptions. Errors occurring during communication with clients are caught and handled, and the connection to the problematic client is terminated.
- Server Shutdown Procedures:
Server Shutdown: Within the
toggle_serverfunction, if the server is already running, it closes the server socket, thus shutting down the server.Safe Termination: The
closeEventfunction is defined to safely terminate server connections when the window is closed.
ChatClient
# -*- coding: utf-8 -*-
import sys
import socket
import threading
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTextEdit, QPushButton, QLabel, QLineEdit, QMessageBox
from PyQt5.QtCore import pyqtSignal
class ChatClient(QWidget):
received_msg_signal = pyqtSignal(str)
def __init__(self):
super(ChatClient, self).__init__()
self.initUI()
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client.connect(('127.0.0.1', 8080))
self.client.settimeout(1)
self.running = True
self.thread = threading.Thread(target=self.receive_message)
self.thread.daemon = True
self.thread.start()
def initUI(self):
self.setWindowTitle('Chat Client')
layout = QVBoxLayout()
self.output_window = QTextEdit(self)
self.output_window.setReadOnly(True)
layout.addWidget(self.output_window)
self.input_line = QLineEdit(self)
self.input_line.returnPressed.connect(self.send_message)
layout.addWidget(self.input_line)
self.send_button = QPushButton('Send', self)
self.send_button.clicked.connect(self.send_message)
layout.addWidget(self.send_button)
# Connect the signal to slot
self.received_msg_signal.connect(self.display_received_message)
self.setLayout(layout)
def send_message(self):
msg = self.input_line.text()
self.client.send(msg.encode('utf-8'))
self.output_window.append("You: {}".format(msg))
self.input_line.clear()
def receive_message(self):
while self.running:
try:
msg = self.client.recv(1024).decode('utf-8')
if not msg: # 소켓이 종료되면 빈 문자열을 반환합니다.
break
self.received_msg_signal.emit(msg)
except socket.timeout: # 타임아웃 예외를 처리합니다.
continue
except:
self.received_msg_signal.emit("An error occurred!")
self.client.close()
break
def display_received_message(self, msg):
self.output_window.append("Received: {}".format(msg))
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Confirm Exit', 'Are you sure you want to exit the client?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
self.running = False
self.client.close()
self.thread.join(2) # Wait for the receive_message thread to finish
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
chat_window = ChatClient()
chat_window.show()
sys.exit(app.exec_())
- Overview:
The
ChatClientclass is a simple chat client application that communicates with the server.- Client Initialization:
Socket Creation and Connection: It creates a TCP socket with intentions to connect to the server IP address ‘127.0.0.1’ and port 8080.
Socket Timeout: It has a 1-second timeout, ensuring the main thread doesn’t block while waiting for data.
- UI Components:
Output Window: Shows the chat history with the server.
Input Line: A space where users can type their messages.
Send Button: Sends the typed message to the server.
- Threading:
It runs the
receive_messagefunction in a separate thread. This ensures continuous message reception from the server and keeps the main thread responsive.
- Signal & Slot:
Uses the PyQt signal-slot mechanism to update the UI safely in the main thread when a message is received. It’s a way of safely transmitting data between threads.
- Message Reception:
When a message is received, it emits the
received_msg_signalto update the UI in the main thread.
- Exception Handling:
It has various exception-handling codes to handle situations that might arise during network operations.