Python中socket解讀


操作系統底層原理

操作系統:(Operating System,簡稱OS)是管理和控制計算機硬件與軟件資源的計算機程序,是直接運行在“裸機”上的最基本的系統軟件,任何其他軟件都必須在操作系統的支持下才能運行。

注:計算機(硬件)->os->應用軟件

網絡通信原理

互聯網的本質就是一系列的網絡協議
一台硬設有了操作系統,然后裝上軟件你就可以正常使用了,然而你也只能自己使用
像這樣,每個人都擁有一台自己的機器,然而彼此孤立

如何能大家一起玩耍

1

結論:英語成為世界上所有人通信的統一標准,如果把計算機看成分布於世界各地的人,那么連接兩台計算機之間的internet實際上就是一系列統一的標准,這些標准稱之為互聯網協議,互聯網的本質就是一系列的協議,總稱為‘互聯網協議’(Internet Protocol Suite).

互聯網協議的功能:定義計算機如何接入internet,以及接入internet的計算機通信的標准。

網絡基礎架構

1.C/S 架構 :client客戶端 和 server服務器端 / 客戶機和服務器結構
	優點:
			1.充分利用兩端硬件環境的優勢,將任務合理分配
			2.能夠實現復雜的應用構造,安全性高,數據傳輸速度快。
			3.應用服務器運行數據負荷較輕
			4.數據的儲存管理功能較為透明
	缺點:
			1.高昂的維護成本且投資大
			
2.B/S架構:browser瀏覽器和server服務器端 / 瀏覽器和服務器結構
	優點:
			1.統一了應用的接口,簡化了客戶端電腦載荷,減輕了系統維護與升級的成本和工作量,
				降低了用戶的總體成本(TCO)。
			2.可以在任何地方進行操作而不用安裝任何專門的軟件,只要有一台能上網的電腦就能使用,
				客戶端零安裝、零維護。系統的擴展非常容易
	缺點:
			1.應用服務器運行數據負荷較重
			
注:C/S和B/S並沒有本質的區別:B/S是基於特定通信協議(HTTP)的C/S架構,也就是說B/S包含在C/S中,是特殊的C/S架構。之所以在C/S架構上提出B/S架構,是為了滿足瘦客戶端、一體化客戶端的需要,最終目的節約客戶端更新、維護等的成本,及廣域資源的共享。
		1.B/S屬於C/S,瀏覽器只是特殊的客戶端;
		2.C/S可以使用任何通信協議,而B/S這個特殊的C/S架構規定必須實現HTTP協議;
		3.瀏覽器是一個通用客戶端,本質上開發瀏覽器,還是實現一個C/S系統。
		
應用場景:
B/S適用於用戶群龐大,或客戶需求經長發生變化的情況。
C/S功能強大,可以減輕服務器端壓力,如果用戶的需求特別復雜,用C/S。

CS/BS 區別

局域網與交換機/網絡常見術語

原主機第一次查詢廣播,目標機收到就返回自己的ip地址和mac地址
回去用單播,因為有互相的ip地址了,交換機五分鍾清空記錄
廣播:有了mac地址,同一網絡內的兩台主機就可以通信了(一台主機通過arp協議獲取另外一台主機的mac地址)
ethernet采用最原始的方式,廣播的方式進行通信,即計算機通信基本靠吼

1.單播(Unicast)
		交換機記錄mac地址,是在一個單個的發送者和一個接受者之間通過網絡進行的通信。
2.組播
		傳輸在發送者和每一接收者之間實現點對多點網絡連接。 如果一台發送者同時給多個接收者傳輸相同的數據,也只需復制一份相同的數據包。 它提高了數據傳送效率,減少了骨干網絡出現擁塞的可能性。
3.Mac地址
		是物理地址:唯一的作用可以唯一標識一台電腦,相當於一個人是身份證
4.ipv4地址
		四位點分十進制 相當於當前所在位置的定位,相當於一個人的學號
5.請求幀
		ip Mac 要找的ip
6.arp協議
		通過ip地址獲取目標Mac地址的協議
