Python Network Programming- Socket Library
Socket ,IPv4, Simple Client/Server Programming
Module Socket
- 네트워크를 경유하는 프로세스간 통신의 양 끝단을 추상화 시킨 개념.
- 대부분 통신은 인터넷 프로토콜 기반으로 , 대부분의 네트워크 소켓은 인터넷 소켓이다. (RFC 147 기술)
- 통신을 위해 양 끝단에서 소켓을 생성하고, 생성한 소켓을 통해 데이터 교환을 수행한다.
구성요소
- Internet Protocol : TCP, UDP rawIP
- Local IP Address / Local Port number
- Remote IP Address / Remote Port number
1. Getting the Machine Information
(1) gethostname() return STRING
현재 호스트의 이름을 반환
(2) gethostbyname(hostname) return STRING
파라미터로 입력된 호스트의 IP 주소를 반환
hostname ①gethostname() 함수를 통해 얻어온 호스트명 ② 원격지 주소
(3) gethostbyname_ex(hostname) return LIST (hostname,aliaslist,ipaddrlist)
1 2 3 4 5 6 7 8 9 10 11 12 | def get_remote_machine_info(): remote_host = 'kiss-PC' ip_addr = socket.gethostbyname_ex(remote_host) host_name = socket.gethostname() try: print("Host Name: %s" %host_name) print("IP Address: %s" %ip_addr[2][0]) except (socket.error, err_msg): print("%s: %s" %(remote_host, err_msg)) | cs |
2. Converting IPv4 Address to different format
로우 레벨 네트워크 함수를 사용하기 위해서 일반적인 문자열 표기법은 유용하지 않기 때문에 문자열을 packed 32-bit binary 형식으로 변환해줄 필요가 있다.
(1) inet_aton(ip_string)
'192.168.0.1'과 같은 IPv4 주소 포맷을 packed 32-bit binary로 변환
(2) inet_ntoa(binary)
inet_aton() 과 반대로 packed 32-bit binary를 IPv4 주소 포맷으로 변환
Module binascii
The
binascii
module contains a number of methods to convert between binary and various ASCII-encoded binary representations. Normally, you will not use these functions directly but use wrapper modules likeuu
,base64
, orbinhex
instead. Thebinascii
module contains low-level functions written in C for greater speed that are used by the higher-level modules.
hexlify(binary_data) : binary 데이터를 16진수 표현으로 값을 반환
1 2 3 4 5 6 7 | def convert_ipv4_address(): for ip_addr in ['127.0.0.1', '192.168.0.1']: packed_ip_addr = socket.inet_aton(ip_addr) unpacked_ip_addr = socket.inet_ntoa(packed_ip_addr) print("IP Address: %s => Packed: %s, Unpacked: %s"\ %(ip_addr, hexlify(packed_ip_addr), unpacked_ip_addr)) | cs |
3. Finding Service Name
getservbyport(port,[protocol]) : 포트번호를 매개변수로 입력해 해당 포트에 해당하는 서비스 이름을 출력해준다. 추가적으로 protocol name 파라미터 값을 줄 수 있다.
1 2 3 4 5 6 7 | def find_service_name(): protocol_name = 'tcp' for port in [80,25,23]: print("Port: %s => service name: %s" \ %(port, socket.getservbyport(port,protocol_name))) print("Port : %s => service name:%s"\ %(53, socket.getservbyport(53, 'udp'))) | cs |
4. Integer Convert to and from host to network byte order
바이트오더(Byte Order)
바이트를 배열하는 방법을 말한다. Little-Endian & BigEndian 방식
소켓을 통해 양 단말 간 통신을 위해 호스트에서 네트워크로 데이터를 전송하기 위해 네트워크 포맷으로 데이터를 변환해 주는 작업이 필요하다.
Because 호스트와 네트워크 사이의 서로다른 Byte Order 사용
1 2 3 4 5 6 7 8 | def convert_integer(): data = 1234 # 32-bit print("Original: %s => Long host byte order: %s, Network byte order: %s"\ %(data, socket.ntohl(data), socket.htonl(data))) # 16-bit print("Original: %s => Short host byte order: %s, Network byte order: %s"\ %(data, socket.ntohs(data), socket.htons(data))) | cs |
5. Setting / Getting Socket timeout
gettimeout(): 소켓 객체의 타임아웃 값을 반환
settimeout() : 소켓의 타임아웃 값을 설정
socket.getdefaulttimeout() : 소켓의 디폴트 타임아웃 값을 반환
socket.getdefaulttimeout(timeout_seconds) : 소켓의 디폴트 타임아웃 값을 변경
1 2 3 4 5 6 | def test_socket_timeout(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("Default socket timeout: %s" %s.gettimeout()) s.settimeout(100) print(socket.getdefaulttimeout()) print("Current socket timeout: %s" %s.gettimeout()) | cs |
6.Python handling Socket errors
네트워킹을 하는 애플리케이션들은 보통 한쪽에서 연결을 시도하고, 다른 한 쪽에서 그 연결에 대해 네트워크 미디어 실패나 다른 이유로 응답에 실패 할 수 있다. 파이썬은 socket.error를 통해 예외들을 제어하기 위한 핸들링 메소드를 제공한다.
기본적인 예외처리
try – except code block 사용
(+) Additional!....
argparse : 사용자 입력을 받기 위한 모듈 . 여러 명령어들 보면 인자를 입력하도록 하는 형태가 있는데 이 모듈을 통해 만들 수 있다. sys.argv로도 가능하지만 더 강력한 기능을 제공한다.
Changed in version 3.5: The method now waits until the connection completes instead of raising an InterruptedError exception if the connection is interrupted by a signal, the signal handler doesn’t raise an exception and the socket is blocking or has a timeout (see the PEP 475 for the rationale).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | import socket import sys import argparse def main(): parser = argparse.ArgumentParser(description='Socket Error Examples') parser.add_argument('--host', action="store", dest="host",\ required=False) parser.add_argument('--port', action="store", dest="port",\ type=int, required=False) parser.add_argument('--file', action="store", dest="file",\ required=False) given_args = parser.parse_args() host = given_args.host port = given_args.port filename = given_args.file # First try-except block -- create socket try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error, e: print("Error creating socket: %s" %e) sys.exit(1) # Second try-except block -- connect to given host/port try: s.connect((host, port)) except socket.gaierror, e: print("Address-related error connecting to server: %s" % e) sys.exit(1) except socket.error, e: print("Connection error: %s" %e) sys.exit(1) # Third try-except block -- sending data try: s.sendall("GET %s HTTP/1.0 \r\n\r\n" %filename) except socket.error, e: print("Error sending data:%s" %e) sys.exit(1) while 1: # Fourth try-except block -- waiting to receive dta from remote host try: buf = s.recv(2048) except socket.error, e: print("Error receiving data:%s" % e) sys.exit(1) if not len(buf): break sys.stdout.write(buf) if __name__ == '__main__': main() | cs |
7. Modifying socket's buffer size
기본적으로 소켓의 버퍼 사이즈는 여러 상황들에 적합하게 되어 있지 않다. 몇 몇 상황에서는 디폴트 소켓 버퍼의 사이즈를 더 적합한 크기로 변경할 필요가 있을 것이다.
(1) setsockopt(level, optname, value:int|buffer)
소켓 속성값을 설정해줌
- level : socket.SOL_IP | SOL_TCP | SOL_UDP | SOL_SOCKET
- optname: socket.OP_ *..
- value : integer | buffer
(2) getsockopt()
소켓 속성값을 반환
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import socket SEND_BUF_SIZE = 4096 RECV_BUF_SIZE = 4096 def modify_buff_size(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF) print("Buffer size [Before]:%d" %bufsize) sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) sock.setsockopt( socket.SOL_SOCKET, socket.SO_SNDBUF, SEND_BUF_SIZE) sock.setsockopt( socket.SOL_SOCKET, socket.SO_RCVBUF, RECV_BUF_SIZE) bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF) print("Buffer size [After]:%d" %bufsize) if __name__ == '__main__': modify_buff_size() | cs |
8. Scoket's Blocking / Non-Blocing mode
기본값으로 TCP 소켓은 블로킹 모드로 되어 있다. 이는 특정 명령어가 완료될 때 까지 프로그램에게 제어권을 반환하지 않는 것을 의미한다.
(1) setblocking(flag)
소켓을 blocking(1) 또는 non-blocking(0) 모드로 설정합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import socket def test_socket_modes(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(1) sock.settimeout(0.5) sock.bind(("127.0.0.1", 0)) socket_address = sock.getsockname() print("Trivial server launched on socket: %s" %str(socket_address)) while(1): sock.listen(1) if __name__ == '__main__': test_socket_modes() | cs |
9. Reusing socket address
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import socket import sys def reuse_socket_addr(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) old_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) print("Old sock state: %s" %old_state) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) new_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) print("new sock state: %s" %new_state) local_port = 8282 srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind(('',local_port)) srv.listen(1) print("Listening on port: %s" %local_port) while True: try: connection, addr = srv.accept() print("connected by %s:%s" %(addr[0]. addr[1])) except socket.error, msg: print("%s" %(msg,)) if __name__ == '__main__': reuse_socket_addr() | cs |
10. time synchronization
유닉스의 make 명령어와 같이 시스템의 정확한 시간에 의존하는 프로그램들을 위해 시간 동기화기 필요하다. ntp를 이용한 시간 동기화를 한다.
(1) Network Time Protocol(NTP) 설치하기
pip install ntplib
(2) time.ctime([secs])
로컬 시간을 나타내는 문자열을 초로 나타내는 시간 표현으로 변환해준다.
1 2 3 4 5 6 7 8 9 10 | import ntplib from time import ctime def print_time(): ntp_client = ntplib.NTPClient() response = ntp_client.request('pool.ntp.org') print(ctime(response.tx_time)) if __name__ == '__main__': print_time() | cs |
Module time
This module provides various time-related functions. For related functionality, see also the
datetime
andcalendar
modules.
11. SNTP Client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import socket import struct import sys import time NTP_SERVER = '0.kr.pool.ntp.org' TIME1970 = 2208988800L def sntp_client(): client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) data = '\x1b' + 47 * '\0' client.sendto(data, (NTP_SERVER,123)) data, address = client.recvfrom(1024) if data: print("Response received from:", address) t = struct.unpack('!12I', data)[10] t -= TIME1970 print("\tTime=%s" %time.ctime(t)) if __name__ == '__main__': sntp_client() | cs |