1. 程式人生 > >網路通訊協議和 tcp udp在 socket 中的區別 網路通訊協議

網路通訊協議和 tcp udp在 socket 中的區別 網路通訊協議

 

網路通訊協議

 

本節主要內容:

1.osi七層模型

2.socket

一、osi七層模型

  網際網路的核心就是由一堆協議組成的,協議就是標準,標準就是大家都認可的,所有人都按照這個來,這樣大家都能夠互相瞭解,互相深入。比如全世界通用語言是英語。

 

五層通訊流程:

 

二、socket

  結合上面五層通訊流程來看,socket在哪一層呢??

  1.socket在內的五層通訊流程:

  Socket又稱為套接字,它是應用層與TCP/IP協議族通訊的中間軟體抽象層,它是一組介面。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。當我們使用不同的協議進行通訊時就得使用不同的介面,還得處理不同協議的各種細節,這就增加了開發的難度,軟體也不易於擴充套件(就像我們開發一套公司管理系統一樣,報賬、會議預定、請假等功能不需要單獨寫系統,而是一個系統上多個功能介面,不需要知道每個功能如何去實現的)。於是UNIX BSD就發明了socket這種東西,socket遮蔽了各個協議的通訊細節,使得程式設計師無需關注協議本身,直接使用socket提供的介面來進行互聯的不同主機間的程序的通訊。這就好比作業系統給我們提供了使用底層硬體功能的系統呼叫,通過系統呼叫我們可以方便的使用磁碟(檔案操作),使用記憶體,而無需自己去進行磁碟讀寫,記憶體管理。socket其實也是一樣的東西,就是提供了tcp/ip協議的抽象,對外提供了一套介面,同過這個介面就可以統一、方便的使用tcp/ip協議的功能了。

  其實站在你的角度上看,socket就是一個模組。我們通過呼叫模組中已經實現的方法建立兩個程序之間的連線和通訊。也有人將socket說成ip+port,因為ip是用來標識網際網路中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程式。 所以我們只要確立了ip和port就能找到一個應用程式,並且使用socket模組來與之通訊。

 

2.套接字socket的發展史及分類

  套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 因此,有時人們也把套接字稱為“伯克利套接字”或“BSD 套接字”。一開始,套接字被設計用在同 一臺主機上多個應用程式之間的通訊。這也被稱程序間通訊,或 IPC。套接字有兩種(或者稱為有兩個種族),分別是基於檔案型的和基於網路型的。

  基於檔案型別的套接字家族

  套接字家族的名字:AF_UNIX

  unix一切皆檔案,基於檔案的套接字呼叫的就是底層的檔案系統來取資料,兩個套接字程序執行在同一機器,可以通過訪問同一個檔案系統間接完成通訊。

  

  基於網路型別的套接字家族:

  套接字家族的名字:AF_INET

  (還有AF_INET6被用於ipv6,還有一些其他的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支援很多地址家族,但是由於我們只關心網路程式設計,所以大部分時候我們只使用AF_INET)

 

3.基於TCP和UDP兩個協議下socket的通訊流程

  TCP(Transmission Control Protocol)可靠的、面向連線的協議(eg:打電話)、傳輸效率低全雙工通訊(傳送快取&接收快取)、面向位元組流。使用TCP的應用:Web瀏覽器;檔案傳輸程式。

  UDP(User Datagram Protocol)不可靠的、無連線的服務,傳輸效率高(傳送前時延小),一對一、一對多、多對一、多對多、面向報文(資料包),盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統 (DNS);視訊流;IP語音(VoIP)。

看下圖的對比差異:

 

 接續往下看:

TCP和UDP下的socket差異對比圖:

  上面的圖只是讓大家感受一下TCP和UDP協議下,socket工作流程的不同,兩者之間的差異是TCP需要連線,UDP不需要。現在是不是有點迷糊!這裡面的bind、listen啥的都是什麼東西啊。下面我們就分開兩者。

 

三、TCP協議下的socket

  基於TCP的socket通訊流程圖片:

  

  雖然上圖將通訊流程中的大致描述了一下socket各個方法的作用,但是還是要總結一下通訊流程(下面一大段內容)

  先從伺服器端說起,伺服器端先初始化socket,然後與埠繫結(bind()),對介面進行監聽(listen()),呼叫accept阻塞,等待客戶端連線。在這時如果有一個客戶端初始化一個socket,然後連線伺服器(connect),如果連線成功,這時客戶端與伺服器的連線就建立了。客戶端傳送資料請求,伺服器端接收請求並處理請求,然後把迴應資料發給客戶端,客戶端讀取資料,最後關閉連線,一次互動結束。

  上程式碼感受下,需要建立兩個檔案,檔名我這裡為了方便看,我的兩個檔名稱為tcp_server.py(服務端)和tcp_client.py(客戶端),將下面的server端的程式碼拷貝到tcp_server.py檔案中,將下面client端的程式碼拷貝到tcp_client.py中,然後先執行服務端,再執行客戶端。

  server端程式碼例項(如果比喻成打電話)

 

  解決辦法:

  但是如果你加上了上面的程式碼之後還是出現這個問題:OSError: [WinError 10013] 以一種訪問許可權不允許的方式做了一個訪問套接字的嘗試。那麼只能換埠了,因為你的電腦不支援埠重用。

  記住一點,用socket進行通訊,必須是一收一發對應好。

  提一下:網路相關或者需要和電腦上其他程式通訊的程式才需要開一個埠。

 

  TCP協議下的socket實現多人通話, listen(3)  要排隊等待的有三個客戶端

  server端

import socket
server = socket.socket()
server_ip = ("127.0.0.1",1026)
server.bind(server_ip)
server.listen(3)
while 1 :
    conn, address = server.accept()
    while 1 :

        from_client = conn.recv(1024)
        print("高熠超說:",from_client.decode("utf-8"))
        a = input("我說:")
        if a.upper() == "Q":
            break
        else :
            conn.send(a.encode("utf-8"))

  

  client端

import socket
client = socket.socket()
client_ip = ("127.0.0.1",1026)
client.connect(client_ip)
while 1 :
    a = input("我說:")
    client.send(a.encode("utf-8"))
    from_server = client.recv(1024)
    print("甜甜說:",from_server.decode("utf-8"))
    if a.upper() == "Q" :
        break
client.close()

  

  你會發現,第一個連線的客戶端可以和服務端收發訊息,但是第二個連線的客戶端發訊息服務端是收不到的

   原因解釋:

    tcp屬於長連線,長連線就是一直佔用著這個連結,這個連線的埠被佔用了,第二個客戶端過來連線的時候,他是可以連線的,但是處於一個佔線的狀態,就只能等著去跟服務端建立連線,除非一個客戶端斷開了(優雅的斷開可以,如果是強制斷開就會報錯,因為服務端的程式還在第一個迴圈裡面),然後就可以進行和服務端的通訊了。什麼是優雅的斷開呢?看程式碼。  

強制斷開連線之後會報錯,報錯資訊:

 

四、UDP協議下的socket

  基於UDP的socket通訊流程:

 

  

  總結一下UDP下的socket通訊流程

  先從伺服器端說起。伺服器端先初始化Socket ( 建立物件 utp_server = socket.socket ( type = socket.SOCK_DGRAM ) ),然後與埠繫結(bind),recvform接收訊息,這個訊息有兩項,訊息內容和對方客戶端的地址,然後回覆訊息時也要帶著你收到的這個客戶端的地址,傳送回去,最後關閉連線

  

  server端程式碼的例項:(實現多人與服務端通話)

  server端

import socket
utp_server = socket.socket(type=socket.SOCK_DGRAM)
utp_server_ip = ("127.0.0.1",1030)
utp_server.bind(utp_server_ip)
while 1 :
    from_client,client_add = utp_server.recvfrom(1024)
    print("客戶端說:",from_client.decode("utf-8"),"客戶端的地址和埠:",client_add)
    a = input("我說:")
    utp_server.sendto(a.encode("utf-8"),client_add)

  

  client端

import socket
utp_client = socket.socket(type=socket.SOCK_DGRAM)
server_ip = ("127.0.0.1",1030)
while 1 :
    a = input("我說:")
    utp_client.sendto(a.encode("utf-8"),server_ip)
    from_server,server_add = utp_client.recvfrom(1024)
    print("服務端說:",from_server.decode("utf-8"),"服務端的地址和埠",server_add)
    if a.upper() == "Q" and from_server.decode("utf-8").upper() == "Q" :
        break
utp_client.close()

  

 

 

