웹소켓이란?
Transport protocol의 일종으로 쉽게 이야기하면 웹버전의 TCP 또는 Socket이라고 이해하면 된다.
WebSocket은 서버와 클라이언트 간에 Socket Connection을 유지해서 언제든 양방향 통신 또는 데이터 전송이 가능하도록 하는 기술이다.
Real-time web application구현을 위해 널리 사용되어지고 있다. (SNS어플리케이션, LoL같은 멀티플레이어 게임, 구글 Doc, 증권거래, 화상채팅 등)
웹소켓을 제공하는 websockets, tornado 등의 모듈이 있지만 websockets의 경우 python 3버전에서만 돌아가는 제약이 있었고
tornado의 경우 커스터마이징 하기가 매우 어려웠습니다.
어쩌다 보니 python2.7만을 사용해야 하는 상황이여서 python 2.7 에서 돌아갈 수 있는 웹소켓을 찾다가 아래와 같은 소스를 발견하여 커스터마이징 하여 사용중입니다.
아래는 원본 소스입니다.
[소스출처] http://lanian-windrunner.blogspot.com/2013/08/python-websocket-server.html?m=1
[웹소켓 클라이언트]
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<title>채팅</title>
<div class="container">
<br>
<div id="chatArea" style="height: 545px; -ms-overflow-y: auto;"></div>
<div class="form-inline">
<input class="form-control" id="msg" onkeypress="if( event.keyCode==13 ){sendMsg();}" type="text">
<input class="btn btn-default" onclick="sendMsg()" type="button" value="전송">
</div>
</div>
<script type="text/javascript">
var ws = 0
document.addEventListener("DOMContentLoaded", function(){
if (ws != 0 && ws.readyState != 1) return;
if ("WebSocket" in window) {
// alert("WebSocket is supported by your Browser!");
ws = new WebSocket("ws://localhost:8008");
ws.onopen = function() {
ws.send('open');
console.log("connected");
};
ws.onmessage = function(event) {
var data = event.data.replace(/</gi, "<");
data = data.replace(/>/gi, "> ");
$("#chatArea").append(data + "<br />");
$("#chatArea").scrollTop($("#chatArea")[0].scrollHeight);
if(data == 'end'){
ws.close();
};
};
window.onbeforeunload = function(event) {
ws.send('off')
ws.close();
};
}
else {
console.log("WebSocket NOT supported by your Browser!");
}
});
function sendMsg() {
ws.send(document.getElementById("msg").value);
document.getElementById("msg").value = '';
}
function ChatAreaResize() {
var div2 = document.getElementById('chatArea');
// div2.style.width = window.innerWidth - 200 + 'px';
div2.style.height = window.innerHeight -200 + 'px';
}
window.onload = function() {
ChatAreaResize();
// 브라우저 크기가 변할 시 동적으로 사이즈를 조절해야 하는경우
window.addEventListener('resize', ChatAreaResize);
}
</script>
[웹소켓 서버]
import socket
import re
import hashlib
import base64
import threading
import struct
def send(client, msg):
data = bytearray(msg.encode('utf-8'))
if len(data) > 126:
data = bytearray([b'\x81', 126]) + bytearray(struct.pack('>H', len(data))) + data
else:
data = bytearray([b'\x81', len(data)]) + data
client.send(data)
def recv(client):
first_byte = bytearray(client.recv(1))[0]
FIN = (0xFF & first_byte) >> 7
opcode = (0x0F & first_byte)
second_byte = bytearray(client.recv(1))[0]
mask = (0xFF & second_byte) >> 7
payload_len = (0x7F & second_byte)
if opcode < 3:
if (payload_len == 126):
payload_len = struct.unpack_from('>H', bytearray(client.recv(2)))[0]
elif (payload_len == 127):
payload_len = struct.unpack_from('>Q', bytearray(client.recv(8)))[0]
if mask == 1:
masking_key = bytearray(client.recv(4))
masked_data = bytearray(client.recv(payload_len))
if mask == 1:
data = [masked_data[i] ^ masking_key[i % 4] for i in range(len(masked_data))]
else:
data = masked_data
else:
return opcode, bytearray(b'\x00')
return opcode, bytearray(data)
def handshake(client):
request = client.recv(2048)
m = re.match('[\\w\\W]+Sec-WebSocket-Key: (.+)\r\n[\\w\\W]+\r\n\r\n', request)
key = m.group(1) + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
response = "HTTP/1.1 101 Switching Protocols\r\n" + \
"Upgrade: websocket\r\n" + \
"Connection: Upgrade\r\n" + \
"Sec-WebSocket-Accept: %s\r\n" + \
"\r\n"
r = response % ((base64.b64encode(hashlib.sha1(key).digest()),))
client.send(r)
def handle_client(client):
handshake(client)
try:
while 1:
opcode, data = recv(client)
if opcode == 0x8:
print 'close frame received'
break
elif opcode == 0x1:
if len(data) == 0:
break
msg = data.decode('utf-8', 'ignore')
send(client, msg)
print(msg)
else:
print 'frame not handled : opcode=' + str(opcode) + ' len=' + str(len(data))
except Exception as e:
print str(e)
print "disconnected"
client.close()
def start_server(port):
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', port))
sock.listen(5)
while True:
print 'Waiting for connection on port ' + str(port) + ' ...'
client, addr = sock.accept()
print 'Connection from: ' + str(addr)
threading.Thread(target=handle_client, args=(client,)).start()
start_server(8008)
소켓연결은 8008번 포트를 사용했습니다.
[실행]
임의의 값을 넣고 전송버튼을 누르면 웹소켓 서버를 통해 웹 접속한 웹페이지로 입력한 내용이 그대로 전달되는 Echo기능의 웹소켓입니다.
[웹소켓 응용 (Cuckoo SandBox)]
위 소스코드를 잘 이용하면 이처럼 로그정보를 실시간으로 보여줄 수도 있습니다.
'프로그래밍 > Python' 카테고리의 다른 글
Python csv, txt 파일 병합, 내용 합치기 (738) | 2020.08.13 |
---|---|
Python 국가명, 국가코드, 위도, 경도 조회(Geoip, mmdb) (952) | 2020.08.13 |
Python 실행파일 만들기 (1916) | 2020.08.12 |
Python Thread(쓰레드) + Heapq(힙큐) + Globals()응용 (313) | 2020.08.11 |
Python Thread(쓰레드) + Queue(큐) 응용 (310) | 2020.08.10 |