在編程的過程當中,咱們須要使用網絡編程,這時咱們不得不和網絡通訊的底層基礎打交道了.咱們必須讓本身傳輸的數據符合網絡通訊的基本協議,即TCP/IP協議,可是網絡通訊協議自己很複雜.咱們不可能在編程的過程當中還本身去對數據進行封包處理,這是便出現了socket幫助咱們處理相關的數據傳輸,在其餘語言裏也能夠使用socket幫咱們處理相關問題.python
socket本質就是一組接口,是在應用層與TCP/IP協議族通訊中間的一個抽象層,龐大複雜的TCP/IP協議族咱們即可以不用過多關注,而只須要經過socket提供的接口就能夠相互鏈接.shell
s.bind() 在服務端綁定(主機,端口號)到套接字 s.listen() 開始TCP監聽 s.accept() 被動接受TCP客戶的鏈接,(阻塞式)等待鏈接的到來,收到的是元組形式,包含內容和地址 s.connect() 主動初始化TCP服務器鏈接 s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常 s.recv() 接收TCP數據 s.send() 發送TCP數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完) s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完) s.recvfrom() 接收UDP數據 s.sendto() 發送UDP數據 s.getpeername() 鏈接到當前套接字的遠端的地址 s.getsockname() 當前套接字的地址 s.getsockopt() 返回指定套接字的參數 s.setsockopt() 設置指定套接字的參數 s.close() 關閉套接字 s.setblocking() 設置套接字的阻塞與非阻塞模式 s.settimeout() 設置阻塞套接字操做的超時時間 s.gettimeout() 獲得阻塞套接字操做的超時時間 s.fileno() 套接字的文件描述符 s.makefile() 建立一個與該套接字相關的文件
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ##網絡編程須要調用AF_INET,而SOCK_STREAM表明數據流,即TCP方式 phone.bind(('192.168.1.101',9000)) ##綁定IP地址和端口,必須是元組 phone.listen(5) ##設置監聽數量 while True: conn, addr = phone.accept() ##獲得內容對象和地址 while True: try: ##異常處理特殊狀況 feedback = conn.recv(1024) ##獲得內容對象傳來的值,裏面規定字節數量 print(feedback.decode("UTF-8")) ##編譯,由於只有被編譯的字符才能被傳輸 msg = input(">>:") conn.send(msg.encode("UTF-8")) ##發送 except Exception: break conn.close() ##關閉鏈接了的對象 phone.close() ##關閉服務器
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.connect_ex(('192.168.1.101',9000)) ##鏈接對象 while True: msg = input(">>:") phone.send(msg.encode("UTF-8")) ##發送消息 feedback = phone.recv(1024) ##返回消息 print(feedback.decode("UTF-8")) phone.close() ##關閉對象
可能在屢次開啓關閉服務器的時候,會出現IP地址已經被使用了的狀況,咱們能夠用如下方法解決:編程
phone = socket(AF_INIT, SOCKET_STREAM) phone.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) phone.bind(('192.168.1.101',9000))
####UDP的服務端和客戶端示例緩存
from socket import * ip_addr = ('192.168.1.101',9000) buff_size = 1024 udp_server = socket(AF_INET, SOCK_DGRAM) ##SOCK_DGRAM表示UDP方式 udp_server.bind(ip_addr) ##綁定IP地址和端口 while True: data, addr = udp_server.recvfrom(buff_size) ##受信息 print(data.decode("utf-8")) udp_server.sendto(data.upper(), addr) ##發信息
from socket import * ip_addr = ('192.168.1.101',9000) buff_size = 1024 udp_client = socket(AF_INET, SOCK_DGRAM) while True: msg = input(">>:") udp_client.sendto(msg.encode("utf-8"), ip_addr) ##不須要綁定,只須要傳一個地址 date, addr = udp_client.recvfrom(buff_size) print(date.decode("utf-8"))
TCP | UDP | |
---|---|---|
傳輸方式 | 數據流(STREAM) | 數據報(DGRAM) |
致使問題 | 粘包 | 丟包 |
接待對象 | 一個 | 多個 |
鏈接 | 須要鏈接線 | 不須要鏈接線 |
TCP是以數據流的方式,在數據不少時,會屢次地不份內容關聯性地傳輸給對方內核態緩存,當用戶態緩存來取數據時,並不知道數據的開頭和結尾,那麼就出現了粘包現象.所以粘包問題主要仍是由於接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的,因此經過TCP方式不能發送空.服務器
然而,UDP方式是以信息爲單位傳輸數據,在數據裏面包含了開頭和結尾的界限,所以不會出現粘包的現象,可是當傳輸來的數據被儲存在內核態緩存裏面時,若是應用態緩存沒有一次性的取完數據,那麼數據就會被丟棄網絡
from socket import * ip_port = ('127.0.0.1', 9000) client = socket(AF_INET, SOCK_STREAM) client.connect_ex(ip_port) while True: command = input(">>:") if not command: continue client.send(command.encode("utf-8")) feedback_of_length = int(client.recv(1024).decode("gbk")) #先獲得數據的長度 client.send("OK".encode("gbk")) #而且發送能夠傳輸了的命令 all_data = b"" data_length = 0 while data_length < feedback_of_length: all_data += client.recv(1024) #循環地把數據聯結在一塊兒 data_length = len(all_data) last_date = all_data.decode("gbk") ##最後解碼 print(last_date) client.close()
from socket import * import subprocess ip_port = ('127.0.0.1', 9000) buff_size = 5 server = socket(AF_INET, SOCK_STREAM) server.bind(ip_port) server.listen(buff_size) while True: conn, addr = server.accept() while True: try: cmd = conn.recv(1024) if not cmd: continue res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stderr = subprocess.PIPE, stdin = subprocess.PIPE, stdout = subprocess.PIPE ) err = res.stderr.read() if not res: get_res = "Run Perfectly" elif err: get_res = err else: get_res = res.stdout.read() length_of_res = len(get_res) #發送數據長度 conn.send(str(length_of_res).encode("gbk")) last_info = conn.recv(1024) #獲得是否傳輸的指令 if last_info.decode("utf-8") == "OK": conn.send(get_res) except Exception: break
import socketserver class MyServer(socketserver.BaseRequestHandler): ##必需要繼承這個父類 def handle(self): ##必需要從新定義這個方法 while True: try: data = self.request.recv(1024) ##recv就至關於conn了 if not data: break print(data.decode("utf-8"), self.client_address) ##只有data和client_address這兩個量 self.request.sendall(data) except Exception: break if __name__ == "__main__": s = socketserver.ThreadingTCPServer(('192.168.1.101',9001), MyServer) ##實例化一個對象 s.serve_forever() ##永遠運行
import socketserver class MyServer(socketserver.BaseRequestHandle): def handle(self): ##(b'ad', <socket.socket fd=444, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, ## laddr=('192.168.1.101', 8081)>) request是一個元組 print(self.request[0].decode("utf-8")) self.request[1].sendto(self.request[0], self.client_address) if __name__ == "__main__": s = socketserver.ThreadingUDPServer(('192.168.1.101',8080), MyServer) s.server_forever()