Recently in a project to do need to use the WebSocket technology introduced in the HTML5, originally thought should be easy to fix, who knows in the real development after the discovery of a lot of trouble, although we are a front-end development and design of the team, And as a second-hand program apes are not to be seen for a long time, but in order to have the same needs of friends less detours, I decided to put the implementation method affixed to this place.
The basic concept of websocket, Wikipedia explained very clearly, and the Internet can be a lot of search, here is a little bit, directly to the point.
The problem first has a prerequisite, is to use Python to implement this server, if there is no limit to the specific language, recommended that everyone preferred node.js a third-party library: Socket.io, very good, 10 minutes without injections do not take medicine to fix WebSocket server, and use JS to write back-end, I believe also can be a lot of artistic developers appetite.
But if you choose to use Python,google search results are almost useless, the most fatal problem is that the WebSocket protocol itself is a draft, so different browsers support the version of the protocol is different, Safari 5.1 is supported by the old version of the Protocol Hybi-02, Chrome 15 and Firefox 8.0 support the new version of the protocol Hybi-10, the old version protocol and the new version of the Protocol in the establishment of the communication Handshake and data transmission format requirements are different, resulting in most of the online implementation can only be applied to Safari browser, and Safari and c&f browsers can't communicate with each other.
The first step is to explain the new and old version of the WebSocket protocol handshake. Let's take a look at the structure of the handshake data sent by three different browsers:
Chrome:
Copy Code code as follows:
get/http/1.1
Upgrade:websocket
Connection:upgrade
host:127.0.0.1:1337
sec-websocket-origin:http://127.0.0.1:8000
sec-websocket-key:erwjbdvalynhvhnulgrw8q==
Sec-websocket-version:8
cookie:csrftoken=xxxxxx; Sessionid=xxxxx
Firefox:
Copy Code code as follows:
get/http/1.1
host:127.0.0.1:1337
user-agent:mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:8.0) gecko/20100101 firefox/8.0
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
accept-charset:iso-8859-1,utf-8;q=0.7,*;q=0.7
Connection:keep-alive, Upgrade
Sec-websocket-version:8
sec-websocket-origin:http://127.0.0.1:8000
sec-websocket-key:1t3f81iaxnize2txqwv+8a==
Cookie:xxx
Pragma:no-cache
Cache-control:no-cache
Upgrade:websocket
Safari:
Copy Code code as follows:
get/http/1.1
Upgrade:websocket
Connection:upgrade
host:127.0.0.1:1337
origin:http://127.0.0.1:8000
cookie:sessionid=xxxx; Calview=day; daycurrentdate=1314288000000
SEC-WEBSOCKET-KEY1:CV ' p1* 42#7 ^9}_ 647 08{
Sec-websocket-key2:o8 415 8x37r A8 4
;" ######
As you can see, Chrome and Firefox implement the new protocol, so only a "Sec-websocket-key" header is transmitted for the server to generate a handshake token, but following the old version of Safari's data, there are two Key: " Sec-websocket-key1″ and "Sec-websocket-key2″, so the service side needs to make a judgment when creating a handshake token. First let's look at the Safari,token generation algorithm using the old version protocol as follows:
Take out all the numeric characters in the Sec-websocket-key1 to form a number, this is 1427964708, and then divide by the number of spaces in the Key1, which seems to be 6 spaces, gets a numeric value, retains the numeric integer, and gets the numeric value N1 To Sec-websocket-key2, get a second integer N2, connect N1 and N2 to Big-endian character sequences, and then connect to another Key3 to get an original sequence Ser_key. So what is Key3? You can see in Safari sent over the handshake request Finally, there is a 8-byte strange string ";" ###### ", this is Key3. Back to Ser_key, the original sequence to do MD5 calculate a 16-byte long Digest, this is the old version of the protocol required token, and then the token attached to the handshake message finally sent back to the client, you can complete the handshake.
The new version of the protocol to generate token is relatively simple: first of all, Sec-websocket-key and a series of fixed uuid "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" do stitching, and then the concatenation of the string do SHA1 encryption , after getting digest, do a base64 code, you can get token.
Also note that the new version and the old version of the handshake protocol back to the client data structure is different, in the attachment of the server source code is very clear, to see what can be understood.
Completing the handshake is only half the function of the WebSocket server and now only guarantees that the server will be able to link to two versions of the browser, but if you try to send a message from Chrome to Safari, you will find that safari cannot receive it. The reason for this result is that the data framing structure of the two versions of the protocol is different, that is, after the handshake is established, the data structure of the client sends and receives is not the same.
The first step is to obtain the original data that the client sends over the different versions of the protocol. The older version of the protocol is simpler, in effect adding a ' \x00′ ' in front of the original data, plus a ' \xff ' at the back, so if Safari's client sends a string ' test ', it actually websocket Server received the data is: ' X00test\xff ', so only need to peel off the two characters on it.
The trouble is that the new version of the protocol data, according to the version of draft, Chrome and Firefox sent over the data message consists of the following parts: First is a fixed byte (1000 0001 or 1000 0002), this byte can not be ignored. Trouble is the second byte, where the second byte is assumed to be 1011 1100, first of all this byte must be 1, which means that this is a "masked" bit, the remaining 7 0/1 bits can calculate a number, such as the remaining 011 1100, calculated to be 60, This value needs to be judged as follows:
If the value is between 0000 0000 and 0111 1101 (0 ~ 125), then the value represents the length of the actual data, and if that number is exactly 0111 1110 (126), then the next 2 bytes represent the true data length; If this number is exactly 0111 1 111 (127), then the next 8 bytes represent the length of the data.
With this judgment, it is possible to know that the byte representing the length of the data ends at the first few, for example, we give example 60, this value is between 0~125, so the second byte itself represents the length of the original data (60 bytes), so from the third byte, we can catch 4 bytes, This string of bytes is called "Masks" (mask), the data after the mask is the actual ... Brother. Said it was a brother, is because this data is actually obtained after a bitwise operation based on the mask, the method of obtaining the raw data is to perform an XOR operation on each bit x of the sibling data, and the first i%4 bit of the mask, where I is the index of x in the sibling data. Look at it, look at this snippet of code and maybe you'll see:
Copy Code code as follows:
def send_data (RAW_STR):
BACK_STR = []
Back_str.append (' \x81 ')
Data_length = Len (raw_str)
If Data_length < 125:
Back_str.append (Chr (data_length))
Else
Back_str.append (Chr (126))
Back_str.append (Chr (data_length >> 8))
Back_str.append (Chr (Data_length & 0xFF))
Back_str = "". Join (BACK_STR) + raw_str
The resulting back_str can be sent to Chrome or Firefox, which uses the new protocol.
At this point, this simple WebSocket server is complete, can be compatible with the old version of the Protocol and the new version of the socket connection, and the different versions of the data transfer. The server's source code please click here to download, it is necessary to note that the twisted framework used to run the TCP service, the code is not very good, only for your reference.