網絡上的兩個程序經過一個雙向的通訊鏈接實現數據的交換,這個鏈接的一端稱爲一個socket。python
Socket又稱"套接字",應用程序一般經過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間能夠通信。python3.x
Python 提供了兩個級別訪問的網絡服務。:服務器
注意:如下實例中所有使用本地局域網 若想實現外網傳輸 在有路由器的狀況下需首先向運營商申請公網動態ip 而後添加內網映射等操做網絡
Python 中,咱們用 socket()函數來建立套接字,語法格式以下:多線程
import socket
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)#所給即爲默認參數
參數異步
family:socket
type: tcp
protocol: 函數
函數 | 描述 |
---|---|
服務器端套接字 | |
s.bind() | 綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。 |
s.listen() | 開始TCP監聽。backlog指定在拒絕鏈接以前,操做系統能夠掛起的最大鏈接數量。該值至少爲1,大部分應用程序設爲5就能夠了。 |
s.accept() | 被動接受TCP客戶端鏈接,(阻塞式)等待鏈接的到來 |
客戶端套接字 | |
s.connect() | 主動初始化TCP服務器鏈接,。通常address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。 |
s.connect_ex() | connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常 |
公共用途的套接字函數 | |
s.recv() | 接收TCP數據,數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其餘信息,一般能夠忽略。 |
s.send() | 發送TCP數據,將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。 |
s.sendall() | 完整發送TCP數據,完整發送TCP數據。將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。 |
s.recvfrom() | 接收UDP數據,與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。 |
s.sendto() | 發送UDP數據,將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。 |
s.close() | 關閉套接字 |
s.getpeername() | 返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port)。 |
s.getsockname() | 返回套接字本身的地址。一般是一個元組(ipaddr,port) |
s.setsockopt(level,optname,value) | 設置給定套接字選項的值。 |
s.getsockopt(level,optname[.buflen]) | 返回套接字選項的值。 |
s.settimeout(timeout) | 設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如connect()) |
s.gettimeout() | 返回當前超時期的值,單位是秒,若是沒有設置超時期,則返回None。 |
s.fileno() | 返回套接字的文件描述符。 |
s.setblocking(flag) | 若是flag爲0,則將套接字設爲非阻塞模式,不然將套接字設爲阻塞模式(默認值)。非阻塞模式下,若是調用recv()沒有發現任何數據,或send()調用沒法當即發送數據,那麼將引發socket.error異常。 |
s.makefile() | 建立一個與該套接字相關連的文件 |
服務端 socket_server.py測試
import socket #默認tcp方式傳輸 sk=socket.socket() #綁定IP與端口 ip_port=('127.0.0.1',8888) #綁定監聽 sk.bind(ip_port) #最大鏈接數 sk.listen(5) #不斷循環 接受數據 while True: #提示信息 print("正在等待接收數據。。。。") #接受數據 鏈接對象與客戶端地址 conn, address = sk.accept() #定義信息 msg = "鏈接成功" #返回信息 #注意 python3.x以上,網絡數據的發送接收都是byte類型 #若是發送的數據是str型,則須要編碼 conn.send(msg.encode()) #不斷接收客戶端發來的消息 while True: #接收客戶端消息 data = conn.recv(1024) print(data.decode()) #接收到退出指令 if data == b'exit': break #處理客戶端信息 本實例直接將接收到的消息從新發回去 conn.send(data) #主動關閉鏈接 conn.close()
客戶端 socket_client.py
import socket #服務端爲tcp方式,客戶端也採用tcp方式 默認參數即爲tcp client = socket.socket() #訪問的服務器的ip和端口 ip_port=('127.0.0.1',8888) #鏈接主機 client.connect(ip_port) #定義發送消息循環 while True: # 接受主機信息 每次接收緩衝區1024個字節 data = client.recv(1024) # 打印接受的數據 print(data.decode()) msg_input = input("請輸入發送的消息:") client.send(msg_input.encode()) if msg_input == 'exit': break
運行結果:
服務端
客戶端
可是這種tcp方式咱們能夠發現一次只能有一個客戶端在運行,咱們可使用更強大的socketserver包來實現非堵塞型tcp鏈接
相對於上面堵塞型實例,咱們僅須要修改服務端文件便可實現多個客戶端與服務端信息交互
socket_server_tcp2.py
#非阻塞模塊 import socketserver #首先咱們須要定義一個類 class MySocketServer(socketserver.BaseRequestHandler): #首先執行setup方法,而後執行handle方法,最後執行finish方法 #若是handle方法報錯,則會跳過 #setup與finish不管如何都會執行 #通常只定義handle方法便可 def setup(self): pass def handle(self): #定義鏈接變量 conn=self.request # 提示信息 print("鏈接成功") #發送消息定義 msg="Hello World!" #發送消息 conn.send(msg.encode()) #進入循環 不斷接收客戶端消息 while True: #接收客戶端消息 data=conn.recv(1024) #打印消息 print(data.decode()) if data==b'exit': break conn.send(data) conn.close() def finish(self): pass if __name__=='__main__': # 提示信息 print("正在等待接收數據。。。。") #建立多線程實例 server=socketserver.ThreadingTCPServer(("127.0.0.1",8888),MySocketServer) #開啓異步多線程,等待鏈接 server.serve_forever()
接着咱們能夠測試,首先運行socket_server_tcp2.py,而後發現如今能夠同時運行多個socket_client.py與服務端進行通訊
總所周知UDP是不可靠的傳輸協議
服務端socket_server_udp.py
import socket #建立實例 並指定udp參數 sk = socket.socket(type=socket.SOCK_DGRAM) #定義綁定的ip和port ip_port = ('127.0.0.1',8888) #綁定監聽 sk.bind(ip_port) print("正在等待接收數據") #循環接收數據 while True: #接收數據 data = sk.recv(1024) #打印數據 print(data.decode())
客戶端socket_client_udp
import socket #實例化對象 sk = socket.socket(type=socket.SOCK_DGRAM) #定義須要鏈接的的ip和port ip_port = ('127.0.0.1',8888) #循環輸入數據 while True: #輸入發送的信息 msg_input = input("請輸入發送的消息:") # 接收到退出指令 if msg_input == b'exit': break #與tcp不一樣 udp使用sendto函數來發送消息 sk.sendto(msg_input.encode(),ip_port) sk.close()
結果:
服務端
客戶端
文件傳輸其實就是比信息交互多了文件IO操做
文件接收端(服務端)file_receive.py:
import socket #實例化 sk = socket.socket() #定義鏈接的ip和port ip_port = ('127.0.0.1',9999) #綁定端口 sk.bind(ip_port) #最大鏈接數 sk.listen(5) #進入循環接收數據 conn, address = sk.accept() print("文件接收開始") while True: with open('file','ab') as f: #接收數據 data = conn.recv(1024) if data == b'quit': break #寫入文件 f.write(data) #接受完成標誌 conn.send('success'.encode()) print("文件接收完成") #關閉鏈接 sk.close()
文件發送端(客戶端)file_send.py:
import socket #實例化 sk = socket.socket() #定義鏈接的ip和port ip_port = ('127.0.0.1',9999) #服務器鏈接 sk.connect(ip_port) #文件上傳 #打開文件 with open('D:\pythonwork\socket\socket_server_tcp2.py','rb') as f: #按每一段分割文件上傳 for i in f: sk.send(i) #等待接收完成標誌 data=sk.recv(1024) #判斷是否真正接收完成 if data != b'success': break #給服務端發送結束信號 sk.send('quit'.encode())
進行測試,首先運行file_receive.py 而後運行file_send.py 能夠發現文件傳輸成功
打開file文件 能夠發現寫入成功 這就實現了簡單的文件傳輸操做