早期的計算機網絡,都是由廠商規定本身的通訊協議,互不兼容,爲了把全世界不一樣類型的計算機鏈接起來,就必須規定一套全球通用的協議,因此就出現了TCP/IP數據庫
要解決怎麼標識一個進制,在一臺電腦上能夠同pid標識進程,可是在網絡上是作不到的,其實TCP/IP就幫咱們解決了這個問題,網絡層的IP能夠標識在網絡上的主機,而傳輸層的協議+端口就能夠標識主機中編程
socket是進程通訊的的一種方式,它與其餘進程通訊的不一樣是,它能實現不一樣主機之間的進程通訊,咱們網絡的應用大多數都是採用這種方式進行通訊的服務器
在Python中使用socket模塊網絡
import socket socket.socket(AddressFamily, Type)
函數socket能夠建立一個socket對象,該函數存在兩個參數併發
Address Family:能夠選擇 AF_INET(用於 Internet 進程間通訊) 或者 AF_UNIX(用於同一臺機器進程間通訊),實際工做中經常使用AF_INETsocket
Type:套接字類型,能夠是 SOCK_STREAM(流式套接字,主要用於 TCP 協議)或者 SOCK_DGRAM(數據報套接字,主要用於 UDP 協議)tcp
建立一個tcp套接字ide
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.close()
建立一個udp套接字函數
import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.close()
Socket函數大數據
bind(address) 將套接字綁定到地址,在AF_INET下,以元祖(hsot,port)的形式表示地址 listen(backlog) 開始監聽TCP傳入鏈接,backlog指定能夠掛起的最大鏈接數 accept() 接收TCP鏈接並返回(conn,address),其中conn是新的套接字對象,address是鏈接客戶端的地址 connect(address) 鏈接到address處的套接字,以元祖(hsot,port)的形式表示地址,鏈接出錯返回socket.error錯誤 connect_ex(address) 功能與s.connect(address) ,可是成功返回0,失敗返回errno的值 recv(bufsize[,flag]) 接收TCP套接字的數據,數據以字節形式返回,bufsize指定接收的最大數據量,flag提供有關消息的其餘信息,一般能夠忽略 send(string[,flag]) 發送TCP數據,將string中的數據發送到鏈接的套接字,返回值是要發送的字節數量 sendall(string[],flag) 完整的發送TCP數據,返回以前會嘗試發送全部數據,成功返回Nonne,失敗拋出異常 recvfrom(bufsize[,flag]) 接收UDP套接字的數據,與s.recv()相似,但返回值是(data,address),data表示接收的數據,address表示發送數據的套接字地址 sendto(string[,flag],address) 發送UDP數據,將數據發送到套接字,address是形式爲(ipaddr,port)的元組,返回值是發送的字節數 close() 關閉套接字 getpeername() 返回鏈接套接字的遠程地址,返回值是形式爲(ipaddr,port)的元組 getsockname() 返回u套接字本身的地址,返回值是形式爲(ipaddr,port)的元組 setsockopt(level,optname,value) 設置給定套接字選項的值 setsockopt(level,optname[.buflen]) 返回套接字選項的值 settimeout(timeout) 設置套接字及操做的朝時期,tiemout爲一個浮點數,單位是秒,值爲None表示永遠沒有朝時期 setblocking(flag) 若是flag爲0,則將套接字設爲非阻塞模式,非阻塞模式下,若是調用recv()沒有接收到任何數據,或send()沒法發送數據,將引發socket.error異常
客戶端
from socket import * # 建立socket tcp_client_socket = socket(AF_INET, SOCK_STREAM) # 目的信息 server_ip = input("請輸入服務器ip:") server_port = int(input("請輸入服務器port:")) # 連接服務器 tcp_client_socket.connect((server_ip, server_port)) # 提示用戶輸入數據 send_data = input("請輸入要發送的數據:") tcp_client_socket.send(send_data.encode("gbk")) # 接收對方發送過來的數據,最大接收1024個字節 recvData = tcp_client_socket.recv(1024) print('接收到的數據爲:', recvData.decode('gbk')) # 關閉套接字 tcp_client_socket.close()
服務端
from socket import * # 建立socket tcp_server_socket = socket(AF_INET, SOCK_STREAM) # 綁定 tcp_server_socket.bind(('',9420)) # 使用socket建立的套接字默認的屬性是主動的,使用listen將其變爲被動的,這樣就能夠接收別人的連接了 tcp_server_socket.listen(128) # 等待鏈接,產生一個新的socket client_socket, clientAddr = tcp_server_socket.accept() # 接收對方發送過來的數據 recv_data = client_socket.recv(1024) # 接收1024個字節 print('接收到的數據爲:', recv_data.decode('gbk')) # 發送一些數據到客戶端 client_socket.send("thank you !".encode('gbk')) # 關閉套接字,只要關閉了,就意味着爲不能再爲這個客戶端服務了,若是還須要服務,只能再次從新鏈接 client_socket.close() tcp_server_socket.close()
客戶端
from socket import * def main(): tcp_client_socket = socket(AF_INET, SOCK_STREAM) server_ip = input("請輸入服務器ip:") server_port = int(input("請輸入服務器port:")) tcp_client_socket.connect((server_ip, server_port)) file_name = input("請輸入要下載的文件名:") tcp_client_socket.send(file_name.encode("utf-8")) msg = '' while True: recv_data = tcp_client_socket.recv(1024) msg += recv_data.decode('utf-8') if len(recv_data) < 1024: break if msg: with open(file_name + 'bak', "w") as f: f.write(msg) tcp_client_socket.close() if __name__ == "__main__": main()
服務端
from socket import * import sys def get_file_content(file_name): """獲取文件的內容""" try: with open(file_name, "rb") as f: content = f.read() return content except: print("沒有下載的文件:%s" % file_name) def main(): tcp_server_socket = socket(AF_INET, SOCK_STREAM) tcp_server_socket.bind(('',9420)) tcp_server_socket.listen(128) while True: client_socket, clientAddr = tcp_server_socket.accept() recv_data = client_socket.recv(1024) file_name = recv_data.decode("utf-8") print("對方請求下載的文件名爲:%s" % file_name) file_content = get_file_content(file_name) if file_content: client_socket.send(file_content) client_socket.close() tcp_server_socket.close() if __name__ == "__main__": main()
client向server發起鏈接
server接收到請求,雙方創建鏈接
client向server發送消息
server迴應client
一次讀寫完畢,鏈接繼續
直到client發起關閉請求
client向server發起鏈接
server接收到請求,雙方創建鏈接
client向server發送消息
server迴應client
一次讀寫完成,client發起斷開鏈接請求
長鏈接
短鏈接
長鏈接能夠省去較多的TCP建立和關閉的操做,減小浪費,節約時間,對於頻繁請求資源的場景來講,適合用長鏈接,可是隨着客戶端鏈接愈來愈多,server端遲早扛不住,這時候就須要採起一些策略,例如關閉一些長時間沒有讀取的鏈接,這樣能夠避免惡意鏈接,還能夠限制每一個客戶端的最長鏈接數,這樣能夠避免某個客戶端拖後腿,短鏈接控制簡單,不須要控制手機,可是若是客戶頻繁的請求資源,那就比較操蛋了,浪費時間,浪費帶寬
長鏈接適用於操做頻繁,點對點的的通信,並且鏈接數不是太多的狀況,每一個TCP須要三次握手,若是每一個操做都是先鏈接,再操做,會浪費很長的時間,因此每一個操做以後咱們就不給它斷開,再次操做直接發送請求就能夠了,例如,數據庫
像WEB網站的http服務通常採用短鏈接,由於長鏈接對服務器佔用的資源太多,並且http服務的鏈接數通常不會太少,服務器難說能扛得住,因此併發量高的場景,最好採用短鏈接
from socket import * udp_socket = socket(AF_INET, SOCK_DGRAM) dest_addr = ('', 9420) send_data = input("請輸入要發送的數據:") udp_socket.sendto(send_data.encode('utf-8'), dest_addr) recv_data = udp_socket.recvfrom(1024) print(recv_data[0].decode('gbk')) print(recv_data[1]) udp_socket.close()
import socket def send_msg(udp_socket): msg = input("\n請輸入要發送的數據:") dest_ip = input("\n請輸入對方的ip地址:") dest_port = int(input("\n請輸入對方的port:")) udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port)) def recv_msg(udp_socket): recv_msg = udp_socket.recvfrom(1024) recv_ip = recv_msg[1] recv_msg = recv_msg[0].decode("utf-8") print(">>>%s:%s" % (str(recv_ip), recv_msg)) def main(): udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_socket.bind(("", 9420)) while True: print("="*30) print("1:發送消息") print("2:接收消息") print("="*30) op_num = input("請輸入要操做的功能序號:") if op_num == "1": send_msg(udp_socket) elif op_num == "2": recv_msg(udp_socket) else: print("輸入有誤,請從新輸入...") if __name__ == "__main__": main()
面向鏈接,通訊雙方必須創建鏈接才能進行數據的傳輸,雙方必須爲對象分配必要的系統資源,TCP發送的每一個報文段都必須獲得接收方的應答才認爲傳輸成功,發送端若是在規定時間內沒有收到接收端的應答,發送端會將報文段從新發送,TCP還會進行數據校驗,還會經過流量控制機制避免主機發送太快而讓接收端接收不到數據,完成數據交換後,通訊雙方必須斷開鏈接,以釋放系統資源,這種鏈接是點對點的,所以TCP不適用廣播應用程序
UDP並不提供對IP協議的可靠機制、流控制以及錯誤恢復功能等,因爲UDP比較簡單, UDP頭包含不多的字節,比 TCP 負載消耗少,UDP 適用於不須要 TCP 可靠機制的情形,QQ就是採用的UDP協議
TCP
UDP