Python網絡編程(1)-socket

   我會在近期儘快更新好以前寫的博客,會添加新的知識點和注意問題,排版和內容都會較以前有很大的改觀,感謝你們一直的支持!

一、 客戶端/服務器架構

  客戶端/服務器架構也稱主從式架構,簡稱C/S架構,它是一種網絡結構,把客戶端(Client)(一般是一個採用圖形界面的程序)與服務器(server)區分開來,在C/S架構中,服務器是一系列的硬件或軟件,客戶端是提交服務請求的用戶,客戶端提供用戶請求接口,服務端響應請求進行對應的處理,並返回給客戶端。客戶端/服務器架構既能夠應用於計算機硬件,也能夠應用於軟件。linux

1.一、 硬件客戶端/服務器架構

  典型的硬件客戶端/服務器架構就是打印機,在企業中,員工經過局域網將我的電腦鏈接到打印機上,做爲客戶端向打印機發送打印請求,打印機做爲服務端完成響應處理相應的請求。程序員

1.二、 軟件客戶端/服務器架構

  軟件服務器也是運行在硬件之上的,典型的軟件服務器是Web服務器。在一臺或多臺電腦上搭建Web服務器,以提供用戶訪問所需的Web頁面和應用程序,Web服務器一旦啓動,都將可能永遠運行,除非受到一些外力驅使纔會中止,如人爲關閉,服務器硬件故障等。它的工做就是接收客戶端的請求,並響應請求給客戶端返回相應的Web頁面,而後等待下一個客戶端的請求。編程

二、 套接字

  套接字是網絡編程中的一個基本組件,若是想要服務器可以響應客戶端發來的請求,首先要創建一個通訊端點,使服務器可以監聽服務,當通訊端點創建後,就會進入無限循環的等待請求狀態,當接收到客戶端的請求,就會響應該請求。緩存

  套接字就是兩個程序之間的信息通道,能夠理解爲上面提到的「通訊端點」的概念。在通訊開始以前,網絡應用程序必須建立套接字。套接字是網絡通訊過程當中端點的抽象表示,包含進行網絡通訊必須的五種信息:鏈接使用的協議,本地主機的IP地址,本地進程的協議端口號,遠程主機的IP地址,遠程進程的協議端口號。服務器

  套接字起源於 20 世紀 70 年代,它是加利福尼亞大學伯克利分校版本的 Unix的一部分,即人們所說的 BSD Unix。 所以,套接字也被人們稱爲「伯克利套接字」或「BSD 套接字」。套接字最初被設計用於同一臺主機上多個應用程序之間的通信,這也就是所謂的進程間通信(IPC)。網絡

  TCP用主機的IP地址加上主機的端口號做爲TCP鏈接的端點,這種端點就叫作套接字(socket)或插口。套接字用(IP地址:端口號)表示。架構

  套接字有兩種類型,分別是基於文件的和基於網絡的。socket

  基於文件的套接字家族名字叫作「AF_UNIX」,表明地址家族(address family):UNIX。在Unix和linux操做系統中,熟爲人知的一句話就是:一切皆文件,一個或多個進程運行在同一臺機器上,因此套接字是基於文件的,它就能夠經過訪問底層的基礎結構來實現進程之間的通訊tcp

  基於網絡的套接字家族名字叫作「AF_INET」,表明地址家族(address family):INET(因特網)。它使用IPv4進行通訊,由於IPv4使用32位地址,相比於IPv6的128位來講,計算更快,更適合於局域網的通訊。目前它也是使用最普遍的。函數

  在本文中,重點講網絡編程,因此在後面的涉及最多的仍是AF_INET。

2.一、 流式套接字(SOCK_STREAM)

  不論使用哪一種地址家族,都只有兩種套接字的鏈接方式,一種是面向鏈接的,一種是無鏈接的。

  面向鏈接的套接字鏈接方式,意味着在進程通訊以前必須先創建好一個鏈接,這種套接字就稱爲流式套接字。

  流式套接字用於提供面向鏈接、可靠的數據傳輸服務。該服務將保證數據可以實現無差錯、無重複發送,並按順序接收。流式套接字之因此可以實現可靠的數據服務,緣由在於其使用了傳輸控制協議,即TCP(The Transmission Control Protocol)協議。在Python中,建立TCP套接字,就必須聲明SOCK_STREAM做爲套接字類型。