7.端口
		操作系統為本機上運行的程序都隨機分配一個端口,其他電腦上的程序可以通過端口獲取到這個程序
8.子網掩碼(subnet mask)
		將某個IP地址划分成網絡地址和主機地址兩部分
9.默認網關(default gateway)
		網關內的所有ip向外通訊都要經過它,出口和入口
10.DNS服務器
		域名服務器(Domain Name Server)在Internet上域名與IP地址之間是一一對應的,域名雖然便於人們記憶,但機器之間只能互相認識IP地址,它們之間的轉換工作稱為域名解析,域名解析需要由專門的域名解析服務器來完成,DNS就是進行域名解析的服務器 
11.DHCP服務器
		就是能夠自動識別當前網絡環境,並依照當前網絡環境給你分配合適的ip地址的服務器。它的存在就是使手動設置Ip變得更加傻瓜式,實現一鍵上網。不需要你自己進行手動的配置
12.ping
		it術語中的ping類似於下圖的聲吶設置,如果目的電腦聯網,目的電腦會把這個Ping包發送回來。如果目的電腦不聯網,則ping包不會返還任何信息。所以在It界會經常使用這個命令來檢測電腦是否已經連上網絡
13.網段號
		我們知道ip由兩部分構成 網段號+主機號。當我們使用自身的子網掩碼和目的主機進行&運算時,我們就能得出目的主機的網段號,進而判斷目的主機與自己是否處在同一個網絡中。

注:交換機和路由器的區別:
		交換機主要功能:組織局域網,經過交換機內部處理解析信息之后,將信息已點對點,點對多的形式,發送給固定端
    交換機是利用物理地址或者說MAC地址來確定轉發數據的目的地址。
    路由器主要功能:進行跨網段進行數據傳輸,路由選擇最佳路徑
    注:如果需要將多太電腦連接到一根網線,用交換機即可
    如果只有一個外網ip,多台電腦想上網,用路由即可

OSI七層模型

互聯網協議按照功能不同分為osi七層或tcp/ip五層或tcp/ip四層

每層運行常見物理設備

1

TCP/IP五層模型講解

我們將應用層,表示層,會話層並作應用層,從tcp/ip五層協議的角度來闡述每層的由來與功能,搞清楚了每層的主要協議,就理解了整個互聯網通信的原理。

首先,用戶感知到的只是最上面一層應用層,自上而下每層都依賴於下一層,所以我們從最下一層開始切入,比較好理解,每層都運行特定的協議,越往上越靠近用戶,越往下越靠近硬件。

物理層

如:光纖,集線器,網線

物理層由來:

​							1.實現計算機之間物理連接

​							2.計算機之間交流必須完成組網。

物理層功能:

​							1.主要是基於電器特性發送高低電壓(電信號),高電壓對應數字1,低電壓對應數字0

數據鏈路層(arp協議)

如:交換機,網卡,網橋

數據鏈路層由來:

​							1.規定了二進制數據分組原理

​							2.規定了只要接入物聯網的計算機,都必須有一塊網卡,網卡號唯一

​							3.單純的電信號0和1沒有任何意義,必須規定電信號多少位一組,每組什么意思

​							注:其實也就是以太網協議 

數據鏈路層的功能:

​							1.定義了電信號的分組方式

網絡層(ip協議)

如:路由器,三層交換機,

網絡層功能:

​							1.規定了計算機必須有一個ip地址

​							2.ip協議可以跨局域網傳輸

​							3.引入一套新的地址用來區分不同的廣播域/子網,這套地址即網絡地址

傳輸層(端口協議,UDP,TCP)

如:四層交換機,四層路由器

​							TCP,UDP基於端口工作的協議

​							端口(port):	唯一標識一台機器上某一個基於網絡通信的應用程序

​							端口范圍:(動態分配)0-65535,0-1023為系統端口,也叫BSD保留端口

​							(1024-8000之間都有使用的),起端口最好在8000后

傳輸層的由來:

