파이썬 소켓 프로그래밍 (10)

CDH·2025년 5월 8일

TCP/IP 프로토콜(1)


TCP/IP 프로토콜(2)


소켓의 개념 1


소켓의 개념 2


소켓의 개념 3

• 소켓의 정의

• 소켓은 1982년 BSD(Berkeley Software Distribution) UNIX 4.1에서 처음 소개되었고, 현재 널리
사용되는 것은 1986년 BSD UNIX 4.3에서 개정된 것

• 소켓은 소프트웨어로 작성된 통신 접속점

• 소켓은 응용프로그램에서 TCP/IP를 이용하는 창구(인터페이스) 역할

• 주로 TCP 상에서 동작하는 소켓을 사용해서 “TCP 소켓”
또는 “TCP/IP 소켓” 이라고 함

• 물론, UDP 소켓도 가능


소켓을 통한 google.com 접속

  • 요청 메세지 GET
import socket

# 1번 방법
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET - IPv4
# socketAF_INET - IPv6 / socket.SOCK_STREAM : TCP , socket.SOCK_DGRAM : UDP 
sock.connect(("www.google.com", 80)) # 주소와 80 설정이 괄호로 되어 있음 (튜플형)

sock.send("GET \n".encode()) # encode() 메소드 - 문자열을 byte로 변환 (필수)

data = sock.recv(1024) # receive의 약자, 버퍼의 크기를 1024 바이트로 설정
print(data.decode())

sock.close()

# 2번 방법 
with socket.socket() as sock: # with 블록을 벗어나는 순간 자동으로 close 함수를 실행해줌
    addr = ("www.google.com", 80)
    sock.connect(addr)
    sock.send("GET \n".encode())
    data = sock.recv(1024) # receive의 약자, 버퍼의 크기를 1024 바이트로 설정
    print(data.decode())

< encode() 메소드 >
• 문자열을 전송할 때 byte로 변환해주는 메소드 (사용 안 할 경우 에러 발생)
• 1바이트 단위 값으로 저장하는 자료형
• b’ ‘ 또는 b” “으로도 쓰임
• cf) str과 다름, bytes()와 유사
• bytes(“ABC”, “utf-8”) vs. “ABC”.encode()


(중요!) 소켓 프로그래밍 구조

  • 여러 프로그래밍 언어에서 동일한 구조를 가지고 있다 ( C, Python, Java 등)

소켓 프로그래밍 - 서버

import socket

# 1.소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

# 2. 바인딩
sock.bind(("",9999)) # 괄호 필수, bind(IP, PORT)
# bind() : "" 는 모든 네트워크 인터페이스에서 들어오는 연결을 수신함

# 3. 접속대기
sock.listen() # 1일 경우 하나의 접속만 허용 (비어있을 경우 모두 받기)
print("The echo server start....")

# 4. 접속 수락
conn, addr = sock.accept() # accept() : 클라이언트의 소켓 객체와 클라이언트의 주소 정보를 리턴

# 5. 데이터 수신
read_data = conn.recv(1024) # 버퍼 사이즈 1024 byte
print("recv : {}".format(read_data))

# 6. 접속종료
conn.close()
sock.close()

# 소켓 생성 -> 바인딩 -> 접속 대기 -> 접속 수락 -> 데이터 수신 -> 접속 종료

소켓 프로그래밍 - 클라이언트

import socket

# 1. 소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 접속 시도
sock.connect(("127.0.0.1", 9999))
# 3. 데이터 송신
send_data = input("입력: ")
sock.sendall(bytes(send_data, "utf-8")) # 인코딩을 utf-8 형식으로 byte 변환

# 4. 접속 종료
print("클라이언트 종료")
sock.close()
# seldall() 을 사용하더라도 send를 거쳐간다

실습 - 서버와 클라이언트 주고 받기

서버 쪽 코드 실행 후 - 오른쪽 아래 아이콘 눌러서 split 해주고 클라이언트 쪽 실행

서버쪽 터미널

클라이언트 터미널


에코 서버 1 (메시지 1번 주고 받는 1회성)

import socket

# 1.소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

# 2. 바인딩
sock.bind(("",9999)) # 괄호 필수, bind(IP, PORT)
# bind() : "" 는 모든 네트워크 인터페이스에서 들어오는 연결을 수신함

# 3. 접속대기
sock.listen() # 1일 경우 하나의 접속만 허용 (비어있을 경우 모두 받기)
print("The echo server start....")

# 4. 접속 수락
conn, addr = sock.accept() # accept() : 클라이언트의 소켓 객체와 클라이언트의 주소 정보를 리턴

# 5. 데이터 수신
read_data = conn.recv(1024) # 버퍼 사이즈 1024 byte
print("recv : {}".format(read_data))

# 5 - 1. 데이터 송신(에코)
conn.sendall(read_data)
print("send : {}".format(read_data))

# 6. 접속종료
conn.close()
sock.close()

# 소켓 생성 -> 바인딩 -> 접속 대기 -> 접속 수락 -> 데이터 수신 -> 접속 종료


에코 클라이언트 1 (메시지 1번 주고 받는 1회성)

import socket

