troubleshooting2024-03-01·8·322/348

Python websocket-client 연결 에러

Diagnosing and fixing Python websocket-client connection errors — handshake failures, SSL issues, timeout handling, and reconnection strategies.

Python websocket-client 연결 에러

Introduction

WebSockets provide persistent, bidirectional communication between clients and servers. They are essential for real-time applications like chat systems, live dashboards, and trading platforms. However, websocket connections are more fragile than HTTP connections, and the error messages are often less informative.

I built a real-time cryptocurrency price monitor using websockets in Lisbon. The connection would drop frequently due to network issues, and the default websocket-client behavior was to crash instead of reconnecting gracefully.

Environment

Python 3.12.3
websocket-client 1.7.1

Problem

Error 1: Handshake failure

import websocket

ws = websocket.create_connection("ws://localhost:8765")
websocket.WebSocketBadStatusException: Handshake status 403 Forbidden

Error 2: Connection refused

import websocket

ws = websocket.create_connection("ws://localhost:8765")
ConnectionRefusedError: [Errno 111] Connection refused

Error 3: SSL certificate error

import websocket

ws = websocket.create_connection("wss://api.example.com/ws")
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] 
certificate verify failed: self-signed certificate (_ssl.c:1006)

Error 4: Connection drops silently

import websocket
import time

def on_message(ws, message):
    print(f"Received: {message}")

ws = websocket.WebSocketApp(
    "wss://api.example.com/ws",
    on_message=on_message
)

ws.run_forever()
# Connection drops after some time — no automatic reconnection

Analysis

WebSocket errors fall into several categories.

Handshake errors (4xx/5xx): The initial HTTP upgrade request failed. This can be due to wrong URL, missing headers, authentication failure, or server-side rejection.

Connection errors: The TCP connection itself failed. Server is not running, wrong port, or firewall blocking.

SSL errors: Certificate verification failed. Common with self-signed certificates or corporate proxies.

Timeout errors: The server did not respond to the handshake within the timeout period.

Disconnection errors: The established connection was closed by the server or network.

Check connection status:

import websocket

ws = websocket.create_connection("wss://api.example.com/ws")
print(f"Connected: {ws.connected}")
print(f"Socket: {ws.sock}")

Solution

Fix 1: Handle handshake with proper headers

import websocket

headers = {
    "Authorization": "Bearer your-token",
    "User-Agent": "MyApp/1.0",
}

ws = websocket.create_connection(
    "wss://api.example.com/ws",
    header=headers,
    timeout=10
)

Fix 2: Disable SSL verification for development

import websocket
import ssl

# Development only — never use in production
ws = websocket.create_connection(
    "wss://localhost:8765",
    sslopt={"cert_reqs": ssl.CERT_NONE}
)

# Production — provide CA bundle
ws = websocket.create_connection(
    "wss://api.example.com/ws",
    sslopt={"ca_certs": "/path/to/ca-bundle.crt"}
)

Fix 3: Auto-reconnection with WebSocketApp

import websocket
import time
import threading

class ReconnectingWebSocket:
    def __init__(self, url, on_message=None, on_error=None):
        self.url = url
        self.on_message = on_message or self._default_on_message
        self.on_error = on_error or self._default_on_error
        self.ws = None
        self.should_reconnect = True
        self.reconnect_delay = 1
        self.max_delay = 60
    
    def _default_on_message(self, ws, message):
        print(f"Received: {message}")
    
    def _default_on_error(self, ws, error):
        print(f"Error: {error}")
    
    def _on_close(self, ws, close_status_code, close_msg):
        print(f"Connection closed: {close_status_code} {close_msg}")
        if self.should_reconnect:
            self._reconnect()
    
    def _on_open(self, ws):
        print("Connection opened")
        self.reconnect_delay = 1  # Reset delay on successful connection
    
    def _reconnect(self):
        if not self.should_reconnect:
            return
        
        print(f"Reconnecting in {self.reconnect_delay}s...")
        time.sleep(self.reconnect_delay)
        
        # Exponential backoff
        self.reconnect_delay = min(self.reconnect_delay * 2, self.max_delay)
        
        self.connect()
    
    def connect(self):
        self.ws = websocket.WebSocketApp(
            self.url,
            on_message=self.on_message,
            on_error=self.on_error,
            on_close=self._on_close,
            on_open=self._on_open
        )
        self.ws.run_forever()
    
    def close(self):
        self.should_reconnect = False
        if self.ws:
            self.ws.close()

# Usage
client = ReconnectingWebSocket("wss://api.example.com/ws")
client.connect()  # Blocks and reconnects on failure

Fix 4: Threaded WebSocket with ping/pong

import websocket
import threading
import time

class ThreadedWebSocket:
    def __init__(self, url):
        self.url = url
        self.ws = None
        self.thread = None
        self.running = False
    
    def start(self):
        self.running = True
        self.thread = threading.Thread(target=self._run, daemon=True)
        self.thread.start()
    
    def _run(self):
        while self.running:
            try:
                self.ws = websocket.WebSocketApp(
                    self.url,
                    on_message=self._on_message,
                    on_error=self._on_error,
                    on_close=self._on_close
                )
                self.ws.run_forever(
                    ping_interval=30,  # Send ping every 30s
                    ping_timeout=10    # Wait 10s for pong
                )
            except Exception as e:
                print(f"Connection error: {e}")
            
            if self.running:
                time.sleep(5)  # Wait before reconnecting
    
    def _on_message(self, ws, message):
        print(f"Received: {message}")
    
    def _on_error(self, ws, error):
        print(f"Error: {error}")
    
    def _on_close(self, ws, status_code, msg):
        print(f"Closed: {status_code}")
    
    def stop(self):
        self.running = False
        if self.ws:
            self.ws.close()

# Usage
client = ThreadedWebSocket("wss://api.example.com/ws")
client.start()
# ... do other work ...
client.stop()

Fix 5: Ping/pong keep-alive

import websocket
import time

def on_open(ws):
    def keep_alive():
        while ws.keep_running:
            ws.send("ping")
            time.sleep(30)
    
    thread = threading.Thread(target=keep_alive, daemon=True)
    thread.start()

ws = websocket.WebSocketApp(
    "wss://api.example.com/ws",
    on_open=on_open,
    on_message=lambda ws, msg: print(f"Received: {msg}")
)
ws.run_forever()

Lessons Learned

  • Always implement reconnection logic — WebSocket connections are inherently less stable than HTTP.
  • Use exponential backoff for reconnection attempts to avoid overwhelming the server.
  • Enable ping/pong keep-alive to detect dead connections early.
  • Run WebSocket operations in a separate thread if your application needs to do other work.
  • Handle SSL properly — never disable certificate verification in production.

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.