杰瑞科技汇

Python close wait 是什么原因?

Of course! The concept of a "close wait" state in Python is crucial for writing robust, non-blocking network applications. It's not a specific Python function, but rather a network socket state that Python programmers need to understand and manage.

Python close wait 是什么原因?-图1
(图片来源网络,侵删)

Let's break it down.

What is "Close Wait"?

"Close Wait" is one of the many states defined in the TCP (Transmission Control Protocol) state machine. It represents a specific point in the process of gracefully closing a network connection.

The Scenario:

Imagine a client and a server are communicating.

Python close wait 是什么原因?-图2
(图片来源网络,侵删)
  1. The Client decides it's done sending data. It sends a FIN (Finish) packet to the Server. This is like saying, "I have no more data to send."
  2. The Server receives the FIN. It sends back an ACK (Acknowledgment) to the Client. This acknowledges that it received the Client's FIN.
  3. The Server now enters the CLOSE_WAIT state.

What CLOSE_WAIT Means:

The server is now in a holding pattern. It has acknowledged that the client is done sending, but it still has one critical job to do: process any remaining data it has received from the client and send its own final FIN packet.

Think of it like this:

  • Client: "I'm done talking." (Sends FIN)
  • Server: "Okay, I heard you. Let me finish what I was doing/saying, and then I'll hang up too." (Enters CLOSE_WAIT, sends ACK)

The server will remain in CLOSE_WAIT until its application code (your Python script) explicitly closes the socket. At that point, the server will send its own FIN to the client, and the connection will proceed through the final stages of termination (LAST_ACK, TIME_WAIT, etc.).

Python close wait 是什么原因?-图3
(图片来源网络,侵删)

The Problem: Lingering in CLOSE_WAIT

The "close wait" problem occurs when a server socket gets stuck in the CLOSE_WAIT state for a long time, or even indefinitely. This happens for one primary reason:

The application code on the server has not properly closed the socket.

This is almost always caused by a bug in the server's logic. For example:

  • The server is stuck in a loop waiting for more data that will never come.
  • The server has a reference to the socket but never calls my_socket.close().
  • An exception occurs before the close() statement is reached, and the socket is not closed in a finally block.

When a socket is stuck in CLOSE_WAIT, it consumes resources on the server (file descriptors, memory). If enough sockets accumulate in this state, the server can run out of resources, leading to a "resource leak" and potentially crashing the application.


How to See CLOSE_WAIT in Python

You can't directly see the state from Python's socket object, but you can see it in your operating system's network connection list.

Here's a simple Python server that will intentionally get stuck in CLOSE_WAIT to demonstrate.

Problematic Server Code (stuck_server.py):

import socket
HOST = "127.0.0.1"
PORT = 65432
# Create a socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((HOST, PORT))
    s.listen()
    print(f"Server listening on {HOST}:{PORT}")
    conn, addr = s.accept()
    print(f"Connected by {addr}")
    # This is the problem: we read some data but never close the connection.
    # We just go into an infinite loop.
    data = conn.recv(1024)
    print(f"Received: {data.decode('utf-8')}")
    # BUG: The connection is never closed here!
    # The server will be stuck in CLOSE_WAIT until the process is killed.
    while True:
        pass # Do nothing, keep the socket open

Client Code (client.py):

import socket
HOST = "127.0.0.1"
PORT = 65432
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b"Hello, server!")
    # The client is done, it will close its side of the connection.
    print("Client: Sent data and closed its connection.")

Steps to Reproduce:

  1. Run the server: python stuck_server.py
  2. Run the client in another terminal: python client.py

Check the Server's State:

On Linux/macOS, use the netstat or ss command in the terminal where the server is running:

# The 'a' is for all, 'n' for numeric, 'p' for program, 't' for tcp
# Look for the state in the last column
netstat -anp | grep :65432
# Or using the more modern 'ss' command
ss -tnp | grep :65432

You will see an output similar to this:

(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:65432         127.0.0.1:54321      ESTABLISHED 12345/python3

Wait a moment... it might show ESTABLISHED. Let's be more specific. The client will close its side, so let's check again.

After the client runs, the server-side connection will be in CLOSE_WAIT. Let's filter by state:

# On Linux
netstat -an | grep CLOSE_WAIT
# On macOS
netstat -an | grep CLOSE_WAIT

You should see an entry showing that your Python server is holding a connection in the CLOSE_WAIT state.


How to Fix and Prevent CLOSE_WAIT in Python

The solution is always in your application logic. You must ensure that every socket is properly closed, especially in the face of errors.

Solution 1: The with Statement (Best Practice)

The with statement is the easiest and most Pythonic way to ensure a socket is closed. It automatically calls the socket.close() method when the block is exited, even if an exception occurs.

Corrected Server Code (good_server.py):

import socket
HOST = "127.0.0.1"
PORT = 65432
# The 'with' statement handles closing the connection automatically.
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((HOST, PORT))
    s.listen()
    print(f"Server listening on {HOST}:{PORT}")
    # Use another 'with' statement for the accepted connection
    with conn, addr := s.accept():
        print(f"Connected by {addr}")
        data = conn.recv(1024)
        print(f"Received: {data.decode('utf-8')}")
        # Do some work...
        # When the 'with' block for 'conn' ends, conn.close() is called automatically.
        # This sends the server's FIN, exiting CLOSE_WAIT.

(Note: The with conn, addr := s.accept(): syntax requires Python 3.10+ for tuple unpacking in a with statement. For older versions, you'd do:)

# For Python < 3.10
conn, addr = s.accept()
with conn:
    # ... rest of the logic

Solution 2: The try...finally Block

If you need more complex logic than a simple with block can provide, use a try...finally block. The finally block is guaranteed to execute, whether an error occurred or not.

import socket
HOST = "127.0.0.1"
PORT = 65432
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen()
print(f"Server listening on {HOST}:{PORT}")
try:
    conn, addr = s.accept()
    print(f"Connected by {addr}")
    try:
        data = conn.recv(1024)
        print(f"Received: {data.decode('utf-8')}")
        # Do some work...
    finally:
        # This ensures the connection is closed even if an error happens in the 'try' block.
        print("Closing connection...")
        conn.close()
finally:
    print("Closing server socket.")
    s.close()

Summary

Concept Explanation How to Fix in Python
What is CLOSE_WAIT? A TCP state where the server has acknowledged the client's FIN but hasn't sent its own FIN yet. It's waiting for the application to finish up. Ensure your application logic correctly signals it's done.
The Problem A socket gets stuck in CLOSE_WAIT because the application code failed to call socket.close(). This is a resource leak. Always close sockets.
The Solution Use robust resource management techniques to guarantee sockets are closed, even during errors. Use with socket.socket(...) for automatic closing.
Use try...finally for manual but guaranteed closing.
分享:
扫描分享到社交APP
上一篇
下一篇