# 1. 소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 접속 시도
sock.connect(("127.0.0.1", 9999))
# 3. 데이터 송신
send_data = input("입력: ")
sock.sendall(bytes(send_data, "utf-8")) # 인코딩을 utf-8 형식으로 byte 변환

# 3-2 데이터 수신 ( 추가됨 )
recv_data = sock.recv(1024)
print(f"recv : {recv_data}")

# 4. 접속 종료
print("클라이언트 종료")
sock.close()
# seldall() 을 사용하더라도 send를 거쳐간다

에코 클라이언트 1 서버와 클라이언트 주고 받기


에코 서버 2 (메시지 여러 번 주고 받음)

import socket

# 1.소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

# 2. 바인딩
sock.bind(("",9999)) # 괄호 필수, bind(IP, PORT)
# bind() : "" 는 모든 네트워크 인터페이스에서 들어오는 연결을 수신함

# 3. 접속대기
sock.listen() # 1일 경우 하나의 접속만 허용 (비어있을 경우 모두 받기)
print("The echo server start....")

# 4. 접속 수락
conn, addr = sock.accept() # accept() : 클라이언트의 소켓 객체와 클라이언트의 주소 정보를 리턴

while True :  # while문 추가하여 여러 번 송수신
    # 5. 데이터 수신
    read_data = conn.recv(1024) # 버퍼 사이즈 1024 byte
    print("recv : {}".format(read_data))

    # 5 - 1. 데이터 송신(에코)
    conn.sendall(read_data)
    print("send : {}".format(read_data))

    if read_data.decode() == 'end' : # 종료문
        break

# 6. 접속종료
conn.close()
sock.close()


에코 클라이언트 2 (메시지 여러 번 주고 받음)

import socket

# 1. 소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 접속 시도
sock.connect(("127.0.0.1", 9999))

while True : # while문 추가
    # 3. 데이터 송신
    send_data = input("입력: ")
    sock.sendall(bytes(send_data, "utf-8")) # 인코딩을 utf-8 형식으로 byte 변환

    if send_data == 'end' :
        break

    # 3-2 데이터 수신 ( 추가됨 )
    recv_data = sock.recv(1024)
    print(f"recv : {recv_data}")


# 4. 접속 종료
print("클라이언트 종료")
sock.close()
# seldall() 을 사용하더라도 send를 거쳐간다

에모 클라이언트 2 서버와 클라이언트 주고 받기

end 입력시 종료됨


(실습) 숫자 맞추기 게임

  • 에코 서버2, 에코 클라이언트2를 수정하여 숫자 맞추기 게임

  • 서버에서 randint(10,99)를 이용해 2자리 난수를 생성

  • 클라이언트가 임의의 2자리 수 입력하면 낮음, 높음, 정답의 결과를 리턴
    - 정답을 맞추면 종료


echo_server2_game 과 client

server

import socket
import random

randNum = random.randint(10, 99)

# 1.소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

# 2. 바인딩
sock.bind(("",9999)) # 괄호 필수, bind(IP, PORT)
# bind() : "" 는 모든 네트워크 인터페이스에서 들어오는 연결을 수신함

# 3. 접속대기
sock.listen() # 1일 경우 하나의 접속만 허용 (비어있을 경우 모두 받기)
print("The echo server start....")
print(randNum) # 서버에서 정답 미리보기
# 4. 접속 수락
conn, addr = sock.accept() # accept() : 클라이언트의 소켓 객체와 클라이언트의 주소 정보를 리턴

while True :  # while문 추가하여 여러 번 송수신
    # 5. 데이터 수신
    read_data = conn.recv(1024) # 버퍼 사이즈 1024 byte
    print("recv : {}".format(read_data.decode())) # byte code를 문자열로 캐스팅함

    # 5 - 1. 데이터 송신(에코)
    
    print("send : {}".format(read_data))

    if int(read_data.decode()) == randNum : # 종료문 . 
        print('정답!')
        conn.send(('정답!').encode('UTF-8'))
        break
    elif int(read_data.decode()) < randNum : # read_data가 바이트 코드이므로 .decode()를 통해 문자열로 변환해 준 후 -> int형으로 바꿔줘야 함. 
        conn.send(('정답보다 높음').encode('UTF-8'))
    elif int(read_data.decode()) > randNum :
        conn.send(('정답보다 낮음').encode('UTF-8'))

# 6. 접속종료
conn.close()
sock.close()


client

import socket

# 1. 소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 접속 시도
sock.connect(("127.0.0.1", 9999))

while True : # while문 추가
    # 3. 데이터 송신
    send_data = input("입력: ")
    sock.sendall(bytes(send_data, "utf-8")) # 인코딩을 utf-8 형식으로 byte 변환

    # if send_data == 'end' :
    #     break

    # 3-2 데이터 수신 ( 추가됨 )
    recv_data = sock.recv(1024)
    print(f"recv : {recv_data.decode()}")

    if recv_data.decode() == '정답!' :
        break 
       


# 4. 접속 종료
print("클라이언트 종료")
sock.close()
# seldall() 을 사용하더라도 send를 거쳐간다

통신해보기

0개의 댓글