HOWTO · Python
OSError: [WinError 10038] 소켓이 아닌 항목에서 작업을 시도했습니다.
이 설명을 통해 우리는 서버와 클라이언트가 무엇이며 소켓 모듈을 사용하여 서버와 클라이언트의 도움으로 응용 프로그램을 만드는 방법을 배웁니다. 또한 Python에서 이러한 유형의 응용 프로그램을 만들 때 오류를 피하는 방법도 배웁니다.
이 페이지의 내용
OSError: [WinError 10038]은 활성 상태가 아닌 대상에 소켓 작업을 시도했다는 뜻입니다. 아래의 잘못된 서버 패턴에서는 클라이언트 연결을 수락한 뒤 해당 소켓을 닫고, 내부 루프에서 다시 사용합니다. 각 연결을 한 번만 처리한 다음 accept()로 돌아가 다음 클라이언트를 받으세요.
최소한의 Python 클라이언트–서버 소켓 예제 만들기
서버는 대기 소켓(listening socket)에서 연결을 기다립니다. accept()가 반환되면 서버는 해당 클라이언트와 연결된 새 소켓을 받으며, 대기 소켓은 이후 연결을 위해 계속 사용할 수 있습니다. 클라이언트는 별도의 소켓을 만들고 같은 주소와 포트로 connect()를 호출합니다.
이 코드를 socket_demo.py로 저장하세요. 인수 없이 실행하면 간단한 로컬 시연을 위해 서버 스레드와 클라이언트를 시작합니다. server 및 client 모드를 사용하면 두 엔드포인트를 별도의 프로세스로 실행할 수도 있습니다.
import socket
import sys
import threading
HOST = "127.0.0.1"
PORT = 6060
MESSAGE = b"Welcome to the server!"
def serve(ready=None):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind((HOST, PORT))
listener.listen(1)
if ready is not None:
ready.set()
connection, address = listener.accept()
with connection:
print(f"Connected: {address[0]}")
connection.sendall(MESSAGE)
def receive():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
client.connect((HOST, PORT))
chunks = []
while True:
chunk = client.recv(2048)
if not chunk:
break
chunks.append(chunk)
message = b"".join(chunks)
print(message.decode("utf-8"))
if len(sys.argv) > 1 and sys.argv[1] == "server":
serve()
elif len(sys.argv) > 1 and sys.argv[1] == "client":
receive()
else:
ready = threading.Event()
server = threading.Thread(target=serve, args=(ready,))
server.start()
ready.wait()
receive()
server.join()
Connected: 127.0.0.1
Welcome to the server!
클라이언트는 서버가 수락된 연결을 닫을 때까지 데이터를 여러 조각으로 받아 모은 뒤, 바이트를 하나로 합쳐 디코딩합니다. 따라서 하나의 메시지가 여러 TCP 읽기에 나뉘어 도착해도 처리할 수 있습니다. 별도 프로세스로 실행하려면 파일이 있는 디렉터리에서 터미널 두 개를 여세요. 첫 번째 터미널에서 python socket_demo.py server를 실행하면 클라이언트 한 개의 연결을 기다립니다. 그런 다음 두 번째 터미널에서 python socket_demo.py client를 실행하세요. 서버는 클라이언트의 루프백 주소를 출력하고, 클라이언트는 받은 메시지를 출력합니다. 서버는 메시지를 보낸 뒤 해당 연결을 닫고, 이 단일 연결 예제가 끝날 때 대기 소켓도 닫힙니다.
OSError: [WinError 10038] 진단 및 해결
잘못된 패턴에서는 바깥쪽 루프가 accept()를 호출해 연결된 소켓을 Client_Socket에 저장합니다. 안쪽 while True 루프는 한 번 전송한 뒤 그 소켓을 닫습니다. 다음 반복에서 같은 닫힌 소켓으로 다시 전송하려 하므로 OSError: [WinError 10038]이 발생할 수 있습니다. 안쪽 루프는 accept()를 호출하지 않으며, 끝나지 않기 때문에 바깥쪽 루프도 다음 클라이언트를 받을 수 없습니다.
해결하려면 조건 없이 반복하는 안쪽 루프를 제거하고, 바깥쪽 루프가 반복될 때마다 수락된 연결 하나를 처리하세요. 즉 연결을 수락하고, 데이터를 보낸 다음, 연결된 소켓을 닫습니다. 서버가 더 많은 클라이언트를 받아야 하는 동안에는 대기 소켓을 열어 두세요. 수락된 소켓을 닫아도 대기 소켓은 닫히지 않으며, 파일을 터미널에서 실행하는 것 자체가 이 오류의 원인은 아닙니다.
직접 작성한 코드를 진단할 때는 실패한 작업에 사용된 소켓이 무엇인지 찾고, 바로 그 소켓이 이미 닫혔는지 확인하세요. 또한 수락된 각 연결을 한 번씩 처리하는지도 점검하세요. 실제 서비스에서는 서버의 수락 루프를 계속 실행하고 클라이언트별 작업을 처리기에 맡기세요. 이 예제는 의도적으로 클라이언트 한 개만 처리한 뒤 종료합니다.