這裡是兩個簡易描述socket各個引數和方法的圖,供大家參考:

socket型別:

 

socket各個方法的解釋:

 

本節主要內容:

1.osi七層模型

2.socket

一、osi七層模型

  網際網路的核心就是由一堆協議組成的,協議就是標準,標準就是大家都認可的,所有人都按照這個來,這樣大家都能夠互相瞭解,互相深入。比如全世界通用語言是英語。

 

五層通訊流程:

 

二、socket

  結合上面五層通訊流程來看,socket在哪一層呢??

  1.socket在內的五層通訊流程:

  Socket又稱為套接字,它是應用層與TCP/IP協議族通訊的中間軟體抽象層,它是一組介面。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。當我們使用不同的協議進行通訊時就得使用不同的介面,還得處理不同協議的各種細節,這就增加了開發的難度,軟體也不易於擴充套件(就像我們開發一套公司管理系統一樣,報賬、會議預定、請假等功能不需要單獨寫系統,而是一個系統上多個功能介面,不需要知道每個功能如何去實現的)。於是UNIX BSD就發明了socket這種東西,socket遮蔽了各個協議的通訊細節,使得程式設計師無需關注協議本身,直接使用socket提供的介面來進行互聯的不同主機間的程序的通訊。這就好比作業系統給我們提供了使用底層硬體功能的系統呼叫,通過系統呼叫我們可以方便的使用磁碟(檔案操作),使用記憶體,而無需自己去進行磁碟讀寫,記憶體管理。socket其實也是一樣的東西,就是提供了tcp/ip協議的抽象,對外提供了一套介面,同過這個介面就可以統一、方便的使用tcp/ip協議的功能了。

  其實站在你的角度上看,socket就是一個模組。我們通過呼叫模組中已經實現的方法建立兩個程序之間的連線和通訊。也有人將socket說成ip+port,因為ip是用來標識網際網路中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程式。 所以我們只要確立了ip和port就能找到一個應用程式,並且使用socket模組來與之通訊。

 

2.套接字socket的發展史及分類

  套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 因此,有時人們也把套接字稱為“伯克利套接字”或“BSD 套接字”。一開始,套接字被設計用在同 一臺主機上多個應用程式之間的通訊。這也被稱程序間通訊,或 IPC。套接字有兩種(或者稱為有兩個種族),分別是基於檔案型的和基於網路型的。

  基於檔案型別的套接字家族

  套接字家族的名字:AF_UNIX

  unix一切皆檔案,基於檔案的套接字呼叫的就是底層的檔案系統來取資料,兩個套接字程序執行在同一機器,可以通過訪問同一個檔案系統間接完成通訊。

  

  基於網路型別的套接字家族:

  套接字家族的名字:AF_INET

  (還有AF_INET6被用於ipv6,還有一些其他的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支援很多地址家族,但是由於我們只關心網路程式設計,所以大部分時候我們只使用AF_INET)

 

3.基於TCP和UDP兩個協議下socket的通訊流程

  TCP(Transmission Control Protocol)可靠的、面向連線的協議(eg:打電話)、傳輸效率低全雙工通訊(傳送快取&接收快取)、面向位元組流。使用TCP的應用:Web瀏覽器;檔案傳輸程式。

  UDP(User Datagram Protocol)不可靠的、無連線的服務,傳輸效率高(傳送前時延小),一對一、一對多、多對一、多對多、面向報文(資料包),盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統 (DNS);視訊流;IP語音(VoIP)。

看下圖的對比差異:

 

 接續往下看:

TCP和UDP下的socket差異對比圖:

  上面的圖只是讓大家感受一下TCP和UDP協議下,socket工作流程的不同,兩者之間的差異是TCP需要連線,UDP不需要。現在是不是有點迷糊!這裡面的bind、listen啥的都是什麼東西啊。下面我們就分開兩者。

 

