socket一般被稱做"套接字",應用程序經過"套接字"向網絡發出請求或者應答網絡請求,是主機或一臺計算機上的進程能夠通訊。python
socket起源於Unix,而Unix/Linux基本哲學之一就是「一切皆文件」,對於文件用【打開】【讀寫】【關閉】模式來操做。socket就是該模式的一個實現,服務器和客戶端各自維護一個"文件",在創建鏈接打開後,各自的"文件"能夠被對方讀取和能夠向本身的"文件"寫入內容 。通信結束時,關閉各自的"文件"。socket是實現TCP,UDP協議的接口,便於使用TCP、UDP。編程
socket簡單的例子服務器
服務器(server)網絡
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket server = socket.socket() # 聲明socket類型,同時建立socket實例 server.bind(('localhost', 8888)) # 爲socket實例綁定IP地址和端口號。 server.listen(5) # 啓動監聽,等待客戶端的接入請求 while True: print('等待客戶端接入...') conn, addr = server.accept() # Accept阻塞,直到有客戶端鏈接進來 client_data = conn.recv(1024).decode('utf-8') # 接收客戶端的信息 print('客戶端的消息:',client_data) conn.send('已鏈接'.encode('utf-8')) # 向客戶端發送消息 server.close() # 關閉鏈接
客戶端 (client)併發
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket client = socket.socket() # 建立一個實例 client.connect(('localhost', 8888)) # 鏈接到服務器 client.send('請求鏈接'.encode()) # 發送信息給服務器 client_data = client.recv(1024).decode('utf-8') # 接受服務器反饋的信息 print(client_data) client.close() # 關閉鏈接
服務器端的方法:socket
客戶端的方法:函數
公用的方法:大數據
使用socket實現簡單的遠程輸入系統命令返回結果值spa
服務器操作系統
import socket import os server = socket.socket() server.bind(('localhost', 8888)) server.listen() print('等待客戶端接入') while True: conn, addr = server.accept() print('new conn', addr) while True: data = conn.recv(1024).decode("utf-8") if not data: print("客戶端斷開") break print("執行指令:", data) cmd_res = os.popen(data).read().encode("utf-8") if len(cmd_res) == 0: cmd_res = '輸入的命令不合法'.encode('utf-8') conn.send(str(len(cmd_res)).encode('utf-8')) # 向客戶端發送它要接受的數據的大小 print(str(len(cmd_res))) check_res = conn.recv(1024) # 主要是爲了解決粘包的問題 conn.send(cmd_res) server.close()
客戶端
#!/usr/bin/env python #-*-coding:utf-8-*- import socket client = socket.socket() client.connect(('localhost', 8888)) while True: cmd = input('>>>').strip().encode("utf-8") if cmd == 'exit'.encode('utf-8'): break if len(cmd) == 0: continue client.send(cmd) len_res = client.recv(1024).decode("utf-8") client.send(bytes('ok',encoding='utf8')) # 主要是爲了在數據太多的時候發生粘包的狀況 # print('返回數據的大小', len_res) 查看要接受多少數據 received_num = 0 received_data = '' while received_num != int(len_res): # 主要是實現數據在一次返回中所有接收完畢 recv_num = client.recv(1024) received_num += len(recv_num) received_data += str(recv_num) print(eval(received_data).decode('utf-8')) # cmd_res = client.recv(1024).decode("utf-8") # print(cmd_res) client.close()
粘包;主要是爲了解決上面的check_res和下面的返回的數據粘在一塊兒,致使咱們的結果不正確。因此在發送數據大小後立馬實現接收客戶端對數據大小的確認,這樣就會是數據產生粘包。
在上面建立socket實例的時候(socket.socket())後面的括號有三個參數
第一個參數:地址簇
第二個參數:類型
第三個參數:協議
下面的列子是根據上面的例子實現遠程下載
FTPserver
#!/usr/bin/env python #-*-coding:utf-8-*- import socket import os,subprocess import hashlib server = socket.socket() server.bind(('localhost', 8888)) server.listen() print('等待客戶端接入') while True: conn, addr = server.accept() print('new conn', addr) while True: data = conn.recv(1024).decode("utf-8") if not data: print("客戶端{}斷開".format(addr)) break cmd, filename = data.split() if os.path.isfile(filename): f = open(filename, 'rb') m = hashlib.md5() file_size = str(os.stat(filename).st_size).encode('utf-8') conn.send(file_size) ack = conn.recv(1024) # 粘包 for line in f: m.update(line) conn.send(line) f.close() conn.send(m.hexdigest().encode('utf-8')) # cmd_res = subprocess.getoutput(data).encode('utf-8') # cmd_len = str(len(cmd_res)).encode('utf-8') # conn.send(cmd_len) # ack = conn.recv(1024) # 粘包 # conn.send(cmd_res) server.close()
client
#!/usr/bin/env python #-*-coding:utf-8-*- import socket, hashlib client = socket.socket() client.connect(('192.168.132.66', 8888)) while True: cmd = input('>>>').strip().encode("utf-8") if cmd == 'exit'.encode('utf-8'): break if len(cmd) == 0: continue if cmd.decode('utf-8').startswith('get'): client.send(cmd) file_size = int(client.recv(1024).decode('utf-8')) client.send(b'ok') filename = cmd.decode('utf-8').split()[1] f = open(filename + '.new','wb') m = hashlib.md5() received_data = 0 while received_data != file_size: if file_size - received_data > 1024: size = 1024 else: size = file_size - received_data data = client.recv(size) f.write(data) received_data += len(data) m.update(data) else: print('file recv done',received_data,file_size) received_md5 = client.recv(1024).decode('utf-8') tota_md5 = m.hexdigest() print('發送',received_md5) print("接收",tota_md5) f.close() client.close()
以上的實例中,客戶端必須排隊與服務器進行通信,只有當正在通信的客戶端斷開鏈接纔可以到下一個客戶端通信,下面咱們將說道多個客戶端能夠同時和服務器通信,也就是客戶端併發。
多併發服務器
#!/usr/bin/env python # -*-coding:utf-8-*- import socketserver # 實現併發的須要的模塊 class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self): while True: try: self.data = self.request.recv(1024).strip().decode("utf-8") # self.data 爲接收到的信息並轉換爲utf-8的格式 print("{} wrote:".format(self.client_address[0])) # self.client_address[0]表示的是客戶端的IP地址與socket的addr是同樣的 # self.client_address[1]表示的是客戶端的端口號與socket的port是同樣的 print(self.data) if not self.data: break # 上面的判斷是客戶端自動結束程序關閉鏈接通道的判斷 self.request.send(self.data.upper().encode('utf-8')) except ConnectionResetError as e: print("erro", e) break # 上面的錯誤處理是直接斷開程序作的判斷捕獲的異常 if __name__ == "__main__": HOST, PORT = "localhost", 8888 # 服務器綁定的IP和端口號 server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) # 這個實現多併發的關鍵就在ThreadingTCPServer()的方法實現了能夠同時和多個會話 # 傳入端口地址和咱們新建的繼承自socketserver模塊下的BaseRequestHandler類 實例化對象 server.serve_forever() # 經過調用對象的serve_forever()方法來激活服務端
客戶端就用簡單的客戶端就好
#!/usr/bin/env python # -*-coding:utf-8-*- import socket client = socket.socket() client.connect(("localhost", 8888)) while True: msg = input(">>>").strip() if not msg: continue if msg == 'exit': break client.send(msg.encode("utf-8")) received = client.recv(1024) print(received.decode("utf-8")) client.close()
上面的就是一些基礎的socket的應用,能夠在上面的基礎上深刻的使用socket。這些是關於我對socket的簡單的初期認識。