Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。linux
因此,咱們無需深刻理解tcp/udp協議,socket已經爲咱們封裝好了,咱們只須要遵循socket的規定去編程,寫出的程序天然就是遵循tcp/udp標準的。編程
也有人將socket說成ip+port,ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程序,ip地址是配置到網卡上的,而port是應用程序開啓的,ip與port的綁定就標識了互聯網中獨一無二的一個應用程序
而程序的pid是同一臺機器上不一樣進程或者線程的標識
套接字分類:設計模式
基於文件類型的套接字家族緩存
套接字家族的名字:AF_UNIX服務器
基於網絡類型的套接字家族網絡
套接字家族的名字:AF_INETsocket
先從服務器端提及。服務器端先初始化Socket,而後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端鏈接。在這時若是有個客戶端初始化一個Socket,而後鏈接服務器(connect),若是鏈接成功,這時客戶端與服務器端的鏈接就創建了。客戶端發送數據請求,服務器端接收請求並處理請求,而後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉鏈接,一次交互結束tcp
socket()模塊函數用法ide
import socket socket.socket(socket_family,socket_type,protocal=0) socket_family 能夠是 AF_UNIX 或 AF_INET。socket_type 能夠是 SOCK_STREAM 或 SOCK_DGRAM。protocol 通常不填,默認值爲 0。 獲取tcp/ip套接字 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 獲取udp/ip套接字 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 因爲 socket 模塊中有太多的屬性。咱們在這裏破例使用了'from module import *'語句。使用 'from socket import *',咱們就把 socket 模塊裏的全部屬性都帶到咱們的命名空間裏了,這樣能 大幅減短咱們的代碼。 例如tcpSock = socket(AF_INET, SOCK_STREAM
實例: 基於TCP協議函數
服務端
import socket # 建立socket對象 指定type參數爲socket.SOCK_DGRAM 表示使用UDP協議 server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # datagram數據報的意思 # 綁定ip和端口 server.bind(("127.0.0.1",8888)) while True: # 接收數據 返回一個元祖 數據和 發送方的地址 msg,c_addr = server.recvfrom(1024) print("收到來自%s: 說:%s" % (c_addr[0] ,msg.decode("utf-8"))) # 發送數據到指定ip和端口 server.sendto(msg.upper(),c_addr)
客戶端
import socket client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # datagram數據報的意思 while True: msg = input(">>>:") client.sendto(msg.encode("utf-8"),("127.0.0.1",8888)) data,addr = client.recvfrom(1024) print(data.decode("utf-8"))
下面是 採用打電話的場景描述:
#服務器 # import socket # #買電話 # phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # #插卡 # phone.bind(('127.0.0.1',12012)) #端口範圍0-65535 0-1024 給系統用的 # #開機 # phone.listen(5) # 5 表示同一時間 最大接受請求值爲5 # #等電話 # conn,client_addr = phone.accept() # print('接受到',client_addr,'的電話') # #收信息 # msg = conn.recv(1024) # 1024是一個最大值 限制 # print(msg.decode('utf-8')) # #回信息 # conn.send('而後呢'.encode('utf-8')) # #掛電話 # conn.close() # #關機 # phone.close()
#客戶端 # import socket # # #買電話 # # phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # # #撥號 # # phone.connect(('127.0.0.1',12012)) # # #發信息 # # phone.send('哈哈哈哈哈哈哈哈'.encode('utf-8')) # # #收信息 # # data = phone.recv(1024) # # print(data.decode('utf-8')) # # #掛電話 # # phone.close()
先運行服務器,在運行客戶端,否則會:
Traceback (most recent call last): File "F:/練習/客戶端.py", line 18, in <module> phone.connect(('127.0.0.1',2323)) ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
tcp是基於連接的,必須先啓動服務端,而後再啓動客戶端去連接服務端
tcp服務端
服務端套接字函數
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() 建立一個與該套接字相關的文件
TCP服務端:
import socket # 使用TCP 能夠直接默認 server = socket.socket() # 指定端口 和 ip 端口 0 - 1023是系統保留的 server.bind(("127.0.0.1",65535)) # 監聽請求 參數爲最大半鏈接數(三次握手未完成的請求 多是服務器來不及 客戶端惡意攻擊) server.listen(5) # 爲了能夠不斷的接受客戶端鏈接請求 while True: # 接受鏈接請求 c,addr = server.accept() # 爲了能夠重複收發數據 while True: try: # 1024 程序的最大緩衝區容量 返回值類型爲bytes類型 data = c.recv(1024).decode("utf-8") # 若是客戶端斷開鏈接(客戶端調用了close) recv 返回值爲kong 此時應該結束循環 if not data:# 在linux中 客戶端異常關閉 服務器也會收空 print("client closed!") c.close() break #解碼 print(data) # 回覆數據 將原始數據轉爲大寫 c.send(data.upper().encode("utf-8")) except ConnectionResetError: print("客戶端異常關閉!!") c.close() break # 關閉資源 server.close() # TCP斷開鏈接的正確姿式 # 客戶端調用close # 服務器判斷若是接收數據爲空則相應的調用close