티스토리 뷰

이미 파이썬에는 urllib 이라는 훌륭한 모듈이 존재하지만 그냥 한번 비슷한 기능을 하는 코드를 만들어보았습니다.

이상하거나 고쳐져야 할 부분이 있다면 말씀 부탁드려요


# -------------------------------
# 사용 예제코드
# requestCode = []
# requestCode.append("GET /robots.txt HTTP/1.1\r\n")
# requestCode.append("Host: 웹사이트주소닷컴\r\n")
# requestCode.append("\r\n")
# responseData = kst_http_by_socket('웹사이트주소닷컴', 443, "".join(requestCode), True, 30)
# print(json.dumps(responseData))
# -------------------------------
# 인자의 설명
# host(str): 요청할 서버의 주소를 써주세요
# port(int): 요청할 서버의 포트를 써주세요 (참고로 일반적으로 ssl통신의 경우 기본포트는 443이고 일반은 80입니다)
# requestData(str): 요청할 내용을 써주세요. https://tools.ietf.org/html/rfc2616 문서를 참조해서 작성하면 됩니다
# use_ssl(boolean): ssl 통신을 하려면 True 아니면 False를 해주세요
# timeout(int): 타임아웃 시간을 써주세요. 단위는 초이며, 의미는 이 시간동안 응답이 없으면 에러를 내고 종료하게된다는 뜻이예요. 타임아웃 안걸거면 0으로해요
# -------------------------------
# 리턴값의 설명
# dict 형태로 리턴하며 그냥 보면 알거예요
# -------------------------------
# 사용을 위해선 모듈을 임포트해줘야해요 
# import ssl
# from socket import *
# -------------------------------
def kst_http_by_socket(host, port, requestData, use_ssl, timeout):
	bufferSizeToRead = 1024 # 네트워크 스트림에서 읽어들일 버퍼의 크기
	newLineChar = '\r\n' # CRLF 헤더부분에서의 개행문자
	headBodyBoundary = newLineChar*2 # 응답에서 헤더부분과 바디부분을 구분지어주는 문자
	boundaryLength = len(headBodyBoundary)
	responseData = {} # 네트워크를 통해 받은 응답을 정리해서 여기에 담아 리턴해줄거예요
	responseData['error'] = ''
	try:
		# 연결을 수립하고 요청을 보냅니다
		socketPointer = socket(AF_INET, SOCK_STREAM)
		if timeout > 0:
			socketPointer.settimeout(timeout)
		if use_ssl == False:
			# HTTP 방식의 통신
			socketPointer.connect((host, port)) # 연결을 맻고
			socketPointer.send(requestData) # 요청을 보냅니다
		else:
			# HTTPS 방식의 통신
			socketPointer = ssl.wrap_socket(socketPointer)
			socketPointer.connect((host, port)) # 연결을 맻고
			socketPointer.write(requestData) # 요청을 보냅니다

		# 일단 헤더부터 받도록 합니다.
		# 헤더가 끝나는 부분인 headBodyBoundary 이 나오는 부분까지를 일단 스트림에서 읽어가지고 올겁니다
		received_stream = [] # 응답값을 담을겁니다
		loop = True
		loopCount = 0
		preventiveInfiniteLimit = 1024*100
		while loop:

			# 1바이트씩 읽어서 받아둡니다
			charData = socketPointer.recv(1)
			if charData:
				received_stream.append(charData)

			# 받은 데이터의 길이가 headBodyBoundary 의 길이보다 클때부터
			# 현재시점까지 받아온 데이터가 헤더의 전체 데이터인지를 계속 체크하는 과정을 거칩니다
			# 만약 헤더를 다 받아온 경우라면 while 반복을 종료합니다.
			receivedLength = len(received_stream)
			if receivedLength >= boundaryLength:
				newlineCounter = 0
				for no in range(0, boundaryLength):
					compareCharAsc = received_stream[receivedLength-(boundaryLength-no)]
					compareCharDesc = received_stream[receivedLength-(no+1)]
					if headBodyBoundary[no] == compareCharAsc or headBodyBoundary[no] == compareCharDesc:
						newlineCounter=newlineCounter+1
				if newlineCounter == boundaryLength:
					loop = False

			# 혹시 모를 무한루프 방지를 위한 코드
			# 만약 타임아웃을 걸엇다면, 무한루프는 안들어갈거예요
			loopCount = loopCount+1
			if preventiveInfiniteLimit < loopCount:
				loop = False

		# 수신이 완료된 헤더부분
		responseData['head'] = "".join(received_stream)

		# 헤더부분에서 바디부분의 길이를 알아온다
		contentLength = None
		keyName = 'Content-Length'
		splited = responseData['head'].split(newLineChar)
		for data in splited:
			if contentLength==None and (data.lower()).find((keyName).lower()+':') == 0:
				contentLength = data[len(keyName+':'):].strip()

		# 만약 Content-Length 가 정의되지 않은 Chunked Encoding 방식의 응답이라면 바디를 읽어오는것을 포기한다.
		if not contentLength == None:
			contentLength = int(contentLength) # 받아와야 할 바디의 길이다
			received_length = 0
			received_stream = [] # 바디를 받아서 넣을 배열을 준비한다

			# 바디부분을 읽어온다.
			loop = True
			while loop:
				# 읽어올 사이즈를 정한다
				bufferSizeToReadForThisTime = bufferSizeToRead
				if  contentLength - received_length < bufferSizeToReadForThisTime:
					bufferSizeToReadForThisTime = contentLength - received_length

				# 읽어올 사이즈가 0보다 크다면 읽어온다
				if bufferSizeToReadForThisTime > 0:

					# 데이터를 스트림에서 읽어온다.
					dataRecieved = socketPointer.recv(bufferSizeToReadForThisTime)
					if dataRecieved:
						# 정상적으로 읽어왔다면 배열에 쌓고 받아온 사이즈를 더해준다.
						received_stream.append(dataRecieved)
						received_length = received_length + len(dataRecieved)
					else:
						# 만약 실패했다면?
						# 힘내라고 응원해준다.
						you_can_do_it = True
				else:
					if contentLength == received_length:
						# 바디를 다 읽으면 여기로 들어와서 루프를 종료하는거다
						# 여기에 들어오지 않는 경우도 있을수 있을까? 무한루프에 주의해야겠다.
						# 만약 타임아웃을 걸엇다면, 무한루프는 안들어갈거예요
						loop = False
			responseData['content'] = "".join(received_stream)
			responseData['content_length'] = contentLength

			# 소켓을 닫는다
			if socketPointer != None:
				socketPointer.close()
		else:
			raise Exception('unknown content length')
	except BaseException as error:
		responseData['error'] = "%s"%error

	# 결과를 리턴해준다 결과값의 형태는 dict
	return responseData


댓글
댓글쓰기 폼
공지사항
Total
29,228
Today
22
Yesterday
27
링크
«   2018/08   »
      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  
글 보관함