46. Python Socket編程
為了防止消息丟失,或者是調用方,不需一直等待響應方的結果。
# threadtest.py
import codecs from queue import Queue from threading import Thread import time class Produce(Thread): def __init__(self, queue): super(Produce, self).__init__() self.fileName = "../firstlession/passwd" self.fileList = list() self.queue = queue def run(self): with codecs.open(self.fileName) as f: self.fileList += f.readlines() for line in self.fileList: self.queue.put(line) class Consumer(Thread): def __init__(self, queue): self.queue = queue super(Consumer, self).__init__() self.newPasswd = "newpasswd.txt" self.fileList = list() self.stat = 1 def run(self): while 1: if self.queue.empty(): time.sleep(2) self.stat += 1 if self.stat == 5: break else: self.stat = 1 data = self.queue.get() self.fileList.append(data) with codecs.open(self.newPasswd, 'w') as f: f.writelines(self.fileList)
調用
# thread1.py
from queue import Queue from onlive.secondlesson.threadtest import Produce, Consumer def main(): q = Queue() produce = Produce(q) consumer = Consumer(q) produce.start() consumer.start() if __name__ == '__main__': main()
socket 簡介
TCP的可靠性實現:
(1)校驗碼
(2)接收方反饋
(3)信息包附帶序號
UDP:
(1)快 不需要花費時間建立和關閉連接
(2)快 偶爾丟失一兩個消息包無所謂,但是TCP會嚴格檢查
(3)快 UDP的限制是一個信息包不超過64KB的數據
TCP和UDP區別:
UDP不建立連接,只保證數據的完整性,數據傳輸快,但是不保證數據是否真的被收到,也不保證數據是否只接收一次,也不保證次序。
TCP則相反。
服務端是用來給一個或多個客戶端提供服務的,當客戶端發起請求,開始等待服務端的返回結果,服務端接受完請求以後,根據自己的邏輯處理請求,並返回給客戶端,客戶端接收到返回結果以後,關閉和服務端的連接。
[備註]:只要是發送數據的就是"寫",只要是接收數據的就是"讀"。
最常用的客戶端和服務端有兩種模式:C/S模式(mysql) 和 B/S模式(百度、京東、淘寶網站等)
socket流程:
(1)服務端創建完一個socket以後
(2)需要綁定一個IP:PORT
(3)對其進行監聽(listen)-->【監聽的方法內需要帶一個數字,這個數字表示同時有多少個客戶端可以來訪問服務端】
(4)然後接收請求(accept)--> 【客戶端每創建一個連接,調用 "connect函數" 後,服務端就需要生成一個新的socket連接和客戶端進行傳輸,傳輸完成後關閉客戶端連接、關閉服務端;服務端永遠要比客戶端多一個socket連接,如果說客戶端是n個socket連接,那麽服務端就要創建n+1個socket連接,因為剛開始啟動服務端的時候,服務端就要創建一個socket連接,每當從客戶端過來一個連接,服務端就要創建一個socket連接跟客戶端進行交互,所以服務端比客戶端多一個連接。】
socket常用函數講解:
創建套接字:
s = socket.socket(address family, socket type)
address family:
socket.AF_INET 默認ipv4
socket.AF_INET6 ipv6
socket.AF_UNIX 只用於單一unix系統間進行通信
socket type:
socket.SOCK_STREAM 流式socket TCP
socket.SOCK_DGRAM 數據報式socket UDP
TCP 方式 socket:
(1)創建socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
(2)綁定地址:
address = ('0.0.0.0', 8009)
s.bind(address) 或者 s.bind((0.0.0.0, 8009)) 兩種方式相同;
【註意:address 必須是一個元組,容易錯誤,address = (host, port)】
【host:服務端ip,字符串類型,如果為0.0.0.0,代表本機的任意一個IP】
【port:服務端提供的端口,整形,0-1024為系統保留(不選這裏面的端口)】
(3)監聽消息:
s.listen(badklog)
backlog 代表可以同時接受多少個socket連接
(4)接受連接:
conn, addr = s.accept()
接受連接並返回元組(conn,addr),其中conn是新的套接字對象,每個新的連接就創建一個新的對象。可以用來接受和發送數據,addr是客戶端的地址:包含host和port。
(5)發送數據:
s.send(string) 發送字符串到連接的套接字,可能未將指定內容全部發送;
s.sendall(string) 內部遞歸調用send,將所有內容發送出去,建議使用。
(6)接收數據:
data = s.recv(bufsize)
接收套接字數據,數據以字符串形式返回,bufsize指定最多接收的數據量,可以使用1024, 2048
如果不知道接收的數量有多少,可以能幾個字節,可能幾兆,一般通過循環接收。
UDP 方式 socket:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(string)
data, address = s.recvfrom(bufsize)
客戶端:
客戶端首先也要創建socket套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
客戶端連接服務端函數:
s.connect(address) #連接到address的套接字
result - connect_ex(address) #成功返回0,失敗返回錯誤碼
通用:
s.close() #關閉套接字
s.getsocketname() #獲取套接字的名字
s.settimeout(timeout) #設置套接字超時時間,timeout為float類型,單位秒
s.gettimeout() #獲得套接字超時時間
s.setblocking(flag) #flage為bool值
setblocking(True) is equivalent to settimeout(None); #不設置超時時間,一直阻塞在那裏
setblocking(False) is equivalent to settimeout(0.0); #設置超時時間為0,如果設置False,accept和recv一旦無數據,則報錯。
s.fileno() # 返回套接字的文件描述符(一個小整數)。這對於select.select()是有用的。
socket例子:
寫socket工具:
# util.py
import socket import time class InitSocketTest(object): def __init__(self, host, port, type): self.host = host self.port = port self.address = (host, port) self.type = type self.s = None self.creatsocket() def creatsocket(self): if self.type.upper() == "TCP": self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) elif self.type.upper == "UDP": self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) else: print("you must input the InitSocket(type) is 'UDP|TCP' ") class SocketServerTest(InitSocketTest): def __init__(self, host, port, type, backlog): self.backlog = backlog super(SocketServerTest, self).__init__(host, port, type) self.clientAddress = None def run(self): self.s.bind(self.address) self.s.listen(self.backlog) print("server starting…………") conn, self.clientAddress = self.s.accept() print("accept connect from {0}".format(self.clientAddress)) for i in range(1, 10): conn.sendall("i = {0}".format(str(i)).encode("utf-8")) self.s.close() class ClientSocketTest(InitSocketTest): def run(self): self.s.connect(self.address) stat = 1 while 1: data = self.s.recv(2048) if len(data)>0: stat = 1 print(data.decode("utf-8")) else: stat += 1 time.sleep(1) if stat == 5: break
# testserver.py
from onlive.sockettest.util import SocketServerTest if __name__ == '__main__': socketServer = SocketServerTest(host="0.0.0.0", port=9999, type="tcp", backlog=5) socketServer.run()
# testclient.py
from onlive.sockettest.util import ClientSocketTest if __name__ == '__main__': socketClient = ClientSocketTest(host="127.0.0.1", port=9999, type="tcp") socketClient.run()
46. Python Socket編程