1. 程式人生 > >python之WebSocket的開發

python之WebSocket的開發

## websocket.py

import socket
import struct
import hashlib,base64
import threading,random
#執行緒,套接字,雜湊表,隨機數

#存放連結客戶fd,元組
connectionlist = {}

#傳送指定的訊息
def sendMessage(message):
	#對全域性變數的引用
	global connectionlist
	#向客戶端集合中每個成員傳送訊息
	# %:字串合成
	# ,:字串疊加
	for connection in connectionlist.values():
		connection.send("\x00%s\xFF" % message)

#刪除連線,從集合中刪除連線物件item(建立一個連線追加到連線中)
def deleteconnection(item):
	global connectionlist
	del connectionlist['connection'+item]

#定義WebSocket物件(基於執行緒物件)
class WebSocket(threading.Thread):
	#
	def __init__(self,conn,index,name,remote, path="/"):
		#初始化執行緒
		threading.Thread.__init__(self)
		#初始化資料,全部儲存到自己的資料結構中self
		self.conn = conn
		self.index = index
		self.name = name
		self.remote = remote
		self.path = path
		self.buffer = ""
	
	#執行執行緒
	def run(self):
		#Log輸出,套接字index啟動
		print 'Socket%s Start!' % self.index
		headers = {}
		#Socket是否握手的標誌,初始化為false.
		self.handshaken = False
		#迴圈執行如下邏輯
		while True:
			#如果沒有進行握手
			if self.handshaken == False:
				#Log輸出,Socket x開始與遠端客戶y進行握手過程
				print 'Socket%s Start Handshaken with %s!' % (self.index,self.remote)
				#從客戶端接受1kb資料,存放到buffer中
				self.buffer += self.conn.recv(1024)
				#如果接受資料中有\r\n\r\n的標誌
				if self.buffer.find('\r\n\r\n') != -1:
					#按照這種標誌分割一次,結果為:header data(網頁的解析)
					#再對header 和 data部分進行單獨的解析
					header, data = self.buffer.split('\r\n\r\n', 1)
					#對header進行分割後,取出後面的n-1個部分
					for line in header.split("\r\n")[1:]:
						#逐行的解析Request Header資訊(Key,Value)
						key, value = line.split(": ", 1)
						#然後存放在一個Hash表中,方便訪問
						headers[key] = value
					#人為定義Location的item的資訊
					#構造location:ws://localhost/path ?
					headers["Location"] = "ws://%s%s" %(headers["Host"], self.path)
					print "Location:",headers["Location"]
					print "headers:",headers
					#取出其中兩項資訊key1 key2
					#key1 = headers["Sec-WebSocket-Key1"]
					
					#key2 = headers["Sec-WebSocket-Key2"]
					#Header解析完畢後,分析data部分
					#如果data部分長度小於8的話,從客戶端那邊接續接受資料使得data變為8位元組
					if len(data) < 8:
						data += self.conn.recv(8-len(data))
					#將data的資料資訊存放為key3變數(前面的第一個八個位元組為key3)
					#key3 = data[:8]
					#將data後面的資料作為buffer進行存放
					self.buffer = data[8:]
					#根據key1,key2,key3產生token?
					#根據客戶的key1、key2、8位元組的關鍵字
					#產生一個16位的安全祕鑰
					#old-Protocol
					#token = self.generate_token(key1, key2, key3)
					#new Protocol
					token = generate_token_2(self, key)
					#握手過程,伺服器構建握手的資訊,進行驗證和匹配
					#Upgrade: WebSocket 表示為一個特殊的http請求,請求目的為從http協議升級到websocket協議
					handshake = '\
					HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
					Upgrade: WebSocket\r\n\
					Connection: Upgrade\r\n\
					Sec-WebSocket-Origin: %s\r\n\
					Sec-WebSocket-Location: %s\r\n\r\n\
					' %(headers['Origin'], headers['Location'])
					#服務端傳送握手資料 & 根據Key產生的Token值
					self.conn.send(handshake+token)
					#這個操作之後才設定為握手狀態
					self.handshaken = True
					#Log輸出狀態:套接字x與客戶y握手成功。
					print 'Socket%s Handshaken with %s success!' % (self.index,self.remote)
					#向全部連線客戶端集合傳送訊息,(環境套接字x的到來)
					sendMessage('Welcome, '+self.name+' !')
			else:
				#如果已經握手
				#從客戶端讀取64位元組的資料
				self.buffer += self.conn.recv(64)
				#如果資料中存在FF的標誌,則按照此標誌進行分解
				if self.buffer.find("\xFF")!=-1:
					#分解方式啥含義??
					s = self.buffer.split("\xFF")[0][1:]
					#如果訊息是'終止',則列印Socket退出
					if s=='quit':
						print 'Socket%s Logout!' % (self.index)
						#全體同志Socket退出的狀態(進行GUI更新準備)
						sendMessage(self.name+' Logout')
						#同時刪除socket連線集合
						deleteconnection(str(self.index))
						#同時關閉對應的WebSocket連線(多執行緒關係)
						self.conn.close()
						break
					else:
						#否則輸出,Socket x收到客戶端的訊息 y
						print 'Socket%s Got msg:%s from %s!' % (self.index,s,self.remote)
						#向全體的客戶端輸出連線的資訊
						sendMessage(self.name+':'+s)
					#Buffer資訊再一次的清空
					self.buffer = ""

def generate_token_2(self, key):
        key = key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        ser_key = hashlib.sha1(key).digest()
        return base64.b64encode(ser_key)
        	
def generate_token(self, key1, key2, key3):
	#list/tuple(ls)-list元組相互轉換
	#這句話沒看懂,如何理解key是否為數字
	num1 = int("".join([digit for digit in list(key1) if digit.isdigit()]))
	#解析key1中空格的個數
	spaces1 = len([char for char in list(key1) if char == " "])
	#解析後number2物件
	num2 = int("".join([digit for digit in list(key2) if digit.isdigit()]))
	#統計空格的個數?安全性驗證
	spaces2 = len([char for char in list(key2) if char == " "])
	#按照一定的格式進行打包,然後進行網路傳輸(格式可以自己進行預訂)
	#struck.pack:http://blog.sina.com.cn/s/blog_4b5039210100f1tu.html
	combined = struct.pack(">II", num1/spaces1, num2/spaces2) + key3
	#對打包的值進行md5解碼後,並返回二進位制的形式
	##hexdigest() 為十六進位制值,digest()為二進位制值
	#處理MD5: http://wuqinzhong.blog.163.com/blog/static/4522231200942225810117/
	return hashlib.md5(combined).digest()
	
#建立WebSocket伺服器物件()
class WebSocketServer(object):
	#初始化時,socket為空
	def __init__(self):
		self.socket = None
	#開啟操作
	def begin(self):
		#伺服器盡心啟動Log輸出
		print 'WebSocketServer Start!'
		#建立TCP的套接字,監聽IP、Port
		#這裡可以自己進行設定,最多可以收聽50個請求客戶
		self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		ip = 'localhost'
		port = 8080
		
		#print "WebServer is listening %s,%d" % ip,port
		#self.socket.bind(ip,port)
		self.socket.bind((ip,port))
		self.socket.listen(50)
		#宣告全域性連線集合
		global connectionlist
		
		i=0
		while True:
			#伺服器響應請求,返回連線客戶的資訊(連線fd,客戶地址)
			connection, address = self.socket.accept()
			#address資訊中的第1個字串為username物件
			username=address[0]
			#根據連線的客戶資訊,建立WebSocket物件(本質為一個執行緒)
			#連線後sockfd,連線index,使用者名稱,地址
			newSocket = WebSocket(connection,i,username,address)
			#執行緒啟動
			newSocket.start()
			#更新連線的集合(hash表的對應關係)-name->sockfd
			connectionlist['connection'+str(i)]=connection
			i = i + 1

if __name__ == "__main__":
	server = WebSocketServer()
	server.begin()

## client.html頁面
<html>
<head>
<title>WebSocket</title>

<style>
 html,body{font:normal 0.9em arial,helvetica;}
 #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
 #msg {width:330px;}
</style>

<script>
var socket;

