πŸ‘©πŸ»β€πŸ’»/python

[python] socket λ™μ‹œ μ—°κ²°

바쿄리 2024. 10. 22. 10:22

μ΅œκ·Όμ— κ°œλ°œν•œ κΈ°λŠ₯ 쀑에 2개의 λ‘œλ΄‡κ³Ό socket 톡신이 μžˆμ—ˆλ‹€.

2개의 λ‘œλ΄‡μ— λ™μ‹œμ— μš”μ²­μ΄ λ“€μ–΄κ°€λ©΄ 1번 λ‘œλ΄‡ β†’ 2번 λ‘œλ΄‡ μ΄λ ‡κ²Œ μˆœμ„œλŒ€λ‘œ μ§„ν–‰λ˜μ–΄μ„œ

λ™μ‹œμ— μ§„ν–‰λ˜λ„λ‘ 해보렀고 ν•œλ‹€.

확인

λ©€ν‹°μŠ€λ ˆλ“œ 방식, 비동기 방식 두가지가 μžˆλ‹€.

λ©€ν‹°μŠ€λ ˆλ“œ

λ©€ν‹°μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ 각 μ†ŒμΌ“ 연결을 λ³„λ„μ˜ μŠ€λ ˆλ“œμ—μ„œ μ²˜λ¦¬ν•˜μ—¬ 두 개의 연결을 λ™μ‹œμ— 관리할 수 μžˆλ‹€

import socket
import threading

# 첫 번째 μ†ŒμΌ“ μ—°κ²° ν•¨μˆ˜
def connect_to_robot_a():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('123.123.123.123', 65432))

    # 데이터 전솑 μ˜ˆμ‹œ
    data = "Hello from Robot A"
    s.sendall(data.encode())
    response = s.recv(1024).decode()
    print(f"Robot A response: {response}")
    s.close()

# 두 번째 μ†ŒμΌ“ μ—°κ²° ν•¨μˆ˜
def connect_to_robot_b():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('123.123.123.123', 65433))

    # 데이터 전솑 μ˜ˆμ‹œ
    data = "Hello from Robot B"
    s.sendall(data.encode())
    response = s.recv(1024).decode()
    print(f"Robot B response: {response}")
    s.close()

# 메인 ν•¨μˆ˜μ—μ„œ 두 μŠ€λ ˆλ“œλ₯Ό μ‹œμž‘
if __name__ == "__main__":
    thread_robot_a = threading.Thread(target=connect_to_robot_a)
    thread_robot_b = threading.Thread(target=connect_to_robot_b)

    # 두 μŠ€λ ˆλ“œ μ‹œμž‘
    thread_robot_a.start()
    thread_robot_b.start()

    # 두 μŠ€λ ˆλ“œκ°€ 끝날 λ•ŒκΉŒμ§€ λŒ€κΈ°
    thread_robot_a.join()
    thread_robot_b.join()

    print("Both connections completed.")

비동기(asyncio) 방식

비동기 방식은 이벀트 루프λ₯Ό 톡해 두 μ†ŒμΌ“ 연결을 λ™μ‹œμ— 처리 κ°€λŠ₯. λ„€νŠΈμ›Œν¬ I/O μž‘μ—…μ—μ„œλŠ” λ©€ν‹°μŠ€λ ˆλ“œλ³΄λ‹€ 더 νš¨μœ¨μ μ΄λ‹€.

import asyncio

async def connect_to_robot_a():
    reader, writer = await asyncio.open_connection('123.123.123.123', 65432)

    # 데이터 전솑 μ˜ˆμ‹œ
    data = "Hello from Robot A"
    writer.write(data.encode())
    await writer.drain()

    # μ„œλ²„λ‘œλΆ€ν„° 응닡 λ°›κΈ°
    response = await reader.read(1024)
    print(f"Robot A response: {response.decode()}")

    writer.close()
    await writer.wait_closed()

async def connect_to_robot_b():
    reader, writer = await asyncio.open_connection('123.123.123.123', 65433)

    # 데이터 전솑 μ˜ˆμ‹œ
    data = "Hello from Robot B"
    writer.write(data.encode())
    await writer.drain()

    # μ„œλ²„λ‘œλΆ€ν„° 응닡 λ°›κΈ°
    response = await reader.read(1024)
    print(f"Robot B response: {response.decode()}")

    writer.close()
    await writer.wait_closed()

# 메인 비동기 ν•¨μˆ˜
async def main():
    await asyncio.gather(
    	connect_to_robot_a(),
        connect_to_robot_b(),
    )

if __name__ == "__main__":
    asyncio.run(main())

 

정리

비동기(asyncio)와 λ©€ν‹°μŠ€λ ˆλ“œ(threading)λŠ” 각각의 상황에 따라 μ ν•©ν•œ 선택이 될 수 μžˆμ§€λ§Œ,

μ†ŒμΌ“ 톡신과 같은 I/O λ°”μš΄λ“œ μž‘μ—…μ—μ„œλŠ” 비동기 방식이 일반적으둜 더 λ‚˜μ€ μ„±λŠ₯κ³Ό νš¨μœ¨μ„±μ„ μ œκ³΅ν•œλ‹€.

  λ©€ν‹° μŠ€λ ˆλ“œ 비동기
μž₯점 1. CPU λ°”μš΄λ“œ μž‘μ—…μ—μ„œ 병렬 처리λ₯Ό κ°€λŠ₯ (단, Python의 *GIL둜 인해 ν•œμ •μ μΈ 병렬성)

2. λ©€ν‹°μŠ€λ ˆλ”©μ€ 전톡적인 λ™μ‹œμ„± 처리 λ°©μ‹μ΄λ―€λ‘œ, 비동기 방식에 λΉ„ν•΄ 직관적인 μ½”λ“œ μž‘μ„±μ΄ κ°€λŠ₯