​							網絡層的ip幫我們區分子網,以太網層的mac幫我們找到主機,然后大家使用的都是應用程序,你的電腦上可能同時開啟qq,暴風影音,等多個應用程序,

傳輸層功能:

​							建立端口到端口的通信

tcp三次握手和四次揮手

建立連接三次握手:

1.客戶端向服務器發送建立連接請求 ,主機A向主機B發送TCP連接請求數據包
2.服務器收到請求后發送確認請求給客戶端 ,主機B收到請求后,會返回連接確認數據包
3.客戶端收到信息后確認且返回信息 ,主機A收到主機B的確認報文后,還需作出確認

斷開連接四次揮手:

1.客戶端先向服務器發送斷開請求,等待服務器確認
2.服務器向客戶端發送信息確認釋放報文
3.服務器再發送關閉信息給客戶端確認釋放端口
4.客戶端收到后返回一個確認信息給服務器
注:服務器發送關閉信息給客戶端,如果沒有接受到返回信息,會等待倆個報文的時間在這之間也會一段時間發一次,超過就自動銷毀關閉報文

應用層(HTTP,HTTPS,FTP)

應用層功能:規定應用程序的數據格式。

每層常見的協議

TCP協議和UDP協議

TCP(Transmission Control Protocol)可靠的、面向連接的協議(eg:打電話)、傳輸效率低全雙工通信(發送緩存&接收緩存)、面向字節流。

使用TCP的應用:Web瀏覽器;電子郵件、文件傳輸程序。

TCP協議的編碼流程

服務器端

​ 1.實例化對象

​ 2.綁定IP地址和端口號

​ 3.監聽

​ 4.接收客戶端的連接

​ 5.收發

​ 6.關閉

客戶端:

​ 1.實例化對象

​ 2.連接服務器

​ 3.收發

​ 4.關閉

UDP(User Datagram Protocol)不可靠的、無連接的服務,傳輸效率高(發送前時延小),一對一、一對多、多對一、多對多、面向報文,盡最大努力服務,無擁塞控制。

使用UDP的應用:域名系統 (DNS);視頻流;IP語音(VoIP)

粘包

只有TCP有粘包現象,UDP永遠不會粘包

TCP協議是面向流的協議:

發送端可以是一K一K地發送數據,而接收端的應用程序可以兩K兩K地提走數據,當然也有可能一次提走3K或6K數據,或者一次只提走幾個字節的數據,也就是說,應用程序所看到的數據是一個整體,或說是一個流(stream),一條消息有多少字節對應用程序是不可見的,因此TCP協議是面向流的協議,這也是容易出現粘包問題的原因,相當於一條河流你用刀砍是不可能砍斷的,字節流沒有明確的邊界。

UDP是面向消息的協議:

每個UDP段都是一條消息,應用程序必須以消息為單位提取數據,不能一次提取任意字節的數據,這一點和TCP是很不同的

粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的

粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的數據后才發送一個TCP段。若連續幾次需要send的數據都很少,通常TCP會根據優化[算法](http://lib.csdn.net/base/datastructure)把這些數據合成一個TCP段后一次發送出去,這樣接收方就收到了粘包數據。

1. TCP(transport control protocol,傳輸控制協議)是面向連接的,面向流的,提供高可靠性服務。收發兩端(客戶端和服務器端)都要有一一成對的socket,因此,發送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle算法),將多次間隔較小且數據量小的數據,合並成一個大的數據塊,然后進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通信是無消息保護邊界的。
2. UDP(user datagram protocol,用戶數據報協議)是無連接的,面向消息的,提供高效率服務。不會使用塊的合並優化算法,, 由於UDP支持的是一對多的模式,所以接收端的skbuff(套接字緩沖區)采用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對於接收端來說,就容易進行區分處理了。 即面向消息的通信是有消息保護邊界的。
3. tcp是基於數據流的,於是收發的消息不能為空,這就需要在客戶端和服務端都添加空消息的處理機制,防止程序卡住,而udp是基於數據報的,即便是你輸入的是空內容(直接回車),那也不是空消息,udp協議會幫你封裝上消息頭

