python週報第九周

0.本週知識點預覽

  •  網絡編程基礎  
    • socket
    • socketserver

 

1.網絡編程基礎

1.socket

 利用socket,咱們就能夠寫網絡編程的服務端和客戶端實現通訊了,附上兩端的基本實現方法圖:shell

 

從圖上能夠看到,網絡編程須要服務端和客戶端,應該先起服務端,再起客戶端。編程

服務端的大體步驟爲:服務器

  1. 服務端實例化一個socket對象
  2. 服務端綁定特定的IP和端口
  3. 服務端監聽所綁定的地址和端口
  4. 服務端等待鏈接
  5. 當客戶端鏈接時,收到客戶端的數據,通過處理後發送回客戶端
  6. 循環步驟5,一直到結束鏈接
  7. 服務端關閉鏈接

客戶端的大體步驟爲:網絡

  1. 客戶端實例化一個socket對象
  2. 客戶端鏈接服務端所監聽的地址和端口
  3. 客戶端創建鏈接
  4. 客戶端發送數據,等待服務端處理後,接收數據
  5. 循環步驟4,一直到結束鏈接
  6. 客戶端關閉鏈接

這裏附上關於socket最簡單的代碼:多線程

服務端:併發

import socket                   ###導入socket模塊

s = socket.socket()                 ###實例化一個socket對象
server_addr = ("127.0.0.1",12345)
s.bind(server_addr)             ###綁定IP和端口
s.listen(1)             ###在拒絕鏈接前,最大接收掛起的客戶端爲1個
conn,addr = s.accept()              ###等待接收數據
while True:
    try:
        recv_data = conn.recv(1024)             ###接收數據,最多收1024個字節,接收的類型必須是bytes類型
        send_data = str(recv_data,encoding="utf-8").upper()             ###把接收的數據轉變成大寫字母
        conn.send(bytes(send_data,encoding="utf-8"))                ###發送數據,類型必須是bytes類型
    except Exception:
        continue
conn.close()                    ###關閉鏈接

客戶端:ssh

import socket               ###導入socket模塊

s = socket.socket()             ###實例化socket對象
server_info = ("127.0.0.1",12345)          
s.connect(server_info)              ###鏈接服務端的地址和端口
while True:
    send_data = input(">> ").strip()            ###客戶端輸入數據
    if send_data == "exit":break
    if len(send_data) == 0:continue
    s.send(bytes(send_data,encoding="utf8"))            ###客戶端發送數據,類型必須是bytes類型
    recv_data = s.recv(1024)            ###客戶端接收數據
    print(str(recv_data,encoding="utf8"))
s.close()               ###關閉鏈接

先執行服務端代碼,後執行客戶端代碼,在客戶端執行的結果以下:socket

>> ls
LS
>> fuck
FUCK
>> haha
HAHA
>> exit

Process finished with exit code 0

代碼解析:以上代碼實現了最簡單的socket通訊,這裏須要注意的是,accept()、recv()、send()方法都是阻塞類型,參數不能爲空,假如參數爲空,程序就會阻塞住,不能執行下面的代碼。函數

 

利用socket,咱們能夠實如今客戶端相似ssh鏈接服務端同樣,執行系統命令,下面是具體代碼及講解。編碼

服務端:

import socket               ###導入socket模塊
import subprocess               ###導入系統命令模塊

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

server_addr = ("127.0.0.1",12345)

s.bind(server_addr)             ###綁定地址和端口

s.listen(1)             ###在拒絕鏈接前,最大接收掛起的客戶端爲1個

while True:
    conn,addr = s.accept()          ###等待接收數據
    while True:
        try:
            recv_data = conn.recv(1024)             ###接收數據
            print(recv_data)
            result = subprocess.Popen(str(recv_data,encoding="utf8"),shell=True,stdout=subprocess.PIPE)     ###執行系統命令
            result_base = result
            res = result_base.stdout.read()         ###獲取命令結果
            result_base.communicate()               ###獲得命令返回值
            if result_base.returncode:              ###判斷命令返回值
                send_data = "cmd error"
            elif len(res) == 0:
                send_data="None"
            else:
                send_data=str(res,encoding="utf-8")
            send_data = bytes(send_data,encoding="utf8")
            len_send_data = bytes(len(send_data))          ###發送字節類型數據
            conn.send(send_data)                 ###發送字節類型數據
        except Exception:
            break
    conn.close()

 客戶端和上個例子同樣代碼便可:

import socket               ###導入socket模塊

s = socket.socket()             ###實例化socket對象
server_info = ("127.0.0.1",12345)
s.connect(server_info)              ###鏈接服務端的地址和端口
while True:
    send_data = input(">> ").strip()            ###客戶端輸入數據
    if send_data == "exit":break
    if len(send_data) == 0:continue
    s.send(bytes(send_data,encoding="utf8"))            ###客戶端發送數據,類型必須是bytes類型
    recv_data = s.recv(1024)            ###客戶端接收數據
    print(str(recv_data,encoding="utf8"))
s.close()               ###關閉鏈接

先執行服務端,再執行客戶端,客戶端執行結果以下:

>> ls
socket_client.py
socket_client1.py
socket_client2.py
socket_server.py
socketclient_test.py
socketserver_test.py
test.py