3. 이미 λ©€ν‹°μŠ€λ ˆλ”©μ— μ΅μˆ™ν•œ 경우, κ΅¬ν˜„κ³Ό 디버깅이 더 μ‰¬μšΈ 수 μžˆλ‹€
1. 단일 μŠ€λ ˆλ“œμ—μ„œ μ—¬λŸ¬ μž‘μ—…μ„ λ™μ‹œμ— μ²˜λ¦¬ν•  수 있기 λ•Œλ¬Έμ—, μŠ€λ ˆλ“œ μ˜€λ²„ν—€λ“œκ°€ μ—†μŒ

2. λ„€νŠΈμ›Œν¬ 톡신과 같은 I/O μž‘μ—…μ—μ„œλŠ” 비동기 방식이 더 효율적. CPU μžμ›μ„ 거의 μ†Œλͺ¨ν•˜μ§€ μ•ŠμœΌλ©°, I/O μž‘μ—…μ΄ 끝날 λ•ŒκΉŒμ§€ λ‹€λ₯Έ μž‘μ—…μ„ 처리 κ°€λŠ₯

3. μ†ŒμΌ“ ν†΅μ‹ μ²˜λŸΌ 비동기 λ„€νŠΈμ›Œν¬ μž‘μ—…μ΄ λ§Žμ„ λ•Œ, μ„±λŠ₯ ν–₯상을 κΈ°λŒ€ν•  수 μžˆλ‹€

4. μ½”λ“œκ°€ λͺ…ν™•ν•˜κ³  κ΄€λ¦¬ν•˜κΈ° μ‰¬μš΄ ꡬ쑰둜 μž‘μ„± κ°€λŠ₯
단점 1. μŠ€λ ˆλ“œ μ˜€λ²„ν—€λ“œ: μŠ€λ ˆλ“œ 생성과 관리에 좔가적인 λ©”λͺ¨λ¦¬μ™€ CPU μžμ›μ΄ μ†Œλͺ¨

2. 동기화 이슈: λ©€ν‹°μŠ€λ ˆλ”©μ€ μ’…μ’… λ°λ“œλ½, 레이슀 μ»¨λ””μ…˜κ³Ό 같은 λ³΅μž‘ν•œ λ¬Έμ œλ“€μ„ μΌμœΌν‚¬ 수 μžˆλ‹€.

3. μ†ŒμΌ“ ν†΅μ‹ μ²˜λŸΌ I/O μž‘μ—…μ΄ λ§Žμ€ ν™˜κ²½μ—μ„œλŠ” μŠ€λ ˆλ“œκ°€ *유휴 μƒνƒœλ‘œ λŒ€κΈ° ν•˜λŠ” μ‹œκ°„μ΄ λ§Žμ•„μ„œ λΉ„νš¨μœ¨μ μΌ 수 μžˆλ‹€.
1. CPU λ°”μš΄λ“œ μž‘μ—…(예: 데이터 뢄석, λ³΅μž‘ν•œ 계산 λ“±)μ—μ„œλŠ” μ ν•©ν•˜μ§€ μ•Šλ‹€. 그런 κ²½μš°μ—λŠ” GIL(Global Interpreter Lock) 문제둜 인해 asyncioλ§ŒμœΌλ‘œλŠ” 병렬 μ²˜λ¦¬κ°€ νž˜λ“€ 수 μžˆλ‹€.

2. 비동기 νŒ¨ν„΄μ— μ΅μˆ™ν•˜μ§€ μ•Šλ‹€λ©΄ μ½”λ“œ μž‘μ„± 및 디버깅이 쑰금 λ³΅μž‘ν•  수 μžˆλ‹€.
μΆ”μ²œ 상황 1. μ†ŒμΌ“ 톡신, 파일 읽기/μ“°κΈ°, λ°μ΄ν„°λ² μ΄μŠ€ 쿼리와 같은 I/O λ°”μš΄λ“œ μž‘μ—…

2. λ™μ‹œμ„± μ²˜λ¦¬κ°€ μ€‘μš”ν•˜κ³ , μ—¬λŸ¬ I/O μž‘μ—…μ„ λΉ„νš¨μœ¨μ μœΌλ‘œ 기닀리고 μ‹Άμ§€ μ•Šμ€ 경우
 1. CPU λ°”μš΄λ“œ μž‘μ—…(λ³΅μž‘ν•œ 계산 μž‘μ—…)μ—μ„œ 유용

2. λ©€ν‹°μ½”μ–΄ μ‹œμŠ€ν…œμ—μ„œ 병렬 μ²˜λ¦¬κ°€ ν•„μš”ν•œ 경우, λ©€ν‹°ν”„λ‘œμ„Έμ‹±κ³Ό κ²°ν•©ν•˜μ—¬ 더 λ‚˜μ€ μ„±λŠ₯을 λ‚Ό 수 μžˆλ‹€

 

*GIL: Global Interpreter Lock의 μ•½μžλ‘œ, μ—¬λŸ¬ 개의 μŠ€λ ˆλ“œκ°€ 파이썬 λ°”μ΄νŠΈμ½”λ“œλ₯Ό ν•œλ²ˆμ— ν•˜λ‚˜λ§Œ μ‚¬μš©ν•  수 있게 Lock을 κ±°λŠ” 것

*유휴 μƒνƒœ: 컴퓨터 μ‹œμŠ€ν…œμ΄ μ‚¬μš© κ°€λŠ₯ν•œ μƒνƒœμ΄λ‚˜ μ‹€μ œμ μΈ μž‘μ—…μ΄ μ—†λŠ” μ‹œκ°„

λŒ“κΈ€μˆ˜0