udp的recvfrom是阻塞的,一個recvfrom(x)必須對唯一一個sendinto(y),收完了x個字節的數據就算完成,若是y>x數據就丟失,這意味着udp根本不會粘包,但是會丟數據,不可靠

tcp的協議數據不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端總是在收到ack時才會清除緩沖區內容。數據是可靠的,但是會粘包。

兩種情況下會發生粘包

1.發送端需要等緩沖區滿才發送出去,造成粘包(發送數據時間間隔很短,數據了很小,會合到一起,產生粘包)

2.接收方不及時接收緩沖區的包,造成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候還是從緩沖區拿上次遺留的數據,產生粘包) 

總結:

		在tcp協議中,有一個合包機制(nagle算法),將多次連續發送且間隔較小的數據,進行打包成一塊數據傳送. 

還有一個機制是拆包機制,在發送端因為受到網卡的MTU限制,會將大的超過MTU限制的數據,進行拆分,

拆分成多個小的數據,進行傳輸,當傳輸到目標主機的操作系統層時,會重新將多個小的數據合並成原本的數據

udp不會發生粘包,udp協議本層對一次收發數據大小的限制是:65535 - ip包頭(20) - udp包(8) = 65507,

站在數據鏈路層,因為網卡的MTU一般被限制在了1500,所以對於數據鏈路層來說,一次收發數據的大小被限制在  1500 - ip包頭(20) - udp包頭(8) = 1472,1472< num < 65507  會在數據鏈路層拆包,而udp本身就是不可靠協議,所以一旦拆包之后,造成的多個小數據包在網絡傳輸中,如果丟任何一個,那么此次數據傳輸失敗。

對應方法

			有矛必有盾,知道了粘包的原理就是應為接收方不知道每次傳輸數據的大小導致的,我們可以在每次發送的時候告知接收方這次數據的大小是多少字節,下次就接收多少字節,就解決了粘包問題

Python中Socket模塊解讀

cockeet是什么?

Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。

socket又叫做套接字

分為多種:

1.基於文件類型的套接字:AF_UNIX
unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信
2.基於網絡類型的套接字:AF_INET/6
還有AF_INET6被用於ipv6,還有一些其他的地址家族,不過,他們要么是只用於某個平台,要么就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由於我們只關心網絡編程,所以大部分時候我么只使用AF_INET

套接字工作流程

​ 一個生活中的場景。你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲后提起電話,這時你和你的朋友就建立起了連接,就可以講話了。等交流結束,掛斷電話結束此次交談。 生活中的場景就解釋了這工作原理。

服務器端先初始化Socket,然后與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。

客戶端初始化一個Socket,然后連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然后把回應數據發送給客戶端,客戶端讀取數據,最后關閉連接,一次交互結束

socket()模塊函數用法

import socket  # 導入socket
socket.socket(socket_family,socket_type,protocal=0)
# 參數:
socket_family可以是 AF_UNIX 或 AF_INET
(AF_UNIX : 文件類型的套接字
AF_INET/6 :網絡類型的套接字)
# 不寫默認是基於網絡

socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM
(SOCK_STREAM:面向連接的穩定數據傳輸,即TCP協議)
(SOCK_DGRAM:基於UDP的,專門用於局域網)

protocol 一般不填,默認值為 0 默認為0填寫的就是tcp協議

#獲取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)

涉及到的參數

AF_UNIX : 文件類型的套接字
AF_INET/6 :網絡類型的套接字
SOCK_STREAM:提供面向連接的穩定數據傳輸,即TCP協議
SOCK_DGRAM :是基於UDP的,專門用於局域網
protocal : 協議默認為0填寫的就是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()        創建一個與該套接字相關的文件

Socket TCP通訊

單次通訊socket

服務端

# Server
import socket

# 實例化對象

server = socket.socket()

# 綁定ip和端口

server.bind(('127.0.0.1', 8000))

# 開監聽,半連接池,最大等待用戶

server.listen(5)

