網絡編程筆記

網絡編程

網路編程基礎知識

  1. C/S和B/S架構python

    C : client S : server 客戶端,服務端shell

    B : browser S : server 瀏覽器,服務端編程

    B/S架構本質也是C/S瀏覽器

  2. 網絡的七層協議:(應表會傳網數物)服務器

    • 物理層:電信號(0和1)網絡

    • 數據鏈路層:把物理層的電信號分組,每一組叫一個數據報數據幀,每一幀有:報頭head和數據data兩部分。多線程

      頭固定18字節:6:發送者地址/6:接收者地址/6:數據類型架構

    • 網絡層:ssh

      • ip:ipv4:32位2進製表示:點分十進制表示異步

      • 子網掩碼:經過子網掩碼和ip判斷兩個ip是否處於同一個網段,經過ip地址和子網掩碼作按位與運算

        ip地址: 172.16.10.1: 10101100.00010000.00001010.000000001
        子網掩碼:255.255.255.0: 11111111.11111111.11111111.000000000
        按位與運算:172.16.10.0 10101100.00010000.00001010.000000000

      • ip和mac有轉換關係:主要是由於ARP協議mac地址學習

    • 傳輸層:

      • tcp協議:

        • 三次握手、四次揮手
        • dos和ddos攻擊:拒接服務攻擊,分佈式的拒接服務攻擊
        • 端口號:0—65535,0—1023爲系統佔用端口
      • udp協議:

        直接發送,不須要響應,因此數據不可靠。(看視頻花屏)

      • 端口:

        • 經過ip + 子網掩碼肯定一臺設備
        • 經過ip + 子網掩碼 + 端口號肯定一個軟件
    • 會話層:使應用創建和維持會話,並能使會話得到同步。

    • 表示層:做用之一是爲異種機通訊提供一種公共語言,以便能進行互操做。

    • 應用層:應用層嚮應用程序提供服務,這些服務按其嚮應用程序提供的特性分紅組,並稱爲服務元素。

網絡編程經常使用模塊

模塊 描述
socket 基於傳輸層TCP、UDP協議進行網絡編程的模塊
asyncore socket模塊的異步版,支持基於傳輸層協議的異步通訊
asynchat asyncore的加強版
cgi 基本的CGI(Common Gateway Interface,早期開發動態網站的技術)支持
email E-mail 和 MIME消息處理模塊
ftplib 支持FTP協議的客戶端模塊
httplib、http.client 支持HTTP協議以及HTTP客戶端模塊
imaplib 支持IMAP4協議的客戶端模塊
mailbox 操做不一樣格式郵箱的模塊
mailcap 支持Mailcap文件處理的模塊
nntplib 支持NTTP協議的客戶端模塊
smtplib 支持SMTP協議(發送郵件)的客戶端模塊
poplib 支持POP3協議的客戶端模塊
telnetlib 支持TELNET協議的客戶端模塊
urllib 支持URL處理的模塊
xmlrpc 支持XML-RPC協議的服務器和客戶端模塊

接下來咱們會主要討論socket模塊

socket模塊

程序再使用socket以前,必須先建立socket對象,能夠經過如下語法建立socket實例:

s = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
  • family 參數用於指定網絡類型。總共有三個值:AF_INET(基於IPv4協議的網絡)、AF_INET6(基於IPv6協議的網絡)、AF_UNIX(UNIX網絡)
  • type 參數用於指定網絡Sock類型。默認是SOCK_STREAM(基於TCP協議的socket)、SOCK_DGRAM(基於UDP協議的socket)、SOCK_RAM(原始socket)
  • 後兩個能夠忽略

那麼創好socket對象後就須要區分服務端和客戶端了。

socket對象方法

做爲服務器端使用的socket必須綁定到指定IP地址和端口,並在該IP地址和端口進行監聽,接收來自客戶端的鏈接。

socket對象提供了以下經常使用方法:

  • bind(address):做爲服務端使用的socket調用該方法,將socket綁定到指定address元組(IP, 端口)。
  • listen([backlog]):服務端的監聽方法。
  • accept() :服務端接收來自客戶端的鏈接。
  • recv(bufsize[,flags]):接收socket中的數據(bytes)。
  • recvfrom(bufsize[,flags]):與上方法相似,返回(bytes, address)元組。
  • send(bytes[, flags]):向socket發送數據(必須已經創建鏈接),一般用於基於TCP協議的網絡。
  • sendto(bytes, address):用於UDP協議網絡中發送數據
  • sendfile(file, offset=0, count=None):將整個文件內容都發過去。
  • connect(address):客服端鏈接服務器。
  • connect_ex(address):與上方法大體相同,只是程序出錯時不會拋出異常,而是返回一個錯誤標識。
  • close():關閉鏈接,回收資源。