function init(){
  var host = "ws://127.0.0.1:8080/";
  try{
    socket = new WebSocket(host);
    socket.onopen    = function(msg){ ; };
    socket.onmessage = function(msg){ log(msg.data); };
    socket.onclose   = function(msg){ log("Lose Connection!"); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("Message can not be empty"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); } catch(ex){ log(ex); }
}

window.onbeforeunload=function(){
	try{ 
		socket.send('quit'); 
		socket.close();
		socket=null;
	}
	catch(ex){ 
		log(ex);
	}
};


function $(id){ return document.getElementById(id); }
function log(msg){ $("log").innerHTML+="<br>"+msg; }
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>

</head>



<body onload="init()">
 <h3>WebSocket</h3>
 <br><br>
 <div id="log"></div>
 <input id="msg" type="textbox" onkeypress="onkey(event)"/>
 <button onclick="send()">傳送</button>
</body>

</html>


## 用法說明

websocket.py

訪問:http://localhost:8080/client.html

相關推薦

pythonWebSocket開發

## websocket.py import socket import struct import hashlib,base64 import threading,random #執行緒,套接字,雜湊表,隨機數 #存放連結客戶fd,元組 connectionlist =

python部署開發環境

Python 一、部署開發環境(windows) 1.建立工作區域   Python     -Package        -Program   &nbs

python美--開發原則篇

上一篇詳細的介紹了python的幾個有深度的知識點,本篇我想再昇華到一個高度,python開發中到底要遵守哪些原則。 1 可讀性: 我把可讀性放在python原則第一位,是因為python太大的靈活性,導致了每個人的程式碼風格天馬行空。像Java那種語言規定的很嚴謹,雖

pythonWebSocket協議

一、WebSocket理論部分 1、websocket是什麼 Websocket是html5提出的一個協議規範,參考rfc6455。 websocket約定了一個通訊的規範,通過一個握手的機制,客戶端(瀏覽器)和伺服器(webserver)之間能建立一個類似tcp的連線

Python路57-前端快速開發

python適用於全棧BootStrapcss、js學習BootStrap規則1.響應式@media<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title&

Python全棧開發4、內置函數、文件操作和遞歸

開發 hang mon alien yun alpha err fdm ax1 %E5%AD%97%E8%8A%82%E5%BA%8F%E8%BD%AC%E6%8D%A2%E4%B8%8E%E7%BB%93%E6%9E%84%E4%BD%93%E4%BD%8D%E5%9F%

python全棧開發從入門到放棄函數基礎

*args 才會 沒有 pri 關鍵字 args none 結果 類型 1、為什麽要用函數#1.避免代碼重用#2.提高代碼的可讀性 2、函數的定義def 函數名(參數1,參數2): ‘‘‘函數註釋‘‘‘ print("函數體") return "返回值"

python全棧開發從入門到放棄裝飾器函數

def return app 不改變 art sdl 兩個 time() 必須 1、函數名可以當作函數的參數 1 import time 2 def timmer(func): 3 #函數名可以當做函數的參數 4 def inner(): 5

python全棧開發從入門到放棄常用模塊和正則

imp 管理 gin idt 由於 說明 多次 mar style 什麽是模塊? 常見的場景:一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。 但其實import加載的模塊分為四個通用類別:    1 使用python編寫

python全棧開發從入門到放棄socket網絡編程基礎

windows lis timeout 標準 網站 入門 make 取數 exce 網絡編程基礎 一 客戶端/服務器架構 1.硬件C/S架構(打印機) 2.軟件C/S架構   互聯網中處處是C/S架構   如黃色網站是服務端,你的瀏覽器是客戶端(B/S架構也是C/S架構的一

python全棧開發從入門到放棄socket並發編程IO模型

map 超時 sting mon recv style 好的 exceptio 得到 一 IO模型介紹 同步(synchronous) IO和異步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分別是什麽,到底有什

python全棧開發Python基礎(1)

python 基礎一、 基礎知識 python的運行方式有兩種: 第一種通過交互式的運行方式,通過 "開始"—>"所有程序" —> "python3.x" —>"IDLE" 運行。 第二種是我們寫好的Pyth

python路--day15---軟件開發目錄規範

python 配置 文件 inf 功能 介紹 b- info bubuko 軟件開發目錄規範 bin--啟動文件 conf--配置文件 core--核心代碼 db--數據文件 lib--常用功能代碼 log--日誌文件 readme--軟件介紹 python之路--da

Python 網絡編程——SOCKET開發

top mage pan .so byte exc dto xxd 2.4.1 一、預備知識 對於我們,主要掌握5層協議就行。 物理層:  轉成二進制數序列數據鏈路層:   形成統一的協議:Internet協議  包括數據頭(18個字節,前6個字節原地址,中間6個字節為目

Python運維開發路》 內置函數&數據結構(六)

ima oob 更新 .com 常用 嵌套列表 數據結構 例子 func 一、Python內置函數詳解 Python內置函數圖解 您也可以訪問(裏面有各種例子):https://docs.python.org/3/library/functions.html#nex

python全棧開發

方法 blog 數據 數據類型 框架 blank 全棧 jquery基礎 one 一、Python基礎 python簡介 python數據類型(數字\字符串\列表) python數據類型(元組\字典) 二、Python文件操作&函數 三、Pytho

Python全棧開發路 【第八篇】:面向對象編程設計與開發(2)

ssi pen 解析 執行 示例 動態 類型 put 所有 一、繼承與派生 什麽是繼承? 繼承指的是類與類之間的關系,是一種什麽是什麽的關系,繼承的功能之一就是用來解決代碼重用問題。 繼承是一種創建新的類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可以成

Python全棧開發路 【第十八篇】:Ajax技術

加載 完全 三種 請求 技術 當前頁 n) let 保存 Ajax技術 Ajax = 異步 JavaScript 和 XML。 Ajax 是一種在無需重新加載整個網頁的情況下,能夠更新部分網頁的技術。 1、jQuery的load()方法 jQuery load()方法是簡單

python模塊、包的導入過程和開發規範

而是 port code 導入模塊 聲明 for 命名 bubuko 根據 摘要:導入模塊、導入包、編程規範 以My_module為例,My_module的代碼如下: __all__ = [‘name‘,‘read‘] print(‘in mymod

Python全棧開發基礎語法

deepcopy 分配 有符號 數字 桌面應用 存儲 tex 算術運算符 多行 No1. Python語言介紹 詳情見百度百科。。。 No.2 Python是一門怎麽樣的語言 詳情在百度百科。。。 No3. Python能做什麽 網絡應用、桌面應用、系統運維、機器學習、科學