>> df -h
Filesystem      Size   Used  Avail Capacity  iused    ifree %iused  Mounted on
/dev/disk1     233Gi  173Gi   59Gi    75% 45438905 15542341   75%   /
devfs          180Ki  180Ki    0Bi   100%      622        0  100%   /dev
map -hosts       0Bi    0Bi    0Bi   100%        0        0  100%   /net
map auto_home    0Bi    0Bi    0Bi   100%        0        0  100%   /home

>> abcd
cmd error

附上socket模塊的其餘方法:

socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

參數一:地址簇

  socket.AF_INET IPv4(默認)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只可以用於單一的Unix系統進程間通訊

參數二:類型

  socket.SOCK_STREAM  流式socket , for TCP (默認)
  socket.SOCK_DGRAM   數據報式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字沒法處理ICMP、IGMP等網絡報文,而SOCK_RAW能夠;其次,SOCK_RAW也能夠處理特殊的IPv4報文;此外,利用原始套接字,能夠經過IP_HDRINCL套接字選項由用戶構造IP頭。
  socket.SOCK_RDM 是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在須要執行某些特殊操做時使用,如發送ICMP報文。SOCK_RAM一般僅限於高級用戶或管理員運行的程序使用。
  socket.SOCK_SEQPACKET 可靠的連續數據包服務

參數三:協議

  0  (默認)與特定的地址家族相關的協議,若是是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議

sk.bind(address)

  s.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。

sk.listen(backlog)

  開始監聽傳入鏈接。backlog指定在拒絕鏈接以前,能夠掛起的最大鏈接數量。

      backlog等於5,表示內核已經接到了鏈接請求,但服務器尚未調用accept進行處理的鏈接個數最大爲5
      這個值不能無限大,由於要在內核中維護鏈接隊列

sk.setblocking(bool)

  是否阻塞(默認True),若是設置False,那麼accept和recv時一旦無數據,則報錯。

sk.accept()

  接受鏈接並返回(conn,address),其中conn是新的套接字對象,能夠用來接收和發送數據。address是鏈接客戶端的地址。

  接收TCP 客戶的鏈接(阻塞式)等待鏈接的到來

sk.connect(address)

  鏈接到address處的套接字。通常,address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。

sk.connect_ex(address)

  同上,只不過會有返回值,鏈接成功時返回 0 ,鏈接失敗時候返回編碼,例如:10061

sk.close()

  關閉套接字

sk.recv(bufsize[,flag])

  接受套接字的數據。數據以字符串形式返回,bufsize指定最多能夠接收的數量。flag提供有關消息的其餘信息,一般能夠忽略。

sk.recvfrom(bufsize[.flag])

  與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。

sk.send(string[,flag])

  將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容所有發送。

sk.sendall(string[,flag])

  將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。

      內部經過遞歸調用send,將全部內容發送出去。

sk.sendto(string[,flag],address)

  將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議。

sk.settimeout(timeout)

  設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如 client 鏈接最多等待5s )

sk.getpeername()

  返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port)。

sk.getsockname()

  返回套接字本身的地址。一般是一個元組(ipaddr,port)

sk.fileno()

2.socketserver 

SocketServer內部使用 IO多路複用 以及 「多線程」 和 「多進程」 ,從而實現併發處理多個客戶端請求的Socket服務端。即:每一個客戶端請求鏈接到服務器時,Socket服務端都會在服務器是建立一個「線程」或者「進程」 專門負責處理當前客戶端的全部請求。

附上服務端代碼:

import socketserver         ###導入socketserver模塊
import subprocess           ###導入系統命令模塊

class MySocketServer(socketserver.BaseRequestHandler):      ###建立一個類繼承socketserver.BaseRequestHandler
    def handle(self):                                       ###建立handle方法,必須叫handle,由於其父類有handle方法
        self.request.sendall(bytes("歡迎致電10086...",encoding="utf8"))     ###向全部客戶端發送一條語句
        while True:
            data = self.request.recv(1024)                  ###接收數據
            str_data = data.decode()                        ###轉碼爲字符串
            ret = subprocess.Popen(str_data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)       ###執行系統命令
            result = ret.stdout.read()  
            if not result:
                result = ret.stderr.read()
            if len(result) == 0:
                result = bytes("執行結果爲空",encoding="utf-8")
            if len(str_data) == 0:
                break
            print("[%s] says: [%s]" % (self.client_address,str_data))       ###打印客戶端地址以及數據
            self.request.sendall(result)                ###發送數據


if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(("127.0.0.1",8007),MySocketServer)     ###實例化socketserver.ThreadingTCPServer的對象,參數一是地址和端口,參數二是建立的類
    server.serve_forever()              ###執行對象的serve_forever方法,做用是循環監聽

客戶端:

import socket

ip_port = ('127.0.0.1',8007)

s = socket.socket()
s.connect(ip_port)
welcome_msg = s.recv(1024)

print("from server: ",welcome_msg.decode())

while True:
    send_data = input(">> ").strip()
    if len(send_data) == 0:continue
    s.send(bytes(send_data,encoding='utf8'))

    recv_data = s.recv(1024)
    print(str(recv_data,encoding="utf8"))

s.close()

客戶端代碼依舊是socket模塊中的例子,先執行服務端代碼,當兩次及以上執行客戶端代碼時,發現輸入的命令都能獲得返回值,實現了併發的效果。

相關文章
相關標籤/搜索