本節導讀:python
一 什麼是scoketlinux
Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有。shell
socket起源於Unix,而Unix/Linux 基本哲學之一就是「一切皆文件」,均可以用「打開open –> 讀寫write/read –> 關閉close」模式 來操做。Socket就是該模式的一個實現,socket便是一種特殊的文件,一些socket函數就是對其進行的操做(讀/寫IO、打開、關閉)編程
你想給另外一臺計算機發消息,你知道他的IP地址,他的機器上同時運行着qq、迅雷、word、瀏覽器等程序,你想給他的qq發消息,那想一下,你如今只能經過ip找到他的機器,但若是讓這臺機器知道把消息發給qq程序呢?答案就是經過port,一個機器上能夠有0-65535個端口,你的程序想從網絡上收發數據,就必須綁定一個端口,這樣,遠程發到這個端口上的數據,就全會轉給這個程序設計模式
二 socket的工做流程瀏覽器
下面咱們舉個打電話的小例子來講明一下緩存
若是你要給你的一個朋友打電話,先撥號,朋友聽到電話鈴聲後提起電話,這時你和你的朋友就創建起了鏈接,就能夠講話了。等交流結束,掛斷電話結束這次交談。 生活中的場景就解釋了這工做原理。安全
(若是你去一家餐館吃飯,假設哪裏的老闆就是服務端,而你本身就是客戶端,當你去吃飯的時候,你確定的知道那個餐館,也就是服務端的地址吧,可是對於你本身來講,餐館的老闆不須要知道你的地址吧)服務器
三 socket套接字方法網絡
socket參數
family(socket家族)
socket type類型
(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
proto=0 請忽略,特殊用途
fileno=None 請忽略,特殊用途
socket函數
phone.bind('主機ip地址',端口號) #綁定到(主機,端口號)套接字 phone.listen() #開始TCP監聽 phone.accept() #被動接受TCP客戶的鏈接,等待鏈接的到來
s.connect() #主動初始化TCP服務器鏈接 s.connect_ex() #connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
s.recv() #接收數據 s.send()# 發送數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完,可後面經過實例解釋) s.sendall() #發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完) s.recvfrom() #Receive data from the socket. The return value is a pair (bytes, address) s.getpeername() #鏈接到當前套接字的遠端的地址 s.sendto() #發送UDP數據 s.getsockname() #返回指定套接字的參數 s.setsockopt() #設置指定套接字的參數 s.close() # 關閉套接字 socket.setblocking(flag) #True or False,設置socket爲非阻塞模式,之後講io異步時會用 socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) #返回遠程主機的地址信息,例子 socket.getaddrinfo('luffycity.com',80) socket.getfqdn()# 拿到本機的主機名 socket.gethostbyname() # 經過域名解析ip地址
phone.setblocking() #設置套接字的阻塞與非阻塞模式 phone.settimeout() #設置阻塞套接字操做的超時時間 phone.gettimeout() #獲得阻塞套接字操做的超時時間
phone.fileno() # 套接字的文件描述符 phone.makefile() #建立一個與該套接字相關的文件
四 socket 服務端,客戶端的建立
socket服務端建立
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#買手機 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #能夠屢次啓動 #執行屢次的時候會報錯,那麼怎麼辦呢、?就在綁卡前面加上上面那句setsockopt方法就ok了 phone.bind(('192.168.20.44',8080))#綁定手機卡(ip,端口) # 端口號在1024之前的是系統用的,1024之後的都是你本身寫的程序去定義的端口 print('starting run......') phone.listen(5) #開機 5表明的是最多掛起5個,也能夠好多個 while True: #連接循環 coon,client_addr=phone.accept()#等待接電話,(coon是創建的連接,客戶端的ip和端口號組成的元組) print(coon,client_addr) #收發消息 while True: #通訊循環 try: #若是不加try...except ,就會報錯,由於它不知道你何時斷開連接的,服務器還覺得你在運行 data = coon.recv(1024) #收了1024個字節的消息 print('client data 收到消息:%s'%data.decode('utf-8')) coon.send(data.upper()) #發消息 except Exception: #由於你不知道客戶端何時斷開連接, break coon.close() #掛電話 phone.close() #關機 # 處理邏輯錯誤的兩種方式: # if 判斷 # try...except 異常處理 # 異常處理 # 當你知道直接錯誤的條件時就用if判斷了 # 當程序錯誤必定發生,可是你又預知不了它出錯的條件是什麼的時候,就用try...except 服務端
socket客戶端建立
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#買手機 phone.connect(('192.168.20.44',8080)) #直接鏈接服務端的ip和端口 # 發收消息 while True: msg = input('>>:').strip() #用戶輸入 if not msg:continue #若是爲空就繼續輸 phone.send(msg.encode('utf-8')) # 發送你輸入的消息 # phone.send('hello'.encode('utf-8')) data = phone.recv(1024) #在接收一下 print('server back res服務端返回結果:>>%s'%data.decode('utf-8')) phone.close()
注意:
若是你在重啓服務端的時候可能遇到這樣的問題:
這個是因爲你的服務端仍然存在四次揮手的time_wait狀態在佔用地址(若是不懂,請深刻研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.服務器高併發狀況下會有大量的time_wait狀態的優化方法)。那麼怎麼解決呢?你也能夠這樣的
1 #加入一條socket配置,重用ip和端口 2 3 phone=socket(AF_INET,SOCK_STREAM) 4 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 5 phone.bind(('127.0.0.1',8080))
基於tcp協議模擬ssh遠程執行命令
import socket import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#買手機 phone.bind(('192.168.20.44',8081))#綁定手機卡 phone.listen(5)#阻塞的最大個數 print('starting....') while True: conn,addr=phone.accept()#等待鏈接 print(addr,conn) while True: cmd=conn.recv(10240)#接收的最大值 # if not cmd :break print('接收的是:%s'%cmd.decode('utf-8')) #處理過程 res=subprocess.Popen(cmd.decode('utf-8'),shell=True, #Popen是執行命令的方法 stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout=res.stdout.read() stuerr=res.stderr.read() conn.send(stdout+stuerr) conn.close() phone.close()
基於udp協議的socket
from socket import * udp_server = socket(AF_INET,SOCK_DGRAM) udp_server.bind(('127.0.0.1',8080)) #綁定 while True:#通信循環 msg,client_addr= udp_server.recvfrom(1024) print('收到的消息是:%s'%msg.decode('utf-8')) udp_server.sendto(msg.upper(),client_addr) udp_server.close()