소켓 네트워크 프로그래밍
네트워크 관련 파이썬 표준 모듈
- 인터넷 : TCP/IP 프로토콜을 사용하여 네트워크를 통해 연결된 장치 사이의 통신임
- TCP/IP protocol(Transmission Control Protocol/Internet Protocol)
- : 컴퓨터 서로 통신하는 경우, 특정 규칙이나 프로토콜을 사용하여 순서대로 데이터를 전송 및 수실할 수 있다.
- 이때 사용하는 규칙중 하나가 TCP/IP 임.
- ex: 메일, 컴퓨터 간 파일 전송, 원격 로그인 등
- 파이썬에서는 TCP/IP 통신을 위한 표준 모듈을 제공
- ipaddress 모듈
- socket 모듈
네트워크 서비스를 위한 파이썬 표준 모듈
- ipaddress: 인터넷 주소 관련 작업 모듈
- 해당 IP 주소가 유효한지 확인하는 경우에도 사용
- IPv4, IPv6의 주소
- 네트워크의 생성 및 처리시에도 사용
- socket: 데이터 송/수신을 위한 소켓 통신 모듈
- select: 입출력을 효율적으로 처리하기 위한 모듈
- socket 모니터링 (파일의 읽기/쓰기가 가능한가? or 통신오류가 발생하는가?)
- selectors : 입출력 다중화 모듈
- 여러 socekt 동시 감시 고급 인터페이스 제공
- socketserver: 네트워크 서버를 작성하기 위한 모듈
- asyncio: 비동기 입출력 모듈
- urlib: URL 관련 프로그래밍 모듈
- http: HTTP 프로토콜을 이용한 프로그래밍 모듈
- HTTP 서버 생성 시에 사용
TCP/IP
- IP 주소 (Internet Protocol address)
- 컴퓨터 네크워크에서 host(장치)들이 식별하고 통신하기 위해 사용한 번호
- subnet mask: 네트워크와 호스트의 아이디를 구분하기 위해 사용
- IP주소와 2진수끼리 곱해 subnet network를 만들 수 있음
- IP 주소 표현 방법
# 기존 방식
192.168.100.10, subnet mask = 255.255.255.0
# CIDR 표기법 : IP주소/10진수(시작 주소/ 네트워크 주소 자리 수) -> subnet mask 필요 x
192.168.100.10/24
- IPv4
- 8bit x 4 = 32bit
- 앞의 24bit : network 주소
- 뒤의 8bit : host 주소
192.168.100.10 11000000.10101000.01100100.00001010 [*******network 주소*******][host주소]
- IPv6
- IPv4의 주소 고갈 문제를 해결
- 16bit x 8 = 128bit
- 0000은 생략하고 :만 남겨둘 수 있음
2001:0DB8:AC10:FE01:0000:0000:0000:0000 0010 0000 0000 0001 : 0000 1101 1011 1000 : ..
ipaddress 모듈
💡 ipaddress.ip_address()
- IP 주소 객체 생성
- IPv4 or IPv6 자동 인식
- 메서드와 속성으로 다양한 주소 관련 처리 가능
- ipaddress 모듈
- IP 주소를 표현하고 처리
- 파이썬에서 사용되는 IP 주소 나타내기 위한 클래스
IPv4Network IPv4 네트워크 주소 192.168.100.0/24 (앞의 24bit가 네트워크 주소임을 가르킴) IPv4Interface IPv4 인터페이스 주소 192.168.100.10/24 (게이트웨이와 같이 네트워크에 진입하는 인터페이스 주소)
IP 주소 생성
1. 호스트 주소
import ipaddress
# 문자열 -> 주소 객체 생성
# IPv4 문자열 주소
ipaddress.ip_address('192.0.2.1')
IPv4Address('192.0.2.1')
# IPv6 문자열 주소
ipaddress.ip_address('2001:DB8::1')
IPv6Address('2001:DB8::1')
# 정수 -> 주소 객체 생성
# IPv4 정수 주소
ipaddress.ip_address(3221225984)
IPv4Address('192.0.2.1')
# IPv6 정수 주소
ipaddress.ip_address(42540766411282592856903984951653826561)
IPv6Address('2001:DB8::1')
2. IPv4 or IPv6 호스트 주소 객체 직접 생성
ipaddress.IPv4Address('192.0.2.1')
IPv4Address('192.0.2.1')
# 정수 혹은 16진수 byte 데이터로 입력해도 동일한 결과 return
ipaddress.IPv4Address(b'\\xc0\\x00\\x02\\x01')
IPv4Address('192.0.2.1')
ipaddress.IPv6Address('2001:DB8::1')
IPv6Address('2001:DB8::1')
3. IP 주소 객체를 문자열이나 정수로 반환
# 해당 주소 객체를 int형으로 타입 변환
int(ipaddress.IPv4Address('192.0.2.1')) #-> 3221225984
# 해당 주소 객체를 문자열로 타입 변환
str(ipaddress.IPv4Address('192.0.2.1')) #-> '192.0.2.1'
IP 주소 속성
1. 주소 객체 속성 (attribute)
- ipaddress 모듈 이용 시 주소의 속성에 접근할 수 있음
- 주소와 관련된 정보를 나타냄
메서드 설명 비고
is_multicast | 멀티캐스트 주소면 참 | 멀티캐스트 : 일대다 통신 |
is_private | 사설망 주소면 참 | 사용자가 설정한 망 |
is_global | 공공망 주소면 참 | 공공이 사용하는 망 |
is_unspecified | 주소 용도 지정x면 참 | |
is_reserved | 유보된 주소면 참 | |
is_loopback | 루프백 주소면 참 | 루프백 주소 : 자신의 host 주소(’127.0.0.1’) |
version | IP 주소 객체의 버전 return | 4 or 6 |
2. 주소 객체 속성 조사
import ipaddress
import binascii
# 다양한 ip주소 문자열 리스트로 생성
addresses = [
# 네이버 ip 주소, 16진수 ip 주소, 루프백 주소 예제
'223.130.195.200',
'2001:0:9d38:6abd:480:f1f:3f57:fffb',
'127.0.0.1'
]
for ipaddr in addresses:
addr = ipaddress.ip_address(ipaddr)
print("-------------")
print(f'ip address : {addr}')
print(f'ip address : {addr!r}')
print('Is ip address multicast? :', addr.is_multicast)
print('Is ip address private? :', addr.is_private)
print('Is ip address global? :', addr.is_global)
print('Is ip address unspecified? :', addr.is_unspecified)
print('Is ip address reserved? :', addr.is_reserved)
print('Is ip address loopback? :', addr.is_loopback)
print('IPversion:', addr.version)
# 바이너리 (4bit씩 16진수)로 변환
print('Packed addr:',binascii.hexlify(addr.packed))
print('Integer addr:', int(addr))
print("-------------")
# 출력 결과
-------------
ip address : 223.130.195.200
ip address : IPv4Address('223.130.195.200')
Is ip address multicast? : False
Is ip address private? : False
Is ip address global? : True
Is ip address unspecified? : False
Is ip address reserved? : False
Is ip address loopback? : False
IPversion: 4
Packed addr: b'df82c3c8'
Integer addr: 3749888968
-------------
-------------
ip address : 2001:0:9d38:6abd:480:f1f:3f57:fffb
ip address : IPv6Address('2001:0:9d38:6abd:480:f1f:3f57:fffb')
Is ip address multicast? : False
Is ip address private? : True
Is ip address global? : False
Is ip address unspecified? : False
Is ip address reserved? : False
Is ip address loopback? : False
IPversion: 6
Packed addr: b'200100009d386abd04800f1f3f57fffb'
Integer addr: 42540488210633193206006041362424135675
-------------
-------------
ip address : 127.0.0.1
ip address : IPv4Address('127.0.0.1')
Is ip address multicast? : False
Is ip address private? : True
Is ip address global? : False
Is ip address unspecified? : False
Is ip address reserved? : False
Is ip address loopback? : True
IPversion: 4
Packed addr: b'7f000001'
Integer addr: 2130706433
-------------
3. 연습문제
- 자신이 사용하는 컴퓨터의 IP 주소 속성 확인하기
my IP addresss: 127.0.0.1
-------------
ip address : 127.0.0.1
ip address : IPv4Address('127.0.0.1')
Is ip address multicast? : False
Is ip address private? : True
Is ip address global? : False
Is ip address unspecified? : False
Is ip address reserved? : False
Is ip address loopback? : True
IPversion: 4
Packed addr: b'7f000001'
Integer addr: 2130706433
-------------
socket 모듈
💡 socket : 통신 채널의 종단점
- 통신을 하기 위해서는 프로그램이 socket에 접속해야 함
- 호스트에 대한 정보를 알아내고,
- socket에 접속하여,
- 데이터를 송수신할 수 있는 함수를 제공
네트워크에서 호스트 검색하기
1. 네트워크 호스트
- 문자열 이름이나 IP 주소 알아내기
- socket 모듈에는 호스트 컴퓨터의 이름이나 주소를 검색할 수 있는 함수가 포함
메서드 (socket. 생략) 설명 입출력
gethostname() | 자신의 호스트 네임 return | ipaddress → name |
gethostbyname() | 해당 hostdml ipaddress return | name → ipaddress |
gethostbyname_ex() | 더 자세한 정보 제공 튜플 반환 | name → (hostname, aliaslist, ipaddrlist) |
getfqdn() | 완전한 도메인 형태 문자열 값으로 return | domain name → full domain name |
gethostbyaddr() | 해당 host 정보 제공 | ipaddress → (hostname, aliaslist, ipaddrlist) |
gethostname() 예제
import socket
socket.gethostname() # isumin-ui-MacBookAir.local
socket.gethostbyname('isumin-ui-MacBookAir.local') # 127.0.0.1
gethostbyname() / gethostbyname_ex() 예제
# host name들을 가진 배열 생성
hosts = [
'www.cnu.ac.kr',
'www.naver.com',
'www.google.com',
'www.python.org'.
'testname'
]
print("-------------")
# [ gethostbyname() / gethostbyname_ex() 예제 ]
for host in hosts:
# host name이 들어오지 않을 때 에러 처리를 해줘야한다!
try:
print(f'{host} : {socket.gethostbyname(host)}')
print(f'more info: {socket.gethostbyname_ex(host)}')
print("-------------")
# 에러 발생시
except socket.error as msg:
print(f'host {host} 에서 에러({e}) 발생')
print("-------------")
# 출력 결과
www.naver.com : 223.130.195.95
more info: ('www.naver.com.nheos.com', ['www.naver.com'], ['223.130.195.95', '223.130.200.104'])
-------------
www.cnu.ac.kr : 168.188.253.130
more info: ('www.cnu.ac.kr', [], ['168.188.253.130'])
-------------
www.google.com : 142.250.199.68
more info: ('www.google.com', [], ['142.250.199.68'])
-------------
host test name 에서 에러([Errno 8] nodename nor servname provided, or not known) 발생
-------------
getfqdn() 예제
from socket import *
getfqdn('cnu.ac.kr') # www.cnu.ac.kr
getfqdn('www.python.org') # www.python.org
getfqdn('pymotw.com') # cdn-185-199-108-153.github.com
gethostbyaddr() 예제
# 호스트 정보 (hostname, aliaslist, ipaddrlist)
hostname, aliases, addresses = socket.gethostbyaddr('203.249.39.46')
print('hostname', hostname)
print('aliaslist', aliases)
print('ipaddrlist', addresses)
# 출력
hostname www.dongyang.ac.kr
aliaslist ['46.39.249.203.in-addr.arpa']
ipaddrlist ['203.249.39.46']
주소 유형(address family) & 소켓 유형(type)
- 주소 유형 : 네트워크 계층의 프로토콜 지정
- AF_INET
- IPv4 주소 유형 : 대부분의 인터넷에서 사용
- 10진 표기법 사용 : 127.0.0.1, 203.249.11.15
- AF_INET
- 소켓 유형 : 전송 계층 프로토콜 지정
- SOCK_STREAM : TCP 프로토콜 사용 (HTTP와 같은 응용 프로토콜에서 사용)
- SOCK_DGRAM : UDP 프로토콜 사용 (TFTP, NTP와 같은 멀티캐스팅에서 사용)
인터넷 서비스 정보 찾기
- 프로토콜 종류
- service : 응용 계층이 제공하는 서비스 프로토콜 (http, ftp, telnet 등)
- proto : 전송 계층 프로토콜 (tcp, udp)
- IP 주소가 하나인 호스트에서 다수 응용 프로그램 실행 가능
- 각각의 응용 프로그램은 고유한 소켓과 연결
- 특정 프로토콜은 지정된 포트 번호 할당
- 소켓은 (IP 주소 + 포트 번호)로 식별서비스 검색의 특징
메서드 설명
getservbyname | |
(servicename[,protocolname]) | servicename: http, ftp등과 같은 응용 프로토콜 |
protocolname: tcp, udp과 같은 전송 계층 프로토콜 | |
getservbyname() | 인터넷 서비스의 이름을 포트 번호로 나타내기 |
getprotobyname() | 전송 계층 프로토콜 이름을 포트 번호로 나타내기 |
getservbyport() | 포트 번호로 부터 서비스 알아내기 |
getservbyname() 예제
print(socket.getservbyname('ftp', "tcp")) # 21
print(socket.getservbyname('ftp')) # 21
print(socket.getservbyname('http')) # 80
print(socket.getservbyname('telnet')) # 23
getprotobyname() 예제
print(socket.getprotobyname('tcp')) # 6
print(socket.getprotobyname('udp')) # 17
getservbyport() 예제
for port in [80, 443, 21, 70, 25]:
url = f'{socket.getservbyport(port)}://example.co.kr/'
print('{:4d}'.format(port), url)
# 출력 결과
80 <http://example.co.kr/>
443 <https://example.co.kr/>
21
70 gopher://example.co.kr/
25 smtp://example.co.kr/
소켓 정보 찾기
1. socket의 속성값 알아내기
- dir(socket) : socket의 모든 속성 표시
- getattr(socket, 'AF_INET') : socket의 해당 속성값 반환
print(getattr(socket, 'AF_INET')) # 2
print(getattr(socket, 'SOCK_STREAM')) # 1
print(getattr(socket, 'SOCK_STREAM')) # 1
2. getaddrinfo() 함수로 소켓에 대한 정보 찾기
- getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
- host : 도메인 이름 (’192.168.0.15.” or ‘www.naver.com’)
- port : 서비스 이름 혹은 포트 번호 (’http’, 2500)
- 반환값 - 5 튜플 (family, type, proto, canonname, sockaddr)
- family: 주소 유형, type: 소켓 타입, proto: 네트워크 계층 프로토콜, canonname: 정식 명칭
results = socket.getaddrinfo('www.naver.com', 'http')
for result in results:
print(result)
# 출력
(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('223.130.195.200', 80))
(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('223.130.195.200', 80))
(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('223.130.195.95', 80))
(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('223.130.195.95', 80))
'CS > 네트워크' 카테고리의 다른 글
[네트워크 프로그래밍] Ch10. 사물인터넷을 위한 프로그래밍 (0) | 2023.06.06 |
---|---|
[네트워크 프로그래밍] Ch9. TCP/UDP 멀티스레드 서버 프로그래밍 (2) | 2023.06.05 |