君は春の中にいる、かけがえのない春の中にいる.

你驻足于春色中,于那独一无二的春色之中.

Js-Python实现socket通信

0x00 前言

Socket,一种开销很小的实现程序之间通信的方式。不同的编程语言几乎都有自己实现Socket的库和方法,建立在Socket基础上的许多通信协议也都广泛运用于各种框架中,之前在博客分享的Hpfeeds协议实际上就是建立在Socket的基础上。

不同编程语言之间也经常使用Socket来传递消息,这样可以避免不同语言之间的嵌套调用。如题,这里我们需要在Python和Js之间实现一个消息传递。

0x01 实现

最初查找资料的时候,许多博客中都提到了说,Js本身是不能够实现Socket的,而常用的方法实际上是利用Flash的Socket来进行通信,然后通过Js获得Flash的响应事件来传递数据。为此有一个专门的Js库叫Aflax。

同时,另一种更新的方法是使用Html5协议中的WebSocket来实现浏览器和服务器的通信。

关于WebSocket的具体协议就不在这里详述了,毕竟我也没有认真去看。

实现WebSocket通信的方法也有很多种,网上容易查到的是使用Node.js的实现方法
https://github.com/SushisMakis/WebSocket

这里,作者提供个一个Python实现的服务器

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import struct
import SocketServer
from base64 import b64encode, b64decode
from hashlib import sha1
from mimetools import Message
from StringIO import StringIO

clients = [];
class WebSocketsHandler(SocketServer.StreamRequestHandler):
magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

def setup(self):
SocketServer.StreamRequestHandler.setup(self)
print "connection established", self.client_address
self.handshake_done = False

def handle(self):
while True:
if not self.handshake_done:
self.handshake()
clients.append(self)
else:
try:
self.read_next_message()
except:
self.request.close()
return

def read_next_message(self):
length = ord(self.rfile.read(2)[1]) & 127
if length == 126:
length = struct.unpack(">H", self.rfile.read(2))[0]
elif length == 127:
length = struct.unpack(">Q", self.rfile.read(8))[0]
masks = [ord(byte) for byte in self.rfile.read(4)]
decoded = ""
for char in self.rfile.read(length):
decoded += chr(ord(char) ^ masks[len(decoded) % 4])
self.on_message(decoded)

def send_message(self, data, fin=True, opcode=1, masking_key=False):
header = struct.pack('!B', ((fin << 7) | (0 << 6)| (0 << 5)| (0 << 4)| opcode))
if masking_key:
mask_bit = 1 << 7
else:
mask_bit = 0
length = len(data)
if length < 126:
header += struct.pack('!B', (mask_bit | length))
elif length < (1 << 16):
header += struct.pack('!B', (mask_bit | 126)) + struct.pack('!H', length)
elif length < (1 << 63):
header += struct.pack('!B', (mask_bit | 127)) + struct.pack('!Q', length)
body = data
self.request.send(bytes(header + body))

def handshake(self):
data = self.request.recv(1024).strip()
headers = Message(StringIO(data.split('\r\n', 1)[1]))
if headers.get("Upgrade", None) != "websocket":
return
print 'Handshaking...'
key = headers['Sec-WebSocket-Key']
digest = b64encode(sha1(key + self.magic).hexdigest().decode('hex'))
response = 'HTTP/1.1 101 Switching Protocols\r\n'+'Upgrade: websocket\r\n'+'Connection: Upgrade\r\n'
response += 'Sec-WebSocket-Accept: %s\r\n\r\n' % digest
self.handshake_done = self.request.send(response)

def on_message(self, message):
text = b64decode(message)
print text
for client in clients:
if client.client_address != self.client_address:
client.send_message(b64encode(text))

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass

if __name__ == "__main__":
server = ThreadedTCPServer(("localhost", 9999), WebSocketsHandler)
server.serve_forever()

我们使用其中的这段代码作为服务器。

用这段Js作为浏览器端的接收Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// SmWebSocket(url,port,b64encode,fonMessage, ...)
var smWs = new SmWebSocket("localhost",9999,true,handle_recv);
smWs.open();
var myDictObjectProtocol = {"p1top2": handle_transit}; // {id : function, ... }
var p = new Protocol(myDictObjectProtocol,"|");

function handle_recv(event)
{

var data = event.data;
data = Base64.decode(data);
alert(data);
p.handleMessage(data);
}

function handle_transit(mess){
document.getElementById("data_in").innerText = mess;
}

这里作者只实现了浏览器发送到服务器以及浏览器之间的交互,所以现在我们还缺一个Python发送的程序,我最初希望直接使用Pyhton的Socket来实现一个端口发送,但是最后发现每次发送的程序都被服务器拒绝了,所以我又找到了一个Python实现的WebSocket库,当然这个和Pip下载的WebSocket库不同。

https://github.com/liris/websocket-client

我修改了其中编码的部分,以和之前的程序适应

1
2
3
4
5
6
7
8
9
10
11
from __future__ import print_function
import websocket
import base64

if __name__ == "__main__":
#websocket.enableTrace(True)
ws = websocket.create_connection("ws://localhost:9999")
data = "p1top2|Hello World"
data = base64.b64encode(data)
ws.send(data)
ws.close()

要求的库直接Clone上面项目里的即可,这样,我们就实现了通过Python和Js进行Socket通信的程序。