① 如何使用python::SocketServer.socket.ssl模塊
現在記錄HTTPS服務端的編寫。
importssl,socket,time
if__name__=="__main__":
context=ssl.SSLContext(ssl.PROTOCOL_SSLv23)
#context.load_cert_chain(certfile=『key_pub.pem』,keyfile=『key_priv.pem')#可以分開定義公鑰和私鑰文件,也可以合並成一個文件
context.load_cert_chain(certfile=』cert.pem')
bindsocket=socket.socket()
bindsocket.bind(('127.0.0.1',443))
bindsocket.listen(5)
newsocket,fromaddr=bindsocket.accept()
connstream=context.wrap_socket(newsocket,server_side=True)
try:
data=connstream.recv(1024)
print(data)
buf='HiNN%f '%time.time()
buf=buf.encode()
connstream.send(buf)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
bindsocket.close()
此例沒有使用socketserver框架,目的在於測試ssl模塊的用法。
繼續,用框架實現HTTPS服務
importsocketserver,ssl,time
classMyHTTPSHandler_socket(socketserver.BaseRequestHandler):
defhandle(self):
context=ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain(certfile="cert.pem")
SSLSocket=context.wrap_socket(self.request,server_side=True)
self.data=SSLSocket.recv(1024)
print(self.data)
buf='testHTTPSServerHandler<br>%f'%time.time()
buf=buf.encode()
SSLSocket.send(buf)
if__name__=="__main__":
port=443
httpd=socketserver.TCPServer(('localhost『,port),MyHTTPSHandler_socket)
print(』httpsservingatport',port)
httpd.serve_forever()
說明:handle()函數負責所有與客戶端的通信。客戶端連接過來之後,ssl模塊載入證書,並用SSLSocket對socket進行封裝,屏蔽底層的加密通信細節。
下面再給出HTTPS文件伺服器代碼,文件訪問功能由SimpleHTTPRequestHandler實現,數據加密傳輸由ssl實現。
importsocketserver,ssl,time,http.server
classMyHTTPS_SimpleHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
defsetup(self):
print('setup')
context=ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain(certfile=『cert.pem』)
SSLSocket=context.wrap_socket(self.request,server_side=True)
self.rfile=SSLSocket.makefile('rb',self.rbufsize)
self.wfile=SSLSocket.makefile('wb',self.wbufsize)
if__name__=="__main__":
port=443
httpd=socketserver.TCPServer(("localhost",port),MyHTTPS_SimpleHTTPRequestHandler)
print('httpsservingatport',port)
httpd.serve_forever()
最後,要指出的是setup()和handle()都是在客戶端開始連接之後才被調用,從順序上來說setup()先於handle()。
② 如何增加Python打開的socket數目
首先服務端這邊的實現如下:
import socket, traceback
host = '' # Bind to all interfaces
port = 51500
# Step1: 創建socket對象
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Step2: 設置socket選項(可選)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Step3: 綁定到某一個埠
s.bind((host, port))
# Step4: 監聽該埠上的連接
while 1:
try:
message, address = s.recvfrom(8192)
print "Got data from ", address
s.sendto("Data is received succeefully.", address)
except (KeyboardInterrupt, SystemExit):
print "raise"
raise
except :
print "traceback"
traceback.print_exc()
其中,host置為空,意思是可以綁定到所有的介面和地址,無論是哪個客戶端的請求,只要是綁定到了同樣的埠,那麼伺服器就可以監聽到這個請求。
在tcp連接中,監聽客戶端的請求需要用到listen或accept函數,並有一個專門的socket和遠程極其連接。
接著使我們的客戶端實現:
import socket, sys
# Step1: 輸入host和port信息
host = raw_input('please input host name: ')
textport = raw_input('please input textport: ')
# Step2: 創建socket對象
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
port = int(textport)
except ValueError:
port = socket.getservbyname(textport, 'udp')
# Step3: 打開socket連接
s.connect((host, port))
# Step4: 發送數據
print "Enter data to transmit: "
data = sys.stdin.readline().strip()
s.sendall(data)
# Step5: 接收伺服器發過來的數據
print "Looking for replies; press Ctrl-C or Ctrl-Break to stop"
while 1:
buf = s.recv(2048)
if not len(buf):
break
sys.stdout.write(buf)
這個例子就是這么簡單,實現起來和C語言版的差不多。看來,只要是了解socket編程的,用不同的語言實現也會相差無幾。
③ Python,socket如何實現通過Server將client1的數據傳給client2。
普通情況下是不行的。但是通過線程或者進程間的數據共享,還是可以的。
提供一個思路,使用多線程socketserver,針對每一個用戶創建一個服務線程,然後在線程之間通過消息隊列,共享數據。
參考:劉江的Python教程中關於多線程和多進程的章節
④ python-socketio 文檔翻譯
教程: https://tutorialedge.net/python/python-socket-io-tutorial/
python-socketio 原文地址 ,在google瀏覽器中可以翻譯為中文去使用。
首先要搞明白幾個問題:
說明
1)第一種room是每一個單獨的客戶端都有的。(通過 session ID 可以找到)
2)第二種是應用程序自己創建的。
在下面這個方法中,如果省略掉room參數,將會自動發送給所有的連接了的客戶端。
譯文:
Python-socketio實現了一個Python Socket.IO 服務,這個服務可以單獨運行也可以綜合於一個web項目中。下面是一些它的特徵:
什麼是Socket.IO?
Socket.IO是一個基於事件的雙向通訊的傳輸協議(一般是web瀏覽器),和一個服務端。原始的客戶端和服務端組件實現是通過JavaScript寫的。
入門指南
可以使用 pip 安裝Socket.IO:
下面是一個使用 aiohttp 框架(只支持Python 3.5+)實現非同步IO的 Socket.IO server 簡單的例子:
下面是一個類似的例子,但是使用的Flask和Eventlet的例子,兼容Python2.7和3.3+:
客戶端應用必須引入 socket.io-client 庫(1.3.5版本以及以上,越高越好)。
每次客戶端連接到伺服器的連接事件處理程序調用sid(會話ID)分配給連接和WSGI環境字典。
每次客戶端連接到服務端的 conenct 事件都是由sid(session ID)分配到連接和WSGI環境字典調用的。服務端可以檢查身份認證或者其他的頭部信息去決定是否這個客戶端允許被連接。要想拒絕一個客戶端的連接,這個處理器必須返回 False 。
當客戶端發送發送一個事件給服務端,相應的事件處理器會被 sid 和這個信息調用,可以是單個或者多個參數。這個應用可以定義盡量多的如果被需要的可以被事件處理器關聯的事件。一個事件可以通過一個名稱簡單定義。
當一個客戶端連接中斷了, disconnect 事件就被調用,允許應用去執行清理工作。
服務端
Socket.IO 服務端是 socketio.Server 類的實例,他們可以被一個WSGI適用應用程序使用 socketio.Middleware 去合並:
使用 socketio.Server.on() 方法來注冊服務端的事件處理器:
對於非同步服務端來說,事件處理器可以是常規方法,或者是協程:
聊天室
因為Socket.IO是一個雙向的協議,服務端可以在任意時間發送消息給任意的連接到的客戶端。為了讓它方便去將客戶端定位到組中,應用程序可以將客戶端放入到聊天室中去,然後將消息定位到整個聊天室中。
當客戶端第一次連接,他們是被分配到他們自己的聊天室中,這個聊天是是以session ID(sid 參數會傳遞給所有的事件處理器)命名的。應用可以通過 socketio.Server.enter_room() 和 socketio.Server.leave_room() 自由地去創建聊天室和管理客戶端。客戶端可以在盡量多的房間里,也可以根據需求盡量頻繁地被拉入拉出聊天室。當他們的連接不在特別的時候,單獨的聊天室將會分配給她它們,應用程序可以自由地增加和移除客戶端從聊天室中,盡管它只要這樣做就會失去定位獨立客戶端的能力。
socketio.Server.emit() 方法會獲得一個事件名稱,一個可能是 str , bytes , list , dict 或者 tuple 類型的消息載體。當發送一個 tuple ,在其中的元素必須是上面的其他類型。元組中的元素將會被傳遞給客戶端的回調函數為多個參數。定位一個個人客戶端,客戶端的 sid 將會被給一個聊天室(假設這個應用沒有修改這些初始的聊天室)。定位所有的連接的客戶端們,這個聊天室參數將會被觸發。
通常在聊天室中當廣播一個消息到一個用戶組的時候,發送者是否接受他自己的消息是可選的。 soicketio.Server.emit() 方法提供了一個可選的 skip_sid 參數去指定一個想在廣播中跳過的客戶端。
Response
當一個客戶端發送一個事件給服務端,它可以選擇提供一個回調方法,當服務端返回一個響應的時候會被觸發。服務端可以便捷地從相應的事件處理器返回它從而提供一個響應。
事件處理器可以返回一個單獨的值,一個帶多個值的元組。這個在客戶端的回調函數將會調用這些返回的值。
Callbacks
回調
服務端可以請求一個響應通過發送一個事件給客戶端。 socketio.Server.emit() 方法有一個可選的 callback 參數能夠被設置為可回調的。當這個參數被傳遞之後,當客戶端返回相應的時候,這個可回調的方法將會被請求。
當廣播給多個客戶端的時候使用回調函數是不被推薦的,因為回調方法將會被只執行一次。
Namespace
命名空間
Socket.IO 協議支持多個邏輯性連接,所有的多路復用都是在相同的物理連接上。客戶端可以通過給每個連接指定不同的 namespace 從而開多個連接。一個命名空間是由 主機名+埠+路徑名稱構成的。比如,連接到 http://example.com:8000/chat 將會開一個連接到命名空間 /chat 。
由於分離的不同的session ID( sid s),不同的事件處理器,不同的聊天室,每一個命名空間都是獨立的。應用程序使用多個命名空間從而來區分命名空間,是非常重要的。可以參考 socketio.Server 類。
當 namespace 參數被觸發了,比如設置為 None 或者 / , 那麼一個默認的命名空間將會被使用。
Class-Based Namespaces
作為一個基於裝飾器的事件處理器的代替,這個屬於一個命名空間事件處理器可以被創建為 socketio.Namesapce 的子類:
對於基於非同步io的服務端,域名空間必須繼承與 socketio.AsyncNamespace , 也可以定義普通的方法或者協程作為事件處理器:
當使用基於類的命名空間的時候,任何被服務端接受的事件將會被分派到一個被事件名稱命名的方法中作為方法名稱(with the on_pfrefix )。比如:事件 my_event 將會被一個名叫 on_my_event 的方法處理。
⑤ Python 之 Socket編程(TCP/UDP)
socket(family,type[,protocal]) 使用給定的地址族、套接字類型、協議編號(默認為0)來創建套接字。
有效的埠號: 0~ 65535
但是小於1024的埠號基本上都預留給了操作系統
POSIX兼容系統(如Linux、Mac OS X等),在/etc/services文件中找到這些預留埠與的列表
面向連接的通信提供序列化、可靠的和不重復的數據交付,而沒有記錄邊界。意味著每條消息都可以拆分多個片段,並且每個消息片段都能到達目的地,然後將它們按順序組合在一起,最後將完整的信息傳遞給等待的應用程序。
實現方式(TCP):
傳輸控制協議(TCP), 創建TCP必須使用SOCK_STREAM作為套接字類型
因為這些套接字(AF_INET)的網路版本使用網際網路協議(IP)來搜尋網路中的IP,
所以整個系統通常結合這兩種協議(TCP/IP)來進行網路間數據通信。
數據報類型的套接字, 即在通信開始之前並不需要建議連接,當然也無法保證它的順序性、可靠性或重復性
實現方式(UDP)
用戶數據包協議(UDP), 創建UDP必須使用SOCK_DGRAM (datagram)作為套接字類型
它也使用網際網路來尋找網路中主機,所以是UDP和IP的組合名字UDP/IP
注意點:
1)TCP發送數據時,已建立好TCP連接,所以不需要指定地址。UDP是面向無連接的,每次發送要指定是發給誰。
2)服務端與客戶端不能直接發送列表,元組,字典。需要字元串化repr(data)。
TCP的優點: 可靠,穩定 TCP的可靠體現在TCP在傳遞數據之前,會有三次握手來建立連接,而且在數據傳遞時,有確認、窗口、重傳、擁塞控制機制,在數據傳完後,還會斷開連接用來節約系統資源。
TCP的缺點: 慢,效率低,佔用系統資源高,易被攻擊 TCP在傳遞數據之前,要先建連接,這會消耗時間,而且在數據傳遞時,確認機制、重傳機制、擁塞控制機制等都會消耗大量的時間,而且要在每台設備上維護所有的傳輸連接,事實上,每個連接都會佔用系統的CPU、內存等硬體資源。 而且,因為TCP有確認機制、三次握手機制,這些也導致TCP容易被人利用,實現DOS、DDOS、CC等攻擊。
什麼時候應該使用TCP : 當對網路通訊質量有要求的時候,比如:整個數據要准確無誤的傳遞給對方,這往往用於一些要求可靠的應用,比如HTTP、HTTPS、FTP等傳輸文件的協議,POP、SMTP等郵件傳輸的協議。 在日常生活中,常見使用TCP協議的應用如下: 瀏覽器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件傳輸.
UDP的優點: 快,比TCP稍安全 UDP沒有TCP的握手、確認、窗口、重傳、擁塞控制等機制,UDP是一個無狀態的傳輸協議,所以它在傳遞數據時非常快。沒有TCP的這些機制,UDP較TCP被攻擊者利用的漏洞就要少一些。但UDP也是無法避免攻擊的,比如:UDP Flood攻擊……
UDP的缺點: 不可靠,不穩定 因為UDP沒有TCP那些可靠的機制,在數據傳遞時,如果網路質量不好,就會很容易丟包。
什麼時候應該使用UDP: 當對網路通訊質量要求不高的時候,要求網路通訊速度能盡量的快,這時就可以使用UDP。 比如,日常生活中,常見使用UDP協議的應用如下: QQ語音 QQ視頻 TFTP ……
⑥ python socket 編程疑問
recv()的參數是緩沖區的大小,你可以設置大一點。recv是一個阻塞函數,如果收不到數據,就會阻塞在那裡。作為伺服器,這樣阻塞是正常的。
⑦ Python怎麼用socket讀取網站數據
是要連接到www.pythonlearn.com,然後發送請求獲取/code/intro-short.txt,這樣才行,HTTP請求格式網上應該有。
⑧ python怎麼建立socket服務端
socket伺服器再細分可分為多種了,tcp,udp,websocket,都是調用socket模塊,但是具體實現起來有一點細微的差別
先給出一個tcp和udp通過socket協議實現的聊天室的例子
python聊天室(python2.7版本):
都是分別運行server.py和client.py,就可以進行通訊了。
TCP版本:
socket-tcp-server.py(服務端):
#-*-encoding:utf-8-*-
#socket.getaddrinfo(host,port,family=0,socktype=0,proto=0,flags=0)
#根據給定的參數host/port,相應的轉換成一個包含用於創建socket對象的五元組,
#參數host為域名,以字元串形式給出代表一個IPV4/IPV6地址或者None.
#參數port如果字元串形式就代表一個服務名,比如「http」"ftp""email"等,或者為數字,或者為None
#參數family為地主族,可以為AF_INET,AF_INET6,AF_UNIX.
#參數socktype可以為SOCK_STREAM(TCP)或者SOCK_DGRAM(UDP)
#參數proto通常為0可以直接忽略
#參數flags為AI_*的組合,比如AI_NUMERICHOST,它會影響函數的返回值
#附註:給參數host,port傳遞None時建立在C基礎,通過傳遞NULL。
#該函數返回一個五元組(family,socktype,proto,canonname,sockaddr),同時第五個參數sockaddr也是一個二元組(address,port)
#更多的方法及鏈接請訪問
#Echoserverprogram
fromsocketimport*
importsys
importthreading
fromtimeimportctime
fromtimeimportlocaltime
importtraceback
importtime
importsubprocess
reload(sys)
sys.setdefaultencoding("utf8")
HOST='127.0.0.1'
PORT=8555#設置偵聽埠
BUFSIZ=1024
classTcpServer():
def__init__(self):
self.ADDR=(HOST,PORT)
try:
self.sock=socket(AF_INET,SOCK_STREAM)
print'%disopen'%PORT
self.sock.bind(self.ADDR)
self.sock.listen(5)
#設置退出條件
self.STOP_CHAT=False
#所有監聽的客戶端
self.clients={}
self.thrs={}
self.stops=[]
exceptException,e:
print"%disdown"%PORT
returnFalse
defIsOpen(ip,port):
s=socket(AF_INET,SOCK_STREAM)
try:
s.connect((ip,int(port)))
#s.shutdown(2)
#利用shutdown()函數使socket雙向數據傳輸變為單向數據傳輸。shutdown()需要一個單獨的參數,
#該參數表示s了如何關閉socket。具體為:0表示禁止將來讀;1表示禁止將來寫;2表示禁止將來讀和寫。
print'%disopen'%port
returnTrue
except:
print'%disdown'%port
returnFalse
deflisten_client(self):
whilenotself.STOP_CHAT:
print(u'等待接入,偵聽埠:%d'%(PORT))
self.tcpClientSock,self.addr=self.sock.accept()
print(u'接受連接,客戶端地址:',self.addr)
address=self.addr
#將建立的clientsocket鏈接放到列表self.clients中
self.clients[address]=self.tcpClientSock
#分別將每個建立的鏈接放入進程中,接收且分發消息
self.thrs[address]=threading.Thread(target=self.readmsg,args=[address])
self.thrs[address].start()
time.sleep(0.5)defreadmsg(self,address):
#如果地址不存在,則返回False
ifaddressnotinself.clients:
returnFalse
#得到發送消息的clientsocket
client=self.clients[address]
whileTrue:
try:
#獲取到消息內容data
data=client.recv(BUFSIZ)
except:
print(e)
self.close_client(address)
break
ifnotdata:
break
#python3使用bytes,所以要進行編碼
#s='%s發送給我的信息是:[%s]%s'%(addr[0],ctime(),data.decode('utf8'))
#對日期進行一下格式化
ISOTIMEFORMAT='%Y-%m-%d%X'
stime=time.strftime(ISOTIMEFORMAT,localtime())
s=u'%s發送給我的信息是:%s'%(str(address),data.decode('utf8'))
#將獲得的消息分發給鏈接中的clientsocket
forkinself.clients:
self.clients[k].send(s.encode('utf8'))
self.clients[k].sendall('sendall:'+s.encode('utf8'))
printstr(k)
print([stime],':',data.decode('utf8'))
#如果輸入quit(忽略大小寫),則程序退出
STOP_CHAT=(data.decode('utf8').upper()=="QUIT")
ifSTOP_CHAT:
print"quit"
self.close_client(address)
print"alreadyquit"
break
defclose_client(self,address):
try:
client=self.clients.pop(address)
self.stops.append(address)
client.close()
forkinself.clients:
self.clients[k].send(str(address)+u"已經離開了")
except:
pass
print(str(address)+u'已經退出')
if__name__=='__main__':
tserver=TcpServer()
tserver.listen_client()
——————————華麗的分割線——————————
socket-tcp-client.py(客戶端):
#-*-encoding:utf-8-*-
fromsocketimport*
importsys
importthreading
importtime
reload(sys)
sys.setdefaultencoding("utf8")
#測試,連接本機
HOST='127.0.0.1'
#設置偵聽埠
PORT=8555
BUFSIZ=1024
classTcpClient:
ADDR=(HOST,PORT)
def__init__(self):
self.HOST=HOST
self.PORT=PORT
self.BUFSIZ=BUFSIZ
#創建socket連接
self.client=socket(AF_INET,SOCK_STREAM)
self.client.connect(self.ADDR)
#起一個線程,監聽接收的信息
self.trecv=threading.Thread(target=self.recvmsg)
self.trecv.start()
defsendmsg(self):
#循環發送聊天消息,如果socket連接存在則一直循環,發送quit時關閉鏈接
whileself.client.connect_ex(self.ADDR):
data=raw_input('>:')
ifnotdata:
break
self.client.send(data.encode('utf8'))
print(u'發送信息到%s:%s'%(self.HOST,data))
ifdata.upper()=="QUIT":
self.client.close()
printu"已關閉"
break
defrecvmsg(self):
#接收消息,如果鏈接一直存在,則持續監聽接收消息
try:
whileself.client.connect_ex(self.ADDR):
data=self.client.recv(self.BUFSIZ)
print(u'從%s收到信息:%s'%(self.HOST,data.decode('utf8')))
exceptException,e:
printstr(e)
if__name__=='__main__':
client=TcpClient()
client.sendmsg()
UDP版本:
socket-udp-server.py
#-*-coding:utf8-*-
importsys
importtime
importtraceback
importthreading
reload(sys)
sys.setdefaultencoding('utf-8')
importsocket
importtraceback
HOST="127.0.0.1"
PORT=9555
CHECK_PERIOD=20
CHECK_TIMEOUT=15
classUdpServer(object):
def__init__(self):
self.clients=[]
self.beats={}
self.ADDR=(HOST,PORT)
try:
self.sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
self.sock.bind(self.ADDR)#綁定同一個域名下的所有機器
self.beattrs=threading.Thread(target=self.checkheartbeat)
self.beattrs.start()
exceptException,e:
traceback.print_exc()
returnFalse
deflisten_client(self):
whileTrue:
time.sleep(0.5)
print"hohohohohoo"
try:
recvData,address=self.sock.recvfrom(2048)
ifnotrecvData:
self.close_client(address)
break
ifaddressinself.clients:
senddata=u"%s發送給我的信息是:%s"%(str(address),recvData.decode('utf8'))
ifrecvData.upper()=="QUIT":
self.close_client(address)
ifrecvData=="HEARTBEAT":
self.heartbeat(address)
continue
else:
self.clients.append(address)
senddata=u"%s發送給我的信息是:%s"%(str(address),u'進入了聊天室')
forcinself.clients:
try:
self.sock.sendto(senddata,c)
exceptException,e:
printstr(e)
self.close_client(c)
exceptException,e:
#traceback.print_exc()
printstr(e)
pass
defheartbeat(self,address):
self.beats[address]=time.time()
defcheckheartbeat(self):
whileTrue:
print"checkheartbeat"
printself.beats
try:
forcinself.clients:
printtime.time()
printself.beats[c]
ifself.beats[c]+CHECK_TIMEOUT<time.time():
printu"%s心跳超時,連接已經斷開"%str(c)
self.close_client(c)
else:
printu"checkp%s,沒有斷開"%str(c)
exceptException,e:
traceback.print_exc()
printstr(e)
pass
time.sleep(CHECK_PERIOD)
defclose_client(self,address):
try:
ifaddressinself.clients:
self.clients.remove(address)
ifself.beats.has_key(address):
delself.beats[address]
printself.clients
forcinself.clients:
self.sock.sendto(u'%s已經離開了'%str(address),c)
print(str(address)+u'已經退出')
exceptException,e:
printstr(e)
raise
if__name__=="__main__":
udpServer=UdpServer()
udpServer.listen_client()
——————————華麗的分割線——————————
socket-udp-client.py:
#-*-coding:utf8-*-
importsys
importthreading
importtime
reload(sys)
sys.setdefaultencoding('utf-8')
importsocket
HOST="127.0.0.1"
PORT=9555
#BEAT_PORT=43278
BEAT_PERIOD=5
classUdpClient(object):
def__init__(self):
self.clientsock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
self.HOST=HOST
self.ADDR=(HOST,PORT)
self.clientsock.sendto(u'請求建立鏈接',self.ADDR)
self.recvtrs=threading.Thread(target=self.recvmsg)
self.recvtrs.start()
self.hearttrs=threading.Thread(target=self.heartbeat)
self.hearttrs.start()
defsendmsg(self):
whileTrue:
data=raw_input(">:")
ifnotdata:
break
self.clientsock.sendto(data.encode('utf-8'),self.ADDR)
ifdata.upper()=='QUIT':
self.clientsock.close()
break
defheartbeat(self):
whileTrue:
self.clientsock.sendto('HEARTBEAT',self.ADDR)
time.sleep(BEAT_PERIOD)
defrecvmsg(self):
whileTrue:
recvData,addr=self.clientsock.recvfrom(1024)
ifnotrecvData:
break
print(u'從%s收到信息:%s'%(self.HOST,recvData.decode('utf8')))if__name__=="__main__":
udpClient=UdpClient()
udpClient.sendmsg()
⑨ 關於Python中socket問題。
HOST='localhost'#如果要兩台電腦遠程通訊,這里應該綁定什麼?
所謂host,就是主機地址。本地使用 'localhost' 會被映射為 '127.0.0.1'的,也就是本機。
不同主機相互通信呢?直接使用 區域網地址即可。比如 (本機)192.168.1.2 (其他電腦)192.168.1.3 然後本機做主機:HOST='192.168.1.2'
換句話說:客戶端的機器 必須能夠連接 主機。 在(其他電腦) "ping 192.168.1.2" 必須能ping通。
那麼 不在同一個區域網的怎麼辦?那就必須使用 外網IP了,或者域名。或者使用類似「花生殼」等軟體,進行主機映射也可以
⑩ python怎麼連接websocket
websocket是一個瀏覽器和伺服器通信的新的協議,一般而言,瀏覽器和伺服器通信最常用的是http協議,但是http協議是無狀態的,每次瀏覽器請求信息,伺服器返回信息後這個瀏覽器和伺服器通信的信道就被關閉了,這樣使得伺服器如果想主動給瀏覽器發送信息變得不可能了,伺服器推技術在http時代的解決方案一個是客戶端去輪詢,或是使用comet技術,而websocket則和一般的socket一樣,使得瀏覽器和伺服器建立了一個雙工的通道。
具體的websocket協議在rfc6455裡面有,這里簡要說明一下。websocket通信需要先有個握手的過程,使得協議由http轉變為webscoket協議,然後瀏覽器和伺服器就可以利用這個socket來通信了。
首先瀏覽器發送握手信息,要求協議轉變為websocket
GET / HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin:
伺服器接收到信息後,取得其中的Sec-WebSocket-Key,將他和一個固定的字元串258EAFA5-E914-47DA-95CA-C5AB0DC85B11做拼接,得到的字元串先用sha1做一下轉換,再用base64轉換一下,就得到了回應的字元串,這樣伺服器端發送回的消息是這樣的
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
這樣握手就完成了,用python來實現這段握手過程的話就是下面這樣。
def handshake(conn):
key =None
data = conn.recv(8192)
if not len(data):
return False
for line in data.split('\r\n\r\n')[0].split('\r\n')[1:]:
k, v = line.split(': ')
if k =='Sec-WebSocket-Key':
key =base64.b64encode(hashlib.sha1(v +'258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest())
if not key:
conn.close()
return False
response ='HTTP/1.1 101 Switching Protocols\r\n'\
'Upgrade: websocket\r\n'\
'Connection: Upgrade\r\n'\
'Sec-WebSocket-Accept:'+ key +'\r\n\r\n'
conn.send(response)
return True
握手過程完成之後就是信息傳輸了,websocket的數據信息格式是這樣的。
+-+-+-+-+-------+-+-------------+-------------------------------+
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| | Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
值得注意的是payload len這項,表示數據的長度有多少,如果小於126,那麼payload len就是數據的長度,如果是126那麼接下來2個位元組是數據長度,如果是127表示接下來8個位元組是數據長度,然後後面會有四個位元組的mask,真實數據要由payload data和mask做異或才能得到,這樣就可以得到數據了。發送數據的格式和接受的數據類似,具體細節可以去參考rfc6455,這里就不過多贅述了。
Python的Websocket客戶端:Websocket-Client
Websocket-Client 是 Python 上的 Websocket 客戶端。它只支持 hybi-13,且所有的 Websocket API 都支持同步。
Installation
This mole is tested on Python 2.7 and Python 3.x.
Type "python setup.py install" or "pip install websocket-client" to install.
Caution!
from v0.16.0, we can install by "pip install websocket-client" for python 3.
This mole depend on
six
backports.ssl_match_hostname for Python 2.x
Python通過websocket與js客戶端通信示例分析
這里,介紹如何使用 Python 與前端 js 進行通信。
websocket 使用 HTTP 協議完成握手之後,不通過 HTTP 直接進行 websocket 通信。
於是,使用 websocket 大致兩個步驟:使用 HTTP 握手,通信。
js 處理 websocket 要使用 ws 模塊; Python 處理則使用 socket 模塊建立 TCP 連接即可,比一般的 socket ,只多一個握手以及數據處理的步驟。
包格式
js 客戶端先向伺服器端 python 發送握手包,格式如下:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin:
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
伺服器回應包格式:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
其中, Sec-WebSocket-Key 是隨機的,伺服器用這些數據構造一個 SHA-1 信息摘要。
方法為: key+migic , SHA-1 加密, base-64 加密
Python 中的處理代碼:
MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())
握手完整代碼
js 端
js 中有處理 websocket 的類,初始化後自動發送握手包,如下:
var socket = new WebSocket('ws://localhost:3368');
Python 端
Python 用 socket 接受得到握手字元串,處理後發送
HOST = 'localhost'
PORT = 3368
MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: {1}\r\n" \
"WebSocket-Location: ws://{2}/chat\r\n" \
"WebSocket-Protocol:chat\r\n\r\n"
def handshake(con):
#con為用socket,accept()得到的socket
headers = {}
shake = con.recv(1024)
if not len(shake):
return False
header, data = shake.split('\r\n\r\n', 1)
for line in header.split('\r\n')[1:]:
key, val = line.split(': ', 1)
headers[key] = val
if 'Sec-WebSocket-Key' not in headers:
print ('This socket is not websocket, client close.')
con.close()
return False
sec_key = headers['Sec-WebSocket-Key']
res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())
str_handshake = HANDSHAKE_STRING.replace('{1}', res_key).replace('{2}', HOST + ':' + str(PORT))
print str_handshake
con.send(str_handshake)
return True
通信
不同版本的瀏覽器定義的數據幀格式不同, Python 發送和接收時都要處理得到符合格式的數據包,才能通信。
Python 接收
Python 接收到瀏覽器發來的數據,要解析後才能得到其中的有用數據。
固定位元組:
( 1000 0001 或是 1000 0002 )這里沒用,忽略
包長度位元組:
第一位肯定是 1 ,忽略。剩下 7 個位可以得到一個整數 (0 ~ 127) ,其中
( 1-125 )表此位元組為長度位元組,大小即為長度;
(126)表接下來的兩個位元組才是長度;
(127)表接下來的八個位元組才是長度;
用這種變長的方式表示數據長度,節省數據位。
mark 掩碼:
mark 掩碼為包長之後的 4 個位元組,之後的兄弟數據要與 mark 掩碼做運算才能得到真實的數據。
兄弟數據:
得到真實數據的方法:將兄弟數據的每一位 x ,和掩碼的第 i%4 位做 xor 運算,其中 i 是 x 在兄弟數據中的索引。
完整代碼
def recv_data(self, num):
try:
all_data = self.con.recv(num)
if not len(all_data):
return False
except:
return False
else:
code_len = ord(all_data[1]) & 127
if code_len == 126:
masks = all_data[4:8]
data = all_data[8:]
elif code_len == 127:
masks = all_data[10:14]
data = all_data[14:]
else:
masks = all_data[2:6]
data = all_data[6:]
raw_str = ""
i = 0
for d in data:
raw_str += chr(ord(d) ^ ord(masks[i % 4]))
i += 1
return raw_str
js 端的 ws 對象,通過 ws.send(str) 即可發送
ws.send(str)
Python 發送
Python 要包數據發送,也需要處理
固定位元組:固定的 1000 0001( 『 \x81 ′ )
包長:根據發送數據長度是否超過 125 , 0xFFFF(65535) 來生成 1 個或 3 個或 9 個位元組,來代表數據長度。
def send_data(self, data):
if data:
data = str(data)
else:
return False
token = "\x81"
length = len(data)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
#struct為Python中處理二進制數的模塊,二進制流為C,或網路流的形式。
data = '%s%s' % (token, data)
self.con.send(data)
return True