웹을 이해하는 관점에서 간단하게 볼 것이다.
웹서버를 간단하게 만들 것이다.
Application -------------Socket------------ Transport
socket: 데이터를 보내고 받는 표준화된 API
방식: 실제로 통신하는 것은 소켓들이 하며, cocoatalk 자체는 외부 네트워크와 아무런 정보도 주고받지 않는다.
Web Service
- 웹 서버는 웹 클라이언트로부터 전송되어 오는 서비스 요청에 대한 결과인 HTML문서를 해당 웹 클라이언트에 제공
- 웹브라우저: HTML로 작성된 하이퍼텍스트 문서를 해석하여 화면에 출력
- 가장 대표적인 웹 서버로 아파치 사용
- 안정성, 확장성을 고려한 OpenSource 프로젝트
HTTP Protocol
HyperText Transfer Protocol
- 웹에서 정보를 주고 받을 수 있는 가장 기본적인 프로토콜(1996)
- HTML 포맷을 사용한 text 및 멀티미디어 자료 공유
- TCP(80번port) 프로토콜을 사용한 응용 계층
- http:로 시작하는 URL을 통해 접속
- 대규모 접속을 지원하기 위한 접속을 유지하지 않는 stateless 프로토콜
- 클라이언트와 서버 사이에 이루어지는 요청/응답 프로토콜
요청-응답으로, 응답은 무조건 html이다.
HTTP Method (GET/POST)
- 웹서버에 요청하는 방식
- 웹서버에 클라이언트의 data를 전송하기 위한 전송 방법
- GET과 POST는 전달하는 방식에서 차이가 있음
- GET 방식 : URL을 통해 data 전송
- GET방식으로 전송할 수 있는 data 크기는 한계가 있음
- 전송 URL이 노출됨
http://xxx/service?name1=value1&name2=value2
GET /service?name1=value1&name2=value2
- Host : xxx
- POST 방식 : Body를 통해 data 전송
- 길이 제한이 없음
POST /service HTTP/1.1
- Host : xxx
name1=value1&name2=value2
HTML
밑에서 이어서 다루겠다.
Practice - server
- server.py
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#소켓이 꼭 TCPIP만 의미하는 것은 아니고 여러가지 통신방법이 있다.
#파라미터가 두 개 - 첫번째는 IP를 쓰겠다
#두번째는 TCP/UCP인데 STREAM은 TCP방법을 쓰겠다
server_socket.bind(('localhost', 12345)) #IP, 포트번호
server_socket.listen(0) #포트번호를 listening. 동시에 연결할 최대 소켓 갯수. 0은 automatically
#대기모드
print('listening...')
client_socket, addr = server_socket.accept() #클라이언트 접속될 때까지는 대기상태
print('accepting')
data =client_socket.recv(65535) #클라이언트 접속이 되면 데이터를 읽어들임.
#데이터는(패킷은) 최대 64k. 더 작을수도 클 수도 있는데, 크면 쪼개서 전송된다.
print('receive >> ' + data.decode()) #unicode to 한글
client_socket.send(data)
print('send data')
client_socket.close()
print('종료')
- client.py
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 12345))
print('서버접속성공')
sock.send('hello'.encode())
print('send message')
data=sock.recv(65535)
print('receive >> '+ data.decode())
print('종료')
데이터 보내기
- server.py 실행 후 client.py 실행하면
#result of server.py
listening...
accepting
receive >> hello
send data
종료
#result of client.py
서버접속성공
send message
receive : hello
종료
주로 server 다룰 것.
Simple HTTP server
- server.py
#simple http server
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 80))
server_socket.listen(0)
print('listening...')
client_socket, addr = server_socket.accept()
print('accepting')
data =client_socket.recv(65535)
print('receive >> ' + data.decode())
client_socket.close()
- 브라우저 요청 후 result
#result of server.py
listening...
accepting
receive : GET / HTTP/1.1 #http protocol version
Host: localhost
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36
Sec-Fetch-Dest: document
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: _xsrf=2|941111c3|0f22749ff2b52f9ef56937eca9661be6|1580775110; username-localhost-8889="..."; username-localhost-8888="..."
#result of browser
페이지가 작동하지 않습니다.localhost에서 전송한 데이터가 없습니다.
ERR_EMPTY_RESPONSE
GET/POST method
- form.html
<form action="http://127.0.0.1/" method=post>
<input type=text name=id> <!--name 속성이 있는 데이터만 서버로 보내-->
<input type=submit value="send">
</form>
- get method - header에 저장
listening...
accepting
receive >> GET /?id=hello HTTP/1.1
- post ,method - body에 저장
listening...
accepting
receive >> POST / HTTP/1.1
...
...
...
id=hello
HTTP
client_socket.send('HTTP/1.0 200 0K\r\n\r\nHello'.encode('utf-8'))
HTTP 규약 : HTML 버전, 에러코드, OK, 엔터코드 2개
send 함수 인풋은 무조건 byte data여야 한다.
buffer(임시메모리) : 어느정도 차면 그때 데이터를 보내. 굳이 그때그때 보내지 않아. (IOA 효율성 향상)
#html file load
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 80))
server_socket.listen(0)
print('listening...')
if True : #while True : 테스트 할 때는 1회접속이 편리
client_socket, addr = server_socket.accept()
print('accepting')
data =client_socket.recv(65535)
data = data.decode()
headers = data.split("\r\n")
filename = headers[0].split(" ")[1]
header = 'HTTP/1.0 200 0K\r\n\r\n'
file = open('.'+filename, 'rt', encoding='utf-8')
html = file.read()
client_socket.send((header+html).encode('utf-8'))
client_socket.close() #HTTP의 특성. 접속하고 끊어버린다.
cf) 아래 두 줄을 html = open('.'+filename, 'rt', encoding='utf-8').read()
로 하면 안 되더라
file = open('.'+filename, 'rt', encoding='utf-8')
html = file.read()
- file 다루기
filename = '/index.html'
text = open('.'+filename, 'rt', encoding='utf-8').read()
#2nd param: read, write, binary, textflie
print(text)
./filename
: current folder
서버사이드 프로그램: 파이썬 텍스트를 서버에 전송, 서버에서 파이썬 코드를 실행, 결과를 가져와 리턴
- 최종 test.py
#확장자 나누기 + threading
def httpprocess(client_socket) :
data =client_socket.recv(65535)
data = data.decode()
print(data)
try:
headers = data.split("\r\n")
filename = headers[0].split(" ")[1]
_, ext = os.path.splitext(filename)
group_a = ['.html', '.htm']
group_b = ['.jpg', '.jpeg', '.png', '.bmp']
if '.py' in filename:
html = subprocess.check_output(['python.exe', '.'+filename])
html = html.decode('cp949')
header = 'HTTP/1.0 200 0K\r\n\r\n'
client_socket.send((header+html).encode('utf-8'))
elif ext in group_a :
file = open('.'+filename, 'rt', encoding='utf-8')
html = file.read()
header = 'HTTP/1.0 200 0K\r\n\r\n'
client_socket.send((header+html).encode('utf-8'))
elif ext in group_b or ext == '.ico' :
client_socket.send('HTTP/1.1 200 OK\r\n'.encode())
client_socket.send("Content-Type: image/png\r\n".encode())
client_socket.send("Accept-Ranges: bytes\r\n\r\n".encode())
file = open('.' + filename, 'rb')
client_socket.send(file.read()) #binary type
file.close()
else :
header = 'HTTP/1.0 404 File Not Found\r\n\r\n'
client_socket.send((header+html).encode('utf-8'))
except Exception as e:
print(e)
client_socket.close()
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 80))
server_socket.listen(0)
print('listening...')
while True :
client_socket, addr = server_socket.accept()
client_socket.settimeout(3)
print('accepting')
t = threading.Thread(target=httpprocess, args=(client_socket,))
t.start()
python과 html coding
코드를 섞어쓰면 불편하다.
같은 파일에서 동작하도록 -> template 기능 사용
- test2.py
html = """
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<font color=red> @out</font>
</body>
</html>
"""
html = html.replace("@out", "제목출력")
#template engine에서 실시간으로 바꿔준다.
print(html)
127.0.0.1/test2.py
들어가면 제목출력
이 출력된다.
'Python > Django' 카테고리의 다른 글
Django에 Database 사용하기 (1) | 2020.02.19 |
---|---|
HTML과 python web server 구축 (0) | 2020.02.18 |
OSI 참조 모델 이론 (0) | 2020.02.18 |
AJAX를 활용한 비동기 방식 웹페이지 Django로 구동하기 (0) | 2020.02.14 |
Django 기본 (0) | 2020.02.14 |