2.二、 數據報套接字(SOCK_DGRAM)

  數據報套接字提供了一種無鏈接的服務。這也意味着,使用這種鏈接方式不須要在進程通訊前創建鏈接。在數據的傳輸過程當中,SOCK_DGRAM並不能保證數據傳輸的可靠性,數據有可能在傳輸過程當中丟失或出現數據重複,且沒法保證順序地接收到數據。數據報套接字使用UDP(User Datagram Protocol)協議進行數據的傳輸。因爲數據報套接字不能保證數據傳輸的可靠性,對於有可能出現的數據丟失狀況,須要在程序中作相應的處理。

  雖然存在數據丟失、重複、數據無序接受等不少缺點,但它也有優點所在,在流式套接字中,由於是面向鏈接並提供了可靠的數據傳輸服務,這對於虛擬電路鏈接的維護須要很大的開銷,但數據報套接字就不須要這些額外的開銷,因此維護、資源佔用成本更低。

三、 網絡編程

  Python是一個很強大的網絡編程工具,Python內有不少針對網絡協議的庫,這些庫對網絡協議的各個層次進行抽象封裝,這對於程序員來講就意味着:沒必要關心網絡協議的原理,只須要經過對程序的邏輯處理,就能夠實現網絡數據的傳輸。

3.一、 建立套接字

  在Python中,建立套接字須要使用socket模塊,經過socket()函數建立套接字對象。

1 class socket(_socket.socket):
2         -- skip --
3         def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
4             -- skip --

  從socket函數的的構造方法中能夠看出,能夠指定地址家族和套接字的鏈接方式,proto默認是0,一般都省略。即建立套接字對象的時候:

  import socket

  #建立TCP/IP套接字,地址家族AF_INET

  tcp_socket = socket.socket(socket.AF_INET,socket.SOCKET_STREAM)

  #建立UDP/IP套接字,地址家族AF_INET

  udp_socket = socket.socket(socket.AF_INET,socket.SOCKET_DGRAM)

3.二、 套接字的內置方法

  常見的套接字內置函數

方法

功能

st.recv()

接受TCP的消息

st.recv_into()

接受TCP的消息到指定的緩存區

st.send()

 

發送TCP的消息(當待發送的消息量大於緩存區剩餘內存時,數據會丟失)

st.sendall()

 

完整的發送TCP消息(當待發送的消息量大於緩存區剩餘內存時,數據不會丟失,循環調用send 直到發完爲止)

st.recvfrom()

接收UDP的消息

st.recvfrom_into()

接收UDP的消息到指定的緩存區

st.sendto()

發送UDP的消息

st.getpeername()

鏈接到套接字的遠程地址(TCP)

st.getsockname()

獲取當前套接字的地址

st.getsockopt()

獲取指定套接字的參數

st.setsockopt()

設置指定套接字的參數

st.close()

關閉套接字

st.shutdown()

關閉鏈接

  服務端套接字方法

方法

功能

st.bind()

將IP地址+端口號綁定到套接字上

st.listen()

開啓TCP監聽功能

st.accept()

被動的接受TCP客戶端的鏈接,(阻塞式)一直等待鏈接直到鏈接到達

  客戶端套接字方法

方法

功能

st.connect()

主動發起TCP服務器鏈接

st.connect_ex()

connect()的擴展版本,以錯誤代碼的形式返回問題,而不是拋出異常

      面向阻塞的套接字方法

方法

功能

st.setblocking()

設置套接字爲阻塞模式或非阻塞模式

st.settimeout()

設置阻塞套接字的操做超時時間

st.gettimoout()

獲取阻塞套接字的操做超時時間

    面向文件的套接字方法

方法

功能

st.fileno()

套接字的文件描述符

st.makefile()

建立與套接字相關聯的文件對象

    數據屬性

屬性

功能

st.family

套接字家族