三、TCP協議下的socket

  基於TCP的socket通訊流程圖片:

  

  雖然上圖將通訊流程中的大致描述了一下socket各個方法的作用,但是還是要總結一下通訊流程(下面一大段內容)

  先從伺服器端說起,伺服器端先初始化socket,然後與埠繫結(bind()),對介面進行監聽(listen()),呼叫accept阻塞,等待客戶端連線。在這時如果有一個客戶端初始化一個socket,然後連線伺服器(connect),如果連線成功,這時客戶端與伺服器的連線就建立了。客戶端傳送資料請求,伺服器端接收請求並處理請求,然後把迴應資料發給客戶端,客戶端讀取資料,最後關閉連線,一次互動結束。

  上程式碼感受下,需要建立兩個檔案,檔名我這裡為了方便看,我的兩個檔名稱為tcp_server.py(服務端)和tcp_client.py(客戶端),將下面的server端的程式碼拷貝到tcp_server.py檔案中,將下面client端的程式碼拷貝到tcp_client.py中,然後先執行服務端,再執行客戶端。

  server端程式碼例項(如果比喻成打電話)

 

  解決辦法:

  但是如果你加上了上面的程式碼之後還是出現這個問題:OSError: [WinError 10013] 以一種訪問許可權不允許的方式做了一個訪問套接字的嘗試。那麼只能換埠了,因為你的電腦不支援埠重用。

  記住一點,用socket進行通訊,必須是一收一發對應好。

  提一下:網路相關或者需要和電腦上其他程式通訊的程式才需要開一個埠。

 

  TCP協議下的socket實現多人通話, listen(3)  要排隊等待的有三個客戶端

  server端

import socket
server = socket.socket()
server_ip = ("127.0.0.1",1026)
server.bind(server_ip)
server.listen(3)
while 1 :
    conn, address = server.accept()
    while 1 :

        from_client = conn.recv(1024)
        print("高熠超說:",from_client.decode("utf-8"))
        a = input("我說:")
        if a.upper() == "Q":
            break
        else :
            conn.send(a.encode("utf-8"))

  

  client端

import socket
client = socket.socket()
client_ip = ("127.0.0.1",1026)
client.connect(client_ip)
while 1 :
    a = input("我說:")
    client.send(a.encode("utf-8"))
    from_server = client.recv(1024)
    print("甜甜說:",from_server.decode("utf-8"))
    if a.upper() == "Q" :
        break
client.close()

  

  你會發現,第一個連線的客戶端可以和服務端收發訊息,但是第二個連線的客戶端發訊息服務端是收不到的

   原因解釋:

    tcp屬於長連線,長連線就是一直佔用著這個連結,這個連線的埠被佔用了,第二個客戶端過來連線的時候,他是可以連線的,但是處於一個佔線的狀態,就只能等著去跟服務端建立連線,除非一個客戶端斷開了(優雅的斷開可以,如果是強制斷開就會報錯,因為服務端的程式還在第一個迴圈裡面),然後就可以進行和服務端的通訊了。什麼是優雅的斷開呢?看程式碼。  

強制斷開連線之後會報錯,報錯資訊:

 

四、UDP協議下的socket

  基於UDP的socket通訊流程:

 

  

  總結一下UDP下的socket通訊流程

  先從伺服器端說起。伺服器端先初始化Socket ( 建立物件 utp_server = socket.socket ( type = socket.SOCK_DGRAM ) ),然後與埠繫結(bind),recvform接收訊息,這個訊息有兩項,訊息內容和對方客戶端的地址,然後回覆訊息時也要帶著你收到的這個客戶端的地址,傳送回去,最後關閉連線

  

  server端程式碼的例項:(實現多人與服務端通話)

  server端

import socket
utp_server = socket.socket(type=socket.SOCK_DGRAM)
utp_server_ip = ("127.0.0.1",1030)
utp_server.bind(utp_server_ip)
while 1 :
    from_client,client_add = utp_server.recvfrom(1024)
    print("客戶端說:",from_client.decode("utf-8"),"客戶端的地址和埠:",client_add)
    a = input("我說:")
    utp_server.sendto(a.encode("utf-8"),client_add)

  

  client端

import socket
utp_client = socket.socket(type=socket.SOCK_DGRAM)
server_ip = ("127.0.0.1",1030)
while 1 :
    a = input("我說:")
    utp_client.sendto(a.encode("utf-8"),server_ip)
    from_server,server_add = utp_client.recvfrom(1024)
    print("服務端說:",from_server.decode("utf-8"),"服務端的地址和埠",server_add)
    if a.upper() == "Q" and from_server.decode("utf-8").upper() == "Q" :
        break
utp_client.close()

  

 

 

這裡是兩個簡易描述socket各個引數和方法的圖,供大家參考:

socket型別:

 

socket各個方法的解釋: