Python - Raw Connection

YJ·2023년 4월 8일
  • Raw Connection이란?
    • Socket을 사용한 네트워크 연결
    • Socket을 직접 조작하여 데이터를 보내고 받을 수 있음
      • Raw Text Message를 작성하여 전달
        • 응답 데이터는 HTTP 헤더와 JSON 데이터로 구성
        • 응답 메시지를 json 라이브러리를 사용해 JSON 데이터를 직접 파싱 데이터 추출해 사용
    • 낮은 수준의 네트워크 작업을 수행해야 할 때 유용
    • 보안 및 오류 처리 직접 처리해야 함
  • Python에서 HTTP 요청을 보내는 방법
      1. requests 라이브러리 사용하여 요청
        - requests 라이브러리는 알아서 HTTP 요청 메세지를 생성
    • requests 라이브러리는 알아서 응답 데이터를 파싱
      1. socket으로 raw connection 연결하여 요청

Raw Text Message

  • HTTP 프로토콜을 사용하여 웹 서버와 통신 시, 요청 메세지에는 반드시 헤더 정보가 포함되어야 함

  • Raw Connection은 HTTP 요청 메세지를 직접 작성하여야 함

  • Sockect을 사용하여 HTTP 요청을 보낼 시, 요청 헤더 정보는 문자열 형태로 요청 메세지에 포함되어야 함

  • 요청 헤더는 CRLF(Carriage Return, Line Feed)로 구분되어진 줄들로 이루어져 있어야 함

  • 요청 헤더를 요청 메세지에 추가하여 HTTP 요청을 Socket을 이용하여 보낼 수 있음

  • Raw Text Message 구성


# 다음과 같은 요청 헤더가 있을 경우
GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.example.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

# Socket을 이용하여 HTTP 요청 메세지에 포함 시킬 경우
GET /hello.htm HTTP/1.1\r\n
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)\r\n
Host: www.example.com\r\n
Accept-Language: en-us\r\n
Accept-Encoding: gzip, deflate\r\n
Connection: Keep-Alive\r\n\r\n
  • 각 헤더는 : 으로 'key' : 'value' 형식으로 구성
  • 각 헤더 필드는 CRLF로 구분된 줄로 구성
  • 마지막 헤더 필드 뒤에는 반드시 한 줄 공백(CRLF)가 포함되어야 함
  • 필수 헤더
    • Host
    • User-Agent
    • Connection
  • 요청 메시지 구성
    <method> <request-URI> <HTTP-version>
	<headers>

	<entity-body>
  • method
    - 요청 메소드(GET, POST 등)를 나타냄
  • request-URI
    - 요청하는 자원의 위치
  • HTTP-version
    - HTTP 프로토콜의 버전
  • headers
    • 요청 메시지의 헤더
    • Host, Connection, User-Agent 등이 존재
  • entity-body
    • 요청 메시지의 바디입니다.
    • POST 요청과 같이 바디를 보내는 경우에만 존재
  • 요청 메시지 예시
    GET /index.html HTTP/1.1
	Host: www.example.com
	User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
	Accept: 			text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
	Accept-Language: en-US,en;q=0.5
	Accept-Encoding: gzip, deflate, br
	Connection: keep-alive
  • GET 요청을 보냄
  • www.example.com의 /index.html 자원을 요청
  • User-Agent, Accept, Accept-Language, Accept-Encoding 등의 헤더를 추가로 보냄
    • User-Agent
      • 클라이언트의 정보를 서버에게 전달하는 역할
      • 일반적으로 웹 브라우저 이름, 버전, 운영체제 정보 등을 포함

HTTP 요청하여 데이터 가져와보기

Request로 HTTP 요청

    import requests
    # requests
	url = "https://jsonplaceholder.typicode.com/users/1"
	# requests의 get 메소드로 url에 응답 요청
	response = requests.get(url)
	# 응답 상태 확인
	print(response.status_code)
	print()
	# 바이트 타입 데이터 확인
	print(response.content)
	print()
	# utf-8로 인코딩된 데이터 확인
	print(response.text)
	print()
	# utf-8로 인코딩된 데이터를 변수에 저장
	response_json = response.json()
	# json 파일 객체 출력
	print(response_json)
	print()
	# json 데이터에서 key 값으로 데이터 꺼내서 사용
	print("Id:", response_json['id'])
	print("Name:", response_json['name'])
	print("Username:", response_json['username'])
	print("Email:", response_json['email'])
	print()

Socket로 Raw Connection로 HTTP 요청

	import socket
	import json
    # socket - Raw Connection

	# Socket으로 Raw Connection 시에는 요청 헤더를 직접 작성하여 서버에 요청해야 한다.
	request_text = """\
	GET /users/1 HTTP/1.1\r\n\
	Host: jsonplaceholder.typicode.com\r\n\
	User-Agent: http_test.py\r\n\
	Connection: close\r\n\
	\r\n\
	"""

	def jsonplace():
    	# 소켓 생성
    	sock = socket.socket()
    	# 'jsonplaceholder.typicode.com' API 엔드포인트로 연결 요청
    	sock.connect(('jsonplaceholder.typicode.com', 80))
    	# 요청 헤더 텍스트 'ascii'로 인코딩 하여 데이터 요청
    	sock.sendall(request_text.encode('ascii'))

    	# 응답을 받을 변수 생성
    	reply = b''
    	while True:
      	  more = sock.recv(4096)
      	  # 서버에서 보낸 데이터가 없을시 연결 종료
        	if not more:
            	break
        	# 서버에서 보낸 데이터가 있을시 응답을 받을 변수에 데이터 저장
        	reply += more
       
    		# 서버로 부터 응답 받은 데이터를 디코딩
    		reply_str = reply.decode('utf-8')
    		# 헤더와 바디 출력
    	print(reply_str)
    	print()

    	# 응답 헤더와 payload 분리
    	header, body = reply_str.split('\r\n\r\n', 1)
    	# payload 변수에 payload를 json 형태로 저장
    	payload = json.loads(body)

    	# json 데이터에서 key 값으로 데이터 꺼내서 사용
    	print("Id:", payload['id'])
   	    print("Name:", payload['name'])
    	print("Username:", payload['username'])
    	print("Email:", payload['email'])
   

	if __name__ == '__main__':
    	jsonplace()
profile
dev

0개의 댓글