st.type

套接字類型

st.proto

套接字協議

3.三、 Tcp服務器和客戶端的通訊

  上面提到過,套接字對象都是經過socket.socket()函數來建立的,下面模擬一個TCP服務器和客戶端,來實現進程間的通訊。

  Tcp服務端:

 1 import socket
 2 tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  //建立服務器套接字
 3 tcp_server.bind(("127.0.0.1",8000))                           //將套接字與地址綁定
 4 tcp_server.listen(5)                                          //創建監聽鏈接
 5 print("The server has started")
 6 while True:
 7     conn,addr= tcp_server.accept()                           //接受客戶端的鏈接
 8     while True:
 9         try:
10             data = conn.recv(1024)                          //會話的接收(或發送)
11             print("msg is",data.decode("utf-8"))           //要將收到的會話數據進行解碼
12             conn.send(data.title())                      //會話的發送(或接受)
13         except Exception:
14             break
15     conn.close()                                         //關閉鏈接
16 tcp_server.close()                                      //關閉服務器套接字

   在Tcp服務端,先建立服務器套接字並指定類型爲流式套接字(SOCK_STREAM)。由於服務器須要佔用一個端口並等待客戶端的請求,因此它們必須綁定到一個本地地址。Tcp是一種面向鏈接的通訊方式,因此必須創建監聽鏈接,listen(5)的意義是容許傳入鏈接的最大數爲5個。當調用accept()函數後,服務端就會進入一個等待狀態,默認狀況下,accept()處於阻塞狀態,也就意味着,執行到此處,程序會暫停,直到有新的鏈接到達,纔會進行下一步的收發操做。

  Tcp客戶端:

 1 import socket
 2 tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  //建立客戶端套接字
 3 tcp_client.connect(("127.0.0.1",8000))                        //鏈接服務器
 4 while True:
 5     msg = input("Please input your message:").strip()
 6     if not msg:continue
 7     tcp_client.send(msg.encode("utf-8"))                      //會話接收(或發送)
 8     data = tcp_client.recv(1024)
 9     print("reply is",data.decode("utf-8"))
10 tcp_client.close()                                            //關閉客戶端套接字

  建立客戶端比服務端要簡單不少,客戶端一旦擁有了套接字,就能夠利用套接字的connect()方法直接建立一個服務器的鏈接,創建好鏈接,就能夠參與到服務端的會話中,當客戶端的需求所有完成,就會關閉套接字,終止這次鏈接。

3.四、 Udp服務端和客戶端的通訊

  Udp服務器不須要Tcp服務器那麼多的配置,由於它不是面向鏈接的,除了等待傳入的鏈接,它基本不須要其餘的操做。

  Udp服務端:

 1 import socket
 2 ip_port = ("127.0.0.1",8000)
 3 udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) //建立服務端套接字
 4 udp_server.bind(ip_port)                                    //綁定本地地址
 5 print("the server has started")
 6 while True:
 7     data,addr = udp_server.recvfrom(1024)                 //關閉接收(或發送)
 8     print(data)
 9     udp_server.sendto(data.title(),addr)                //關閉發送(或接受)
10     從上面代碼中能夠看出,除了建立套接字並綁定本地地址後,基本

  沒有其它的操做,它是無鏈接的,這也就意味着,它無需爲了成功通訊而使一個客戶端鏈接到一個「特定」的套接字進行轉換操做,服務器端僅僅是接收數據並進行回覆。

       Udp客戶端:

1 import socket
2 ip_port = ("127.0.0.1",8000)
3 udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)  //建立服務端套接字
4 while True:
5     msg = input(">>>").strip()
6     udp_client.sendto(msg.encode("utf-8"),ip_port)           //發送
7     data,addr = udp_client.recvfrom(1024)                   //接收
8     print(data)
9 udp_client.close()                                        //關閉套接字

    Udp客戶端,一旦建立了套接字,就能夠進行會話循環中,當會話結束,關閉套接字。

    在使用Udp進行通訊的時候,服務端能夠同時接收多個客戶端的會話請求並返回請求結果。

相關文章
相關標籤/搜索