tcp服務端
ss = socket() #創建服務器套接字 ss.bind() #把地址綁定到套接字 ss.listen() #監聽鏈接 inf_loop: #服務器無限循環 cs = ss.accept() #接受客戶端鏈接 comm_loop: #通訊循環 cs.recv()/cs.send() #對話(接收與發送) cs.close() #關閉客戶端套接字 ss.close() #關閉服務器套接字(可選)
tcp客戶端
cs = socket() # 創建客戶套接字 cs.connect() # 嘗試連接服務器 comm_loop: # 通訊循環 cs.send()/cs.recv() # 對話(發送/接收) cs.close() # 關閉客戶套接字
udp服務端
cs = socket() # 創建客戶套接字 cs.connect() # 嘗試連接服務器 comm_loop: # 通訊循環 cs.send()/cs.recv() # 對話(發送/接收) cs.close() # 關閉客戶套接字
udp客戶端
cs = socket() # 創建客戶套接字 comm_loop: # 通訊循環 cs.sendto()/cs.recvfrom() # 對話(發送/接收) cs.close() # 關閉客戶套接字
recv與recvfrom的區別
1.udp的sendinto不用管是否有一個正在運行的服務端,可以己端一個勁的發消息
2.udp的recvfrom是阻塞的,一個recvfrom(x)必須對一個一個sendinto(y),收完了x個字節的數據就算完成,若是y>x數據就丟失,這意味着udp根本不會粘包,但是會丟數據,不可靠
3.tcp的協議數據不會丟,己端總是在收到ack時才會清除緩沖區內容。數據是可靠的,但是會粘包。
粘包
兩種情況下會發生粘包。
發送端需要等緩沖區滿才發送出去,造成粘包(發送數據時間間隔很短,數據了很小,會合到一起,產生粘包)
接收方不及時接收緩沖區的包,造成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候還是從緩沖區拿上次遺留的數據,產生粘包)
server端
import socket,struct,json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 phone.bind(('127.0.0.1',8080)) phone.listen(5) while True: conn,addr=phone.accept() while True: cmd=conn.recv(1024) if not cmd:break print('cmd: %s' %cmd) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err=res.stderr.read() print(err) if err: back_msg=err else: back_msg=res.stdout.read() headers={'data_size':len(back_msg)} head_json=json.dumps(headers) conn.send(struct.pack('i',len(head_json))) #先發報頭的長度 conn.send(head_json.encode('utf-8')) #再發報頭 conn.sendall(back_msg) #在發真實的內容 conn.close()
client端
from socket import * import struct,json ip_port=('127.0.0.1',8080) client=socket(AF_INET,SOCK_STREAM) client.connect(ip_port) while True: cmd=input('>>: ') if not cmd:continue client.send(bytes(cmd,encoding='utf-8')) head=client.recv(4) head_json_len=struct.unpack('i',head)[0] head_json=json.loads(client.recv(head_json_len).decode('utf-8')) data_len=head_json['data_size'] recv_size=0 recv_data=b'' while recv_size < data_len: recv_data+=client.recv(1024) recv_size+=len(recv_data) print(recv_data.decode('utf-8'))
ThreadingTCPServer
ThreadingTCPServer實現的Soket服務器內部會為每個client創建一個 “線程”,該線程用來和客戶端進行交互。
1、ThreadingTCPServer基礎
使用ThreadingTCPServer:
- 創建一個繼承自 SocketServer.BaseRequestHandler 的類
- 類中必須定義一個名稱為 handle 的方法
- 啟動ThreadingTCPServer
#!/usr/bin/env python # -*- coding:utf-8 -*- import SocketServer class MyServer(SocketServer.BaseRequestHandler): def handle(self): # print self.request,self.client_address,self.server conn = self.request conn.sendall('歡迎致電 10086,請輸入1xxx,0轉人工服務.') Flag = True while Flag: data = conn.recv(1024) if data == 'exit': Flag = False elif data == '0': conn.sendall('通過可能會被錄音.balabala一大推') else: conn.sendall('請重新輸入.') if __name__ == '__main__': server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer) server.serve_forever()
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8009) sk = socket.socket() sk.connect(ip_port) sk.settimeout(5) while True: data = sk.recv(1024) print 'receive:',data inp = raw_input('please input:') sk.sendall(inp) if inp == 'exit': break sk.close()
練習項目:
1、udp監控程序
server端,將接收的數據轉換csv格式存儲到文本中
client端,將本地的狀態信息發送給server端
git:https://github.com/wangyufu/udp-monitoring
2、FTP
多用戶登錄進行上傳下載,可以加md5校驗文件。
上傳下載進度條;
上傳時判斷家目錄存儲配額;
cd切換目錄,鎖定到自己的家目錄;
pwd查看當前路徑、del功能;
del刪除文件。
git:https://github.com/wangyufu/FTP