사물인터넷이란 ?
- 각 종 사물에 센서와 통신기술을 이용하여 인터넷에 연결하는 것
- 사물인터넷 프로토콜
- HTTP (Hyper Text Transfer Protocol)
- CoAP (Constrained Application Protocol)
- MQTT (Message Queueing Telemetry Transport)
HTTP의 기본 동작
- HTTP : 서버와 클라이언트 사이의 응용 프로토콜로 클라이언트가 서버에게 정보를 요청 (HTTP Request) 하면 서버는 응답 (HTTP Response) 을 전송하는 구조이다
- 요청은 URL(Uniform Resource Locator)로 구성되어 서버에게 전달됨
- 서버는 URL을 분해하여 요청을 처리함
- 서로 연결할 수 있는 장치 많고 웹 브라우저를 통한 연결 가능
- url : 요청하는 것에 대한 정보 제공
웹 브라우저 요청을 구성한다 (url)
→ 웹 서버에게 url을 통해서 정보 요청 (HTTP GET Request)
→ 웹 서버는 웹 브라우저가 정보 전달 (HTTP Response Message)
1. urllib.parse 모듈을 사용한 URL 분해
urllib.parse 모듈
- 서버에서 URL 문자열을 분해하거나 합성하기 위한 모듈
- url은 6가지 요소로 구성된다. ( url = “http://location/path;parameter?query=argument#frag” )
서버에서 URL 분해하기
urllib.parse.urlparse() 함수는 url을 분해하여 6개의 요소를 갖는 RarseResult 객체를 반환함
from urllib import parse url = "http://location/path;parameter?query=argument#frag" p_url = parse.urlparse(url) print(p_url) # 실행 결과 ParseResult(scheme='http', netloc='location', path='/path', params='parameter', query='query=argument', fragment='frag')
URL 요소
url = "http://location/path;parameter?query=argument#frag"
scheme 자원에 접근하기 위해 사용되는 프로토콜 netloc 서버의 도메인 네임 path Web Client가 접근하려는 서버 내의 자원 (Resource) 경로 params path parameter(;로 분리) query 자원을 처리하거나 검색하기 위한 정보 문자열 (?name1=value1&name2=value2) fragment Web Page 내부 section 식별자 ParseResult 객체
- Tuple의 subclass인 namedtuple 객체
URL 겹합하기
두 개의 문자열을 하나의 URL로 결합하기
urljoin(base_url_string, relative_url_string) 함수 이용
from urllib import parse new_url = parse.urljoin('http://host.com/path/rsc/1". "../other/rsc") print("Joined URL: ", new_url) # 실행 결과 Joined URL: http://host.com/path/other/rsc
geturl() 함수를 사용하여 분해된 URL 요소를 하나의 URL 문자열로 표현
from urllib import parse original = 'http://netloc/path;pram?query=arg#frag' #URL p_url = parse.urlparse(original) #URL 분해 print("Unparsed URL:", p_url.geturl()) # 분해된 URL을 재결합
urlunparse() 함수를 사용하여 Tuple로 표시한 요소를 하나의 URL 문자열로 표현
import urllib.parse import urlunparse tuple_url = ('http', 'netloc', '/path', 'query=arg') # 튜플 형태로 표시된 URL 요소 url = urlunparse(tuple_url) # url 요소의 결합 print('Encoded:', url)
2. urllib.request 모듈을 사용한 네트워크 자원 요청과 생성
GET/POST request
GET: 서버에 있는 자원 요청
“http://localhost:8080”에 질의(request)를 보내 자원을 요청한다
질의 내용
/test/demo_form.php?name1=value1&name2=value2 ? 앞부분 Location, 뒷 부분 Query
from urllib import request response = response.urlopen('http://www.naver.com') # urlopen 함수로 GET request print("Response", response) print("URL", response.geturl()) headers = response.info() # 응답의 header 정보를 받아온다 print("date", headers['date']) # 헤더는 딕셔너리 형태로 정보를 저장한다 data = response.read().decode("utf-8") # 응답의 데이터 정보 read() 함수로 받아옴
urllib.parse.urlencode()를 이용한 GET request
urllib.request.urlopen(URL) 함수를 사용하여 GET 요청을 함
질의(query)내용은 urllib.parse.urlencode() 함수로 생성함
query는 딕셔너리 형태이다
# 쿼리를 사용하여 상세 질문을 요청하는 코드 from urllib import parse from urllib import request query = {'name' : 'john', 'position' : 'student'} # query를 딕셔너리로 표현 encoded_query = parse.urlencode(query) # 딕셔너리에서 query URL 생성 url = 'http://localhost:8080/?' + encoded_query # resourse + query = URL resp = request.urlopen(url) print(resp.read().decode('utf-8'))
POST: 서버로 데이터를 전송하여 자원 생성(create)
“http://localhost:8080”에 데이터를 보내 자원 생성
데이터 내용
{'q' : 'query string', 'foo' : 'bar'}
from urllib import parse from urllib import request data = {'temperature': '25C', 'humidity': '60%'} # query encoded_data = parse.urlencode(data).encode('utf-8') # bytes type url = 'https://localhost:8080/' # 베이스 URL resp = request.urlopne(url, encoded_data) # post 요청 print(resp.read().decode('utf-8'))
3. requests 모듈을 사용한 네트워크 자원 요청과 생성
[ requests 모듈 ]
- requests 모듈 이용 시 HTTP request를 간단히 보낼 수 있음
- requests 모듈 별도 설치 필요
- 기능
- GET, PUT, POST, DELETE, HEAD, OPTIONS 등
GET requests: requests.get(url)
import requests resp = requests.get("https://httpbin.org.get") print(resp.text)
PUT requests: requests.put(url, data)
자원을 갱신하거나 생성
import requests resp = requests.put("https://httpbin.org/put", data={"key":"value"}) print(resp.text)
POST requests: requests.post(URL, data)
자원 생성
import requests resp = requests.post("https://httpbin.org/put", data={"key":"value"})
DELETE requests
자원삭제
resp = requests.delete("https://httpbin.org/delete")
HEAD requests
응답 전체를 가져올 필요없이 응답 headers 속의 메타 정보 검색
resp = requests.head("https://httpbin.org/get")
OPTIONS requests
서버가 지원하는 HTTP 메소드 반환
resp = requests.options("http://www.google.com") print(resp.headers['allow']) # GET, HEAD 출력
[ requests URL에 파라미터 추가하기 ]
URL 속에 데이터 전달하기
? 다음에 key/value 쌍 추가
https://httpbin.org/get?key=value
데이터를 딕셔너리로 구성, params 키워드를 이용하여 전달
data = {'key1':'value1', 'key2':'value2', 'key3':'value3'} resp = requests.get('https://httpbin.org/get', params=data) print(resp.url)
[ requests에 대한 서버의 응답 ]
서버의 응답 속성
url, text, encoding, content, json(), raw, headers, status_code text, encoding, content, json(), status_code에 대한 속성 확인
resp = requests.get("https://httpbin.org/get") print(resp.text) # text 응답 print(resp.encoding) # 인코딩 방법 print(resp.content) # 바이너리 응답 print(resp.json()) # json 응답 print(resp.status_code) # 상태 코드
raw 속성 확인
# stream 파라미터를 True로 설정 resp = requests.get("https://httpbin.org/get", stream = True) print(resp.raw) print(resp.raw.read(10))
headers
resp = requests.get("https://httpbin.org/get") resp.headers # 딕셔너리로 표시 resp.headers['Content-Type'] # headers의 Content-Type 값 'application/json' resp.headers['Date'] # headers의 Date 값 'Fri, 28 Jun 2019 05:23:21'
4. http.server 모듈을 사용한 HTTP 서버 구현
HTTP Server
http server 모듈의 BaseHTTPRequsetHandler 파생 클래스를 정의하여 HTTP request를 처리
HTTP request 요소
- command : command 처리를 위해 do_GET(), do_POST() 메서드를 정의하고 path, headers 요소 처리
- path
- headers
HTTP Server 기본 구조
from http.server import HTTPServer, BaseHTTPRequestHandler from urllib import parse class Handler(BaseHTTPRequestHandler): def do_GET(self): # 클라이언트가 GET 함수를 호출하면 실행되는 함수 ... def do_POST(self): ... server = HTTPServer(('localhost', 8080), Handler) # HTTPServer 객체 생성 server.serve_forever() # Server 시작
간단한 HTTP Server( dummy_web_server.py)
do_GET 함수 → 1) 헤더 전송 2) 데이터 전송 수행
200 → 응답 okay
end_headers() : 헤더가 끝났음을 알린다
from http.esrver import HTTPServer, BaseHTTPRequsetHandler from io import BytesIO # 메모리 상주 파일에 데이터를 쓰거나 읽을 때 사용 class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) # 헤더 응답 전송 self.end_headers() self.wfile.write(b'Hello, world!') # 응답 메시지 전송 def do_POST(self): content_length = int(self.headers['Content-Length']) body = self.rfile.read(content_length) # 헤더에 있는 컨텐츠 길이만큼 입력을 받는다 self.send_response(200) self.end_headers() response = BytesIO() # 메모리 상주 바이너리 파일 객체 response.write(b'This is POST request.') response.write(b'Received: ') response.write(body) self.wfile.write(response.getvalue()) # 응답 전송 httpd = HTTPServer(("localhost', 8080), SimpleHTPRequestHandler) httpd.serve_forever()
HTTP GET Server 기본 구조 (http_server_GET.py)
class GetHandler(BaseHTTPRequestHandler): def do_GET(self): parsed_path = parse.urlparse(self.path) # 전달 받은 url 분해 for name, value in sorted(self.headers.items()): print("{} = {}".format(name, value.rstrip()) message # 응답 메시지 준비 ... # header 전송 self.send_response(200) self.send_header('Content-Type', 'text/plain; charset=utf-8') self.end_headers() # 메세지 전송 self.wfile.write(message.encode('utf-8') if __name__ == "__main__": server = HTTPServer(("localhost", 8080), GetHandler) server.serve_forever()
HTTP GET Server 예제
urlparse로 url 분석 = url 주소 + params (key = value & key = value)
query_parse로 뽑아낸 query를 문자열 형태에서 딕셔너리 형태로 변환
def do_GET(self); parsed_path = parse.urlparse(self.path) # url 분해 msg = parsed_path.query # url에서 query를 분리한다 if msg == "": return # query가 없으면 종료 parsed_query = query_parse(msg) # 다중 query를 딕셔너리로 분해 resp = "Fault" # 허용되지 않음 query에 대한 응답 try: if parsed_query["led"] == "on": # query의 'led'가 'on'일때 resp = "LED is ON" elif parsed_query["led"] == "off": # query의 'led'가 'off'일때 resp = "LED is OFF" except: pass self.wfile.write(resp.encode()) # 응답 전송
다중 query를 딕셔너리로 분해
def query_parse(query): # query 분해 (문자열 형태의 params -> dic 형태) query = query.split("&") # "&"로 연결된 다중 query를 리스트로 분리 temp = [] for item in query: # 각각의 query에 대해 temp.append(item.split("=")) # "="로 연결된 name과 value를 분리 for i in range(len(temp)): if len(temp[i]) == 1: # query에 name만 있으면 (switch=) temp[i].append("") # blank value 추가 return dic(temp) # 리스트를 딕셔너리로 반환
'CS > 네트워크' 카테고리의 다른 글
[네트워크 프로그래밍] Ch9. TCP/UDP 멀티스레드 서버 프로그래밍 (2) | 2023.06.05 |
---|---|
[네트워크 프로그래밍] Ch6. 소켓 네트워크 프로그래밍 (0) | 2023.06.05 |