杰瑞科技汇

Python localsocket如何实现本地通信?

Of course! Let's dive into localsocket in Python. The term "local socket" most commonly refers to Unix Domain Sockets (UDS). It's a powerful method for Inter-Process Communication (IPC) on the same machine.

Python localsocket如何实现本地通信?-图1
(图片来源网络,侵删)

What is a Unix Domain Socket?

Think of it like a network socket, but instead of communicating over the network using an IP address and port, processes communicate through a special file on the filesystem (a "socket file").

Key Characteristics:

  • Local Only: It only works for processes on the same operating system.
  • File-based: The connection endpoint is a file (e.g., /tmp/my_socket.sock). You can see it with ls -l.
  • Fast: It's much faster than TCP/IP sockets because it doesn't have the overhead of the network stack (no packet creation, routing, etc.). It's essentially a kernel-level pipe.
  • Simple API: The Python API is almost identical to that of TCP sockets, making it easy to learn if you already know socket programming.

When to Use a Unix Domain Socket

  • High-performance IPC: When two processes on the same machine need to communicate with maximum speed.
  • Web Servers & Applications: A common pattern is a web server (like Nginx) receiving HTTP requests and forwarding them to a Python application via a UDS. This avoids the overhead of loopback TCP.
  • Microservices on a single host: When you have multiple services running on one machine that need to talk to each other.
  • Any local service: When you want a simple, fast communication channel between a script and a daemon.

Python Implementation: socket Module

Python's built-in socket module handles both network and Unix domain sockets. The key difference is the address family you use.

Feature TCP/IP Socket (Network) Unix Domain Socket (Local)
Address Family socket.AF_INET socket.AF_UNIX
Address Tuple ('ip_address', port) '/path/to/socket_file.sock'
Protocol socket.SOCK_STREAM (TCP) or socket.SOCK_DGRAM (UDP) socket.SOCK_STREAM (reliable) or socket.SOCK_DGRAM (unreliable)

Example: A Simple Echo Server and Client

Let's create a server that listens for a connection, receives a message, and sends it back. The client will connect, send a message, and print the response.

Python localsocket如何实现本地通信?-图2
(图片来源网络,侵删)

Step 1: The Server (server.py)

This server will create a socket file at /tmp/myapp.sock.

# server.py
import socket
import os
# Define the socket file path
SOCKET_FILE = "/tmp/myapp.sock"
# Remove the socket file if it already exists
try:
    os.unlink(SOCKET_FILE)
except OSError:
    if os.path.exists(SOCKET_FILE):
        raise
# Create a Unix domain socket
server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Bind the socket to the file
server_socket.bind(SOCKET_FILE)
# Listen for incoming connections (backlog of 1)
server_socket.listen(1)
print(f"Server listening on {SOCKET_FILE}")
try:
    # Accept a connection
    connection, client_address = server_socket.accept()
    print(f"Connection from {client_address}")
    try:
        # Receive data (up to 1024 bytes)
        data = connection.recv(1024)
        print(f"Received: {data.decode('utf-8')}")
        # Echo the data back to the client
        connection.sendall(data)
        print("Data echoed back")
    finally:
        # Clean up the connection
        connection.close()
        print("Connection closed")
finally:
    # Clean up the socket file
    server_socket.close()
    os.unlink(SOCKET_FILE)
    print("Server shut down and socket file removed.")

Step 2: The Client (client.py)

This client will connect to the server and send a message.

# client.py
import socket
import time
# Define the same socket file path
SOCKET_FILE = "/tmp/myapp.sock"
# Create a Unix domain socket
client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Connect to the server
# This will block until the server is ready and listening
print(f"Connecting to {SOCKET_FILE}...")
try:
    client_socket.connect(SOCKET_FILE)
    print("Connected.")
    # Send a message to the server
    message = b"Hello from the client!"
    print(f"Sending: {message.decode('utf-8')}")
    client_socket.sendall(message)
    # Receive the echoed response
    data = client_socket.recv(1024)
    print(f"Received: {data.decode('utf-8')}")
except FileNotFoundError:
    print(f"Error: The socket file '{SOCKET_FILE}' was not found.")
    print("Make sure the server is running and has created the socket file.")
except ConnectionRefusedError:
    print(f"Error: Connection refused. Is the server running?")
finally:
    print("Closing client.")
    client_socket.close()

Step 3: How to Run It

  1. Open two separate terminal windows.

  2. In the first terminal, run the server:

    python3 server.py

    You will see:

    Server listening on /tmp/myapp.sock
  3. In the second terminal, run the client:

    python3 client.py

    You will see the client's output:

    Connecting to /tmp/myapp.sock...
    Connected.
    Sending: Hello from the client!
    Received: Hello from the client!
    Closing client.
  4. Go back to the first terminal. You will see the server's output:

    Connection from /tmp/myapp.sock
    Received: Hello from the client!
    Data echoed back
    Connection closed
    Server shut down and socket file removed.

Handling Errors and Permissions

A common issue with Unix domain sockets is permission denied.

  • The Problem: By default, the created socket file (/tmp/myapp.sock) might only be writable by the user who created it. If you run the server as one user and the client as another, the client won't be able to connect.
  • The Solution: You can change the permissions of the socket file after creating it.

Modify the server.py code:

# ... after server_socket.bind(SOCKET_FILE) ...
# Set permissions to allow other users to connect (e.g., 666)
os.chmod(SOCKET_FILE, 0o666) 

Security Note: Be careful with 0o666 as it allows anyone to read and write to your socket. For more security, you might use 0o660 and ensure the client process is in the same group as the server process.


Advanced: Using socketserver for Concurrency

The socketserver module makes it easy to create robust, multi-threaded or multi-process servers. Here's how you'd adapt the server to handle multiple clients.

server_threaded.py

# server_threaded.py
import socket
import os
from socketserver import UnixStreamServer, BaseRequestHandler
SOCKET_FILE = "/tmp/myapp_threaded.sock"
# Remove the socket file if it already exists
try:
    os.unlink(SOCKET_FILE)
except OSError:
    if os.path.exists(SOCKET_FILE):
        raise
# Define a custom request handler
class MyTCPHandler(BaseRequestHandler):
    """
    The request handler class for our server.
    It is instantiated once per connection to the server.
    """
    def handle(self):
        # self.request is the Unix socket connected to the client
        data = self.request.recv(1024).strip()
        print(f"Received from {self.client_address}: {data.decode('utf-8')}")
        # Echo the data back
        self.request.sendall(data)
        print("Data echoed back")
if __name__ == "__main__":
    # Create the server, binding to the socket file
    with UnixStreamServer(SOCKET_FILE, MyTCPHandler) as server:
        # Set permissions
        os.chmod(SOCKET_FILE, 0o666)
        print(f"Threaded server listening on {SOCKET_FILE}")
        server.serve_forever()

This threaded server will handle each client connection in a separate thread, allowing it to serve many clients concurrently without blocking. You can run the same client.py against this new server.

分享:
扫描分享到社交APP
上一篇
下一篇