DevXStudio
Back to blog
Networking Jun 15, 2026 10 min read

Building a Real-Time Chat App from Scratch with Python Socket Programming

No libraries, no frameworks — just raw TCP sockets. How I built ShadowLink, a desktop chat app with messaging, file sharing, and video calls.

PythonSocket ProgrammingNetworkingOpenCVPyAudio
Networking

Why Raw Sockets?

Most developers use libraries like Socket.IO or SignalR for real-time communication. That's perfectly reasonable for production. But if you want to truly understand how real-time communication works, there's no substitute for building it from scratch with raw TCP sockets.

ShadowLink is my exploration of that idea.

The Client-Server Architecture

Client A ──────┐
Client B ──────┼──── Server (Hub) ──── Routes messages
Client C ──────┘

The server acts as a central hub. Every client connects to the server, and the server is responsible for routing messages between clients.

python
import socket
import threading

class ChatServer:
    def __init__(self, host='0.0.0.0', port=9090):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((host, port))
        self.clients = {}  # {socket: username}
    
    def handle_client(self, conn, addr):
        username = conn.recv(1024).decode()
        self.clients[conn] = username
        
        while True:
            try:
                data = conn.recv(4096)
                if not data:
                    break
                self.broadcast(data, conn)
            except:
                break
        
        del self.clients[conn]
        conn.close()

The Custom Protocol

One of the most interesting parts of this project was designing the message protocol. Every message needs a type header so the client knows how to handle it:

[TYPE:4][LENGTH:8][PAYLOAD:n]

Types:
- TEXT: Plain text message
- FILE: File transfer
- AUDIO: Audio stream chunk
- VIDEO: Video frame
- PRIVATE: Private message with target

Voice Calls with PyAudio

Audio streaming is surprisingly straightforward with PyAudio. You capture audio in chunks and send each chunk over the socket:

python
import pyaudio

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100

def stream_audio(sock):
    audio = pyaudio.PyAudio()
    stream = audio.open(format=FORMAT, channels=CHANNELS,
                       rate=RATE, input=True, frames_per_buffer=CHUNK)
    while True:
        data = stream.read(CHUNK)
        sock.send(b'AUDIO' + data)

Video Calls with OpenCV

Video is similar — capture frames with OpenCV, compress them as JPEG, and send over the socket:

python
import cv2
import numpy as np

def stream_video(sock):
    cap = cv2.VideoCapture(0)
    while True:
        ret, frame = cap.read()
        _, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 50])
        data = buffer.tobytes()
        sock.send(len(data).to_bytes(4, 'big') + data)

What I Learned

  1. TCP is reliable but not instant. You need to handle buffering carefully — received data might come in chunks that don't align with your message boundaries.
  2. Threading is essential. Every client needs its own thread on the server; blocking I/O will freeze everything.
  3. Latency is everything for AV. Video and audio need to be treated differently from text — dropped frames are acceptable, but dropped audio chunks cause awful artifacts.

Watch the full demo to see all features in action.