使用socket通訊(TCP)

  • 服務端:

    import socket
    
    host = socket.gethostname()  # 獲取本機host
    server = socket.socket()
    
    server.bind((host,8080))
    server.listen(5)
    
    while 1:
        c, addr = server.accept()  # 與客戶端創建鏈接
        c.send(b"start:")
    
        while 1:
            '''通訊循環,try爲了等待客戶端斷開後鏈接其餘的客戶端'''
            try:
                print(f"\t\033[1;36m{c.recv(1024).decode()}\033[0m")
                msg = input(">>").encode('utf-8')
                c.send(msg)
            except Exception:
                print("客戶端斷開鏈接了!")
                break
  • 客戶端:

    import socket
    
    host = socket.gethostname()  # 獲取本機host
    cilent = socket.socket()
    cilent.connect((host, 8080))
    
    while 1:
        '''通信循環'''
        print(f"\t\033[1;36m{cilent.recv(1024).decode()}\033[0m")
        msg = input(">>").encode('utf-8')
        cilent.send(msg)

粘包問題

TCP協議中的數據都是以數據流(type=SOCK_STREAM)的形式傳遞的,很容易發生幾回send的數據被一次recv(接收),或者被截成了好幾段。爲了解決粘包問題,咱們借用了數據報的思想,給每一條數據加一個固定長度的頭。這裏引入struct模塊來建立固定的頭。

示例:寫一個仿SHH的客戶端和服務端

  • 服務端

    import socket
    import subprocess
    import struct
    
    server = socket.socket()
    host = socket.gethostname()
    port = 8081
    
    server.bind((host, port))
    server.listen(5)
    
    while 1:
        c, addr = server.accept()
        print(addr, "鏈接了 >>>")
        msg = b"hello "
        le = struct.pack('i',len(msg))  # 將要發送的數據的長度封裝成固定長度爲4的二進制數據
        c.send(le)  # 先發送一個頭
        c.send(msg) # 再發送數據
        try:
            while 1:
                ssh = c.recv(1024)
                obj = subprocess.Popen(ssh.decode('gbk'), shell=True, stdout=subprocess.PIPE , stderr=subprocess.PIPE)
                # subprocess是與系統交互的模塊
                msg = obj.stdout.read() or obj.stderr.read()  # 必然拿到其中一項
                le = struct.pack('i',len(msg))  # 將結果打上頭
                c.send(le)  # 與上同樣
                c.send(msg)
        except Exception:
            print(" 斷開鏈接 !")
  • 客戶端

    import socket
    import struct
    
    client = socket.socket()
    host = socket.gethostname()
    port = 8081
    
    client.connect((host,port))
    
    while 1:
        le = struct.unpack('i', client.recv(4))[0] # 解析頭,獲取數據長度
        print(client.recv(le).decode('gbk'))  # shell只接收gbk編碼
        ssh = input(" 請輸入命令 >>")
        client.send(ssh.encode('gbk'))  # 返回執行結果

socketserver模塊

socketserver模塊實現了多線程通訊。使用socketserver建立服務端:

#使用socketserver寫服務端
import socketserver

#本身定義一個類,必須繼承StreamRequestHandler或BaseRequestHandler
class MyTcp(socketserver.StreamRequestHandler):
    #必須重寫handle方法
    def handle(self):
        '''這是服務端與客戶端交互的方法'''
        try:
            while True :  #通訊循環
                # print(self)
                #給客戶端回消息
                #conn對象就是request
                #接收數據
                print(self.client_address)
                data=self.request.recv(1024)
                print(data)
                if len(data)==0:
                    return
                #發送數據
                self.request.send(data.upper())
        except Exception:
            pass
        
if __name__ == '__main__':
    #實例化獲得一個tcp鏈接的對象,Threading意思是說,只要來了請求,它自動的開線程來處理鏈接跟交互數據
    #第一個參數是綁定的地址,第二個參數傳一個類
    server=socketserver.ThreadingTCPServer(('127.0.0.1',8009),MyTcp)
    #一直在監聽
    #這麼理解:只要來一個請求,就起一個線程交互
    server.serve_forever()
相關文章
相關標籤/搜索