# 被動接受TCP客戶的連接,(阻塞式)等待連接的到來

sock, addr = server.accept()  # sock 連接通道 和 ip地址

msg = sock.recv(1024).decode('utf-8')  # 單次接收的最大字節

print(msg)

# 返回給客戶端信息
sock.send('已收到'.encode('utf-8'))

sock.close()  # 關閉連接
server.close()  # 關閉socket

客戶端

# client
import socket

# 實例化對象
client = socket.socket()

# 綁定
client.connect(('127.0.0.1', 8000))

# 發送信息給服務器
client.send('123'.encode('utf-8'))

# 接收字節
msg = client.recv(1024).decode('utf-8')
print(msg)

# 關閉
client.close()

鏈接循環

服務端

# server
import socket

# 實例化對象 = 開機
server = socket.socket()
# 綁定 = 插電話卡
server.bind(('127.0.0.1', 18000))
# 開監聽,半連接池,最大等待用戶
server.listen(5)

while True:
    # 被動接受TCP客戶的連接,(阻塞式)等待連接的到來
    # 來電話建立通訊
    sock, addr = server.accept()  # sock 連接通道 和 ip地址
    while True:
        # 等於每次來電話他最多說多少個字
        try:
            msg = sock.recv(1024).decode('utf-8')  # 單次接收的最大字節
            print(msg)
            # 返回給客戶端信息
            # 等於對方說話你給他說的回復信息
            sock.send('已收到'.encode('utf-8'))
        except Exception:
            break
            # 如果客戶端異常斷開服務器也會斷開,所有用異常處理
        # 掛電話
    sock.close()  # 關閉連接

server.close()  # 關閉socket 也就等於出現意外的異常直接關閉

客戶端

# client
import socket

# 實例化對象
client = socket.socket()

# 綁定
client.connect(('127.0.0.1', 18000))

# 發送信息給服務器
while True:
    msg = input('發送給服務器的內容:').encode('utf-8')

    if not msg:
        print('你自己終止了程序')
        break

    client.send(msg)
    # 接收字節
    msg = client.recv(1024).decode('utf-8')
    print('服務器發送回來的內容:%s' % msg)

Socket UDP通訊

單次通訊

服務端

import socket

server = socket.socket(type=socket.SOCK_DGRAM)  # UDP協議傳輸
server.bind(('127.0.0.1', 8080))  # 綁定ip和端口

# 收發

msg, addr = server.recvfrom(1024)  # 接收來自於哪里的數據
print(msg.decode('utf-8'))

server.close()

客戶端

import socket

client = socket.socket(type=socket.SOCK_DGRAM)  # UDP協議傳輸

msg = input('發送給服務器端信息:').encode('utf-8')

client.sendto(msg, ('127.0.0.1', 8080))  # 發送數據給哪里

client.close()

循環鏈接

服務端

import socket

server = socket.socket(type=socket.SOCK_DGRAM)  # UDP協議傳輸
server.bind(('127.0.0.1', 8080))  # 綁定ip和端口

# 收發
while True:
    msg, addr = server.recvfrom(1024)  # 接收來自於哪里的數據
    print("來自客戶端%s的信息:%s" % (addr, msg.decode('utf-8')))

    # 發送信息給客戶端
    msg_c = input('發送給客戶端的數據:')
    server.sendto(msg_c.encode('utf-8'), addr)  # 發送信息給客戶端,addr里面是客戶端的ip和端口

server.close()

客戶端

import socket

client = socket.socket(type=socket.SOCK_DGRAM)  # UDP協議傳輸

while True:
    msg = input('發送給服務器端信息:').encode('utf-8')

    if not msg:
        print('輸入為空,客戶端自動退出')
        break

    client.sendto(msg, ('127.0.0.1', 8080))  # 發送數據給哪里

    # 接收來來自於服務器的數據
    msg_s, addr = client.recvfrom(1024)
    print("來自服務器%s的信息:%s" % (addr, msg_s.decode('utf-8')))

client.close()

UDP協議及編碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM