python的學習之路day7-socket網絡編程

python基礎部分學習完了,時間也已通過了兩個月左右,感受沒學到什麼,多是我學習以後忘記的太多了。python

因爲沒錢買書,要是去培訓就更沒錢了,因此在網上找了一本書,感受還不錯,講的比較好,比較詳細。linux

Python核心編程(第3版)PDF高清晰完整中文版 ,須要的小夥伴能夠去下載,固然若是你們不像我這麼窮逼,也能夠去網上購買。數據庫

 

入門半小時,精通一 杯子」 !!!編程

接下來繼續更新Python緩存

 

大綱

一、一些原理及解釋服務器

客戶端/服務器的架構原理網絡

客戶端/服務器的網絡編程數據結構

套接字:通訊端點架構

二、Python中的網絡編程併發

 socket()模塊函數

套接字對象(內置)方法

建立一個簡單的TCP服務器以及客戶端

建立一個簡單的UDP服務器以及客戶端

三、socket()模塊屬性

四、SocketServer模塊

SocketServer模塊類

SocketServerTCP服務器和客戶端(這個地方的代碼有問題,若是有大神路過,但願能幫我看看,謝謝了)

 

一些原理及解釋:

客戶端/服務器的架構原理

什麼是客戶端/服務器架構?對於不一樣的人來講,它意味着不一樣的東西,這取決於你問誰以及描述的是軟件仍是硬件系統。在這兩種狀況中的任何一種下,前提都很簡單:服務器就是一系列硬件或軟件,爲一個或多個客戶端(服務的用戶)提供所需的「服務」。它存在惟一目的就是等待客戶端的請求,並響應它們(提供服務),而後等待更多請求。另外一方面,客戶端因特定的請求而聯繫服務器,併發送必要的數據,而後等待服務器的迴應,最後完成請求或給出故障的緣由。服務器無限地運行下去,並不斷地處理請求;而客戶端會對服務進行一次性請求,而後接收該服務,最後結束它們之間的事務。客戶端在一段時間後可能會再次發出其餘請求,但這些都被看成不一樣的事務。

客戶端/服務器的網絡編程

在服務器響應客戶端請求以前,必須進行一些初步的設置流程來爲以後的工做作準備。首先會建立一個通訊端點,它可以使服務器監聽請求。能夠把服務器比做公司前臺,或者應答公司主線呼叫的總機接線員。一旦電話號碼和設備安裝成功且接線員到達時,服務就能夠開始了。

這個過程與網絡世界同樣,一旦一個通訊端點已經創建,監聽服務器就能夠進入無限循環中,等待客戶端的鏈接並響應它們的請求。固然,爲了使公司電話接待員一直處於忙碌狀態,咱們毫不能忘記將電話號碼放在公司信箋、廣告或一些新聞稿上;不然,將沒有人會打電話過來!
類似地,必須讓潛在的客戶知道存在這樣的服務器來處理他們的需求;不然,服務器將永遠不會獲得任何請求。想象着建立一個全新的網站,這多是最了不得的、勁爆的、使人驚異的、有用的而且最酷的網站,但若是該網站的 Web 地址或 URL 歷來沒有以任何方式廣播或進行廣告宣傳,那麼永遠也不會有人知道它,而且也將永遠不會看到任何訪問者。如今你已經很是瞭解了服務器是如何工做的,這就已經解決了較困難的部分。客戶端比服務器端更簡單,客戶端所須要作的只是建立它的單一通訊端點,而後創建一個到服務器的鏈接。而後,客戶端就能夠發出請求,該請求包括任何須要的數據交換。一旦請求被服務器處理,且客戶端收到結果或某種確認信息,這次通訊就會被終止。

套接字:通訊端點

套接字是計算機網絡數據結構,它體現了上節中所描述的「通訊端點」的概念。在任何類型的通訊開始以前,網絡應用程序必須建立套接字。能夠將它們比做電話插孔,沒有它將沒法進行通訊。
套接字的起源能夠追溯到 20 世紀 70 年代,它是加利福尼亞大學的伯克利版本 UNIX(稱爲 BSD UNIX)的一部分。所以,有時你可能會聽過將套接字稱爲伯克利套接字或 BSD 套接字。套接字最初是爲同一主機上的應用程序所建立,使得主機上運行的一個程序(又名一個進程)與另外一個運行的程序進行通訊。這就是所謂的進程間通訊(Inter Process Communication,IPC)。有兩種類型的套接字:基於文件的和麪向網絡的。
UNIX 套接字是咱們所講的套接字的第一個家族,而且擁有一個「家族名字」AF_UNIX(又名 AF_LOCAL,在 POSIX1.g 標準中指定),它表明地址家族(address family):UNIX。包括 Python 在內的大多數受歡迎的平臺都使用術語地址家族及其縮寫 AF;其餘比較舊的系統可能會將地址家族表示成域(domain)或協議家族(protocol family),並使用其縮寫 PF 而非 AF。相似地,AF_LOCAL(在 2000~2001 年標準化)將代替 AF_UNIX。然而,考慮到後向兼容性,不少系統都同時使用兩者,只是對同一個常數使用不一樣的別名。Python 自己仍然在使用 AF_UNIX。

由於兩個進程運行在同一臺計算機上,因此這些套接字都是基於文件的,這意味着文件系統支持它們的底層基礎結構。這是可以說得通的,由於文件系統是一個運行在同一主機上的多個進程之間的共享常量。第二種類型的套接字是基於網絡的,它也有本身的家族名字 AF_INET,或者地址家族:因特網。另外一個地址家族 AF_INET6 用於第 6 版因特網協議(IPv6)尋址。此外,還有其餘的地址家族,這些要麼是專業的、過期的、不多使用的,要麼是仍未實現的。在全部的地址家族之中,目前 AF_INET 是使用得最普遍的。
Python 2.5 中引入了對特殊類型的 Linux 套接字的支持。套接字的 AF_NETLINK 家族(無鏈接[見2.3.3節])容許使用標準的BSD套接字接口進行用戶級別和內核級別代碼之間的IPC。
以前那種解決方案比較麻煩,而這個解決方案能夠看做一種比前一種更加優雅且風險更低的解決方案,例如,添加新系統調用、/proc 支持,或者對一個操做系統的「IOCTL」。針對 Linux 的另外一種特性(Python 2.6 中新增)就是支持透明的進程間通訊(TIPC)協議。TIPC 容許計算機集羣之中的機器相互通訊,而無須使用基於 IP 的尋址方式。Python 對TIPC 的支持以 AF_TIPC 家族的方式呈現。
總的來講,Python 只支持 AF_UNIX、AF_NETLINK、AF_TIPC 和 AF_INET 家族。

 

Python中的網絡編程

socket()模塊函數

要建立套接字,必須使用socket.socket()函數

如何建立socket()函數:
socket(socket_family, socket_type, protocol=0)
socket_family是AF_UNIX或AF_INET(AF_INET6)(ipv4和ipv6)
socket_type是SOCK_STREAM或SOCK_DGRAM(前者表示TCP,後者表示UDP)
protocol一般省略,默認爲0

import socket

# 建立TCP/IP套接字:
tcpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立UDP/IP套接字:
udpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

有不少socket模塊屬性,因此可使用「from socket import *」:

from socket import * tcpsocket = socket(AF_INET, SOCK_STREAM)

 

一旦有了一個套接字對象,那麼使用套接字對象的方法將能夠進行進一步的交互

 

套接字對象(內置)方法:

名稱

描述

服務器套接字方法

s.bind()

將地址(主機名、端口號對)綁定到套接字上

s.listen()

設置並啓動TCP監聽器

s.accept()

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

客戶端套接字方法

s.connect()

主動發起TCP服務器鏈接

s.connect_ex()

Connect()的擴展版本,此時會以錯誤碼的形式返回問題,而不是拋出一個異常

普通的套接字方法

s.recv()

接收TCP消息

s.recv_into()

接收TCP消息到指定的緩衝區

s.send()

發送TCP消息

s.sendall()

完整地發送TCP消息

s.recvfrom()

接收UDP消息

s.recvfrom_into()

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

s.sendto()

發送UDP消息

s.getpeername()

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

s.getsockname()

當前套接字的地址

s.getsockopt()

返回給定套接字選項的值

s.setsockopt()

設置給定套接字選項的值

s.shutdown()

關閉鏈接

s.close()

關閉套接字

s.detach()

在未關閉文件描述符的狀況下關閉套接字,返回文件描述符

s.ioctl()

控制套接字的模式(僅支持Windows)

面向阻塞的套接字方法

s.setblocking()

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

s.settimeout()

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

s.gettimeout()

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

面向文件的套接字方法

s.fileno()

套接字的文件描述符

s.makefile()

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

數據屬性

s.family

套接字家族

s.type

套接字類型

s.proto

套接字協議

 

建立一個簡單的TCP服務器以及客戶端

服務器端:

# TCP時間戳服務器的僞代碼
# import socket
# ss = socket.socket()          #建立服務器套接字
# ss.bind()                     #套接字與地址綁定
# ss.listen()                   #監聽鏈接
# inf_loop:                     #服務器無限循環
#     cs = ss.accept()          #接收客戶端鏈接
#     comm_loop:                #通訊循環
#         cs.recv()/cs.send()   #對話(接收/發送)
#     cs.close()                #關閉客戶端套接字
# ss.close()                    #關閉服務器套接字(可選)

import socket
import time  # 分別導入兩個模塊

HOST = ''  # 這是對bind()方法的標識,表示可使用任何可用的地址
PORT = 1504  # 端口
BUFSIZ = 1024  # 設置緩存區大小
ADDR = (HOST, PORT)
tcp_ser_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_ser_sock.bind(ADDR)  # 將(地址、端口號)綁定到套接字在上
tcp_ser_sock.listen(5)
while True:  # 進入一個無限的循環
    print("waiting for connection...")  # 提示信息
    tcp_cli_sock, addr = tcp_ser_sock.accept()  # 被動的等待客戶端鏈接
    print("...connected from: ", addr)  # 某個客戶端已鏈接
    while True:  # 進入一個對話循環
        data = tcp_cli_sock.recv(BUFSIZ).decode()  # 接收TCP消息
        if not data:
            break  # 若是接收的消息爲空,跳出當前循環,繼續等待
        tcp_cli_sock.send(('[%s] %s' % (time.ctime(), data)).encode())  # 發送接收的消息,而且在前面加上當前時間
    tcp_cli_sock.close()  # 關閉客戶端

客戶端:

# 僞代碼
# import socket
# cs = socket.socket()    #建立客戶端套接字
# cs.connect()            #嘗試鏈接服務器
# comm_loop:              #通訊循環
#     cs.send()/cs.recv() #對話(發送/接收)
# cs.close()              #關閉客戶端套接字

import socket

HOST = 'localhost'
PORT = 1504
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcp_cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_cli_sock.connect(ADDR)  # 主動發起TCP服務器鏈接

while True:
    data = input(">>>")
    if not data:  # 判斷輸入是否爲空
        break
    tcp_cli_sock.send(data.encode())  # 發送輸入的內容
    data = tcp_cli_sock.recv(BUFSIZ).decode()  # 接收返回的內容
    if not data:  # 判斷返回的內容是否爲空
        break
    print(data)  # 將返回的內容打印出來
tcp_cli_sock.close()  # 而後關閉鏈接

 輸出:

服務器:
waiting for connection...
...connected from:  ('127.0.0.1', 52064)
waiting for connection...

客戶端:
>>>qwe
[Thu Jan 18 20:09:59 2018] qwe
>>>456
[Thu Jan 18 20:10:04 2018] 456
>>>

 

當咱們從服務器退出時,必須跳出它,這就會致使一個異常。爲了不這種錯誤,最好的方式就是建立一種更優雅的退出方式

核心提示:優雅地退出和調用服務器 close()方法
在開發中,建立這種「友好的」退出方式的一種方法就是,將服務器的 while 循環放在一個 try-except 語句中的 except 子句中,並監控 EOFError 或 KeyboardInterrupt 異常,這樣你就能夠在 except 或 finally 字句中關閉服務器的套接字。在生產環境中,你將想要可以以一種更加自動化的方式啓動和關閉服務器。在這些狀況下,須要經過使用一個線程或建立一個特殊文件或數據庫條目來設置一個標記以關閉服務。

 

建立一個簡單的UDP服務器以及客戶端

服務器端:

# 僞代碼:
# ss = socket.socket()                  #建立服務器套接字
# ss.bind()                             #綁定服務器套接字
# inf_loop:                             #服務器無限循環
#     cs = cs.recvfrom()/ss.sendto()    #對話(接收/發送)
# ss.close()                            #關閉服務器套接字

import socket
import time  # 分別導入兩個模塊

HOST = ''  # 主機地址
PORT = 1504  # 端口號
BUFSIZ = 1024  # 緩存區大小
ADDR = (HOST, PORT)
udp_ser_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 使用udp協議
udp_ser_sock.bind(ADDR)  # 將地址和端口綁定到套接字上
# 能夠看到沒有listen,由於udp是無鏈接的,因此不用調用「監聽傳入鏈接」
while True:  # 進入一個無限循環
    print("waiting for massage...")  # 提示輸出
    data, addr = udp_ser_sock.recvfrom(BUFSIZ)  # 接收UDP消息
    udp_ser_sock.sendto(("[%s] %s" % (time.ctime(), data.decode("utf-8"))).encode(), addr)  # 將接收到的消息加上時間發送給客戶端
    print("...received from and returned to:", addr)  # 打印已發送給誰
udp_ser_sock.close()

 客戶端:

# 僞代碼
# cs = socket()                     #建立客戶端套接字
# comm_loop:                        #通訊循環
#     cs.sendto()/cs.recvfrom()     #對話(發送/接收)
# cs.close()                        #關閉客戶端套接字

import socket

HOST = 'localhost'  # 地址
PORT = 1504  # 端口
BUFSIZ = 1024  # 緩存
ADDR = (HOST, PORT)
udp_cli_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 使用udp協議
while True:  # 進入一個無限循環
    data = input(">>>")  # 輸入
    if not data:  # 判斷輸入是否爲空
        break
    udp_cli_sock.sendto(data.encode(), ADDR)  # 將輸入的內容和本地的地址和端口發送到服務器
    data, ADDR = udp_cli_sock.recvfrom(BUFSIZ)  # 接收服務器傳回來的內容
    if not data:
        break
    print(data.decode("utf-8"))  # 打印
udp_cli_sock.close()

 輸出:

服務器:
waiting for massage...
...received from and returned to: ('127.0.0.1', 64721)
waiting for massage...
...received from and returned to: ('127.0.0.1', 64722)
waiting for massage...


客戶端:
>>>asd
[Fri Jan 19 14:21:47 2018] asd
>>>asd
[Fri Jan 19 14:21:51 2018] qwe

 

socket模塊屬性

 

SocketServer模塊

SocketServer 是標準庫中的一個高級模塊(Python 3.x 中重命名爲 socketserver),它的目標是簡化不少樣板代碼,它們是建立網絡客戶端和服務器所必需的代碼。這個模塊中有爲你建立的各類各樣的類;

SocketServer模塊類

 

描述

BaseServer

包含核心服務器功能和 mix-in 類的鉤子;僅用於推導,這樣不會建立這個類的實例;能夠用 TCPServer 或 UDPServer 建立類的實例

TCPServer/UDPServer

基礎的網絡同步 TCP/UDP 服務器

UnixStreamServer/UnixDatagramServer

基於文件的基礎同步 TCP/UDP 服務器

ForkingMixIn/ThreadingMixIn

核心派出或線程功能;只用做 mix-in 類與一個服務器類配合實現一些異步性;不能直接實例化這個類

ForkingTCPServer/ForkingUDPServer

ForkingMixIn 和 TCPServer/UDPServer 的組合

ThreadingTCPServer/ThreadingUDPServer

ThreadingMixIn 和 TCPServer/UDPServer 的組合

BaseRequestHandler

包含處理服務請求的核心功能;僅僅用於推導,這樣沒法建立這個類的實例;可使用 StreamRequestHandler 或 DatagramRequestHandler 建立類的實例

StreamRequestHandler/DatagramRequestHandler

實現 TCP/UDP 服務器的服務處理器

 

SocketServerTCP服務器和客戶端(表示沒有成功,書上寫的python2,我用的是python3,不知道如何改代碼,若是有哪位大神路過看到可否救救我,謝謝了)

服務器端:

import socketserver
import time

HOST = ""
PORT = 1504
ADDR = (HOST, PORT)


class my_request_handler(socketserver.StreamRequestHandler):
    def handle(self):
        print("...connected from:", self.client_address)
        self.wfile.write('[%s] %s' % (time.ctime(), self.rfile.readline()))


tcp_serv = socketserver.TCPServer(ADDR, my_request_handler)
print("waiting for connection...")
tcp_serv.serve_forever()

 客戶端:

import socket

HOST = "localhost"
PORT = 1504
BUFSIZ = 1024
ADDR = (HOST, PORT)
while True:
    tcp_cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_cli_sock.connect(ADDR)
    data = input(">>>")
    if not data:
        break
    tcp_cli_sock.send(data.encode())
    data = tcp_cli_sock.recv(BUFSIZ).decode()
    if not data:
        break
    print(data.strip())
    tcp_cli_sock.close()

 輸出:

服務器:
waiting for connection...
...connected from: ('127.0.0.1', 59956)

客戶端:
>>>sdas
就這樣一直卡着若是像書上同樣:TypeError: a bytes-like object is required, not 'str'
相關文章
相關標籤/搜索