python ==》 網絡編程

 1、服務端和客戶端

BS架構 (騰訊通軟件:server+client)python

CS架構 (web網站)linux

 

C/S架構與socket的關係:web

咱們學習socket就是爲了完成C/S架構的開發shell

2、OSI七層模型

互聯網協議按照功能不一樣分爲osi七層或tcp/ip五層或tcp/ip四層

學習socket必定要先學習互聯網協議:編程

1.首先:本節課程的目標就是教會你如何基於socket編程,來開發一款本身的C/S架構軟件json

2.其次:C/S架構的軟件(軟件屬於應用層)是基於網絡進行通訊的windows

3.而後:網絡的核心即一堆協議,協議即標準,你想開發一款基於網絡通訊的軟件,就必須遵循這些標準。設計模式

4.最後:就讓咱們從這些標準開始研究,開啓咱們的socket編程之旅網絡

socket:就是位於 應用層和傳輸層 之間。socket幫咱們封裝了一系列協議,統一標準。架構

3、socket是什麼?

Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。

因此,咱們無需深刻理解tcp/udp協議,socket已經爲咱們封裝好了,咱們只須要遵循socket的規定去編程,寫出的程序天然就是遵循tcp/udp標準的。

4、套接字發展史及分類

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

 

一、基於文件類型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,能夠經過訪問同一個文件系統間接完成通訊

 

二、基於網絡類型的套接字家族

套接字家族的名字:AF_INET

(還有AF_INET6被用於ipv6,還有一些其餘的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是不多被使用,或者是根本沒有實現,全部地址家族中,AF_INET是使用最普遍的一個,python支持不少種地址家族,可是因爲咱們只關心網絡編程,因此大部分時候我麼只使用AF_INET)

 

5、套接字工做流程

      生活中的場景,你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲後提起電話,這時你和你的朋友就創建起了鏈接,就能夠講話了。等交流結束,掛斷電話結束這次交談。    

生活中的場景就解釋了這工做原理,也許TCP/IP協議族就是誕生於生活中,這也不必定。

socket例子:

1.服務端與客戶端的正常通訊。

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('really ==== go!!')

conn,client_addr=phone.accept()
print(conn,client_addr)

data=conn.recv(1024)
conn.send(data.upper())
print('client data:<%s>'%data)

conn.close()
phone.close()
服務端
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

phone.send('hello'.encode('utf-8'))
data1 = phone.recv(1024)
print('server back res:<%s>'%data1)

phone.close()
客戶端
首先:
服務端 先開始運行,等待接收,
以後,客戶端運行,向服務端發送信息。

結果以下:
服務端:
really ==== go!!
<socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53789)> ('127.0.0.1', 53789)
client data:<b'hello'>

客戶端:
server back res:<b'HELLO'>

客戶端發了 hello  給服務端, 服務端收到信息,作了 大寫化處理,返回給客戶端。

2.服務端與客戶端的正常通訊。socket通訊循環

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('really ==== go!!')

conn,client_addr=phone.accept()
print(conn,client_addr)

while True:  #通訊循環
    data=conn.recv(1024)
    # print('server has recv')
    conn.send(data.upper())
    print('client data:<%s>'%data)

conn.close()
phone.close()
服務端
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #若是cmd爲空,繼續發
    phone.send(cmd.encode('utf-8'))
    print('====> has send')
    data = phone.recv(1024)
    print('server back res:<%s>'%data)

phone.close()
客戶端
首先:
這裏比上一個例子,優化了,這裏設置了input,能夠本身輸入。
添加了個循環,當客戶端輸入爲空,不在報錯,而是須要繼續輸入。

結果以下:
服務端:
really ==== go!!
<socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53865)> ('127.0.0.1', 53865)
client data:<b'1'>
client data:<b'2'>
client data:<b'3'>
client data:<b'a'>
client data:<b'b'>
client data:<b'c'>

客戶端:
>>:1
====> has send
server back res:<b'1'>
>>:2
====> has send
server back res:<b'2'>
>>:3
====> has send
server back res:<b'3'>
>>:a
====> has send
server back res:<b'A'>
>>:b
====> has send
server back res:<b'B'>
>>:c
====> has send
server back res:<b'C'>

注意:
這裏服務端在接到客戶端的額信息是,只作了加大化吃力,因此把abc處理後爲ABC返回給客戶端。

3.服務端與客戶端的正常通訊。socket連接循環。

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('really ==== go!!')
while True:  #連接循環
    conn,client_addr=phone.accept()
    print(conn,client_addr)

    while True:  #通訊循環
        try:
            data=conn.recv(1024)
            # print('server has recv')
            conn.send(data.upper())
            print('client data:<%s>'%data)
        except Exception:
            break
    conn.close()
phone.close()
服務端
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #若是cmd爲空,繼續發
    phone.send(cmd.encode('utf-8'))
    data = phone.recv(1024)
    print('server back res:<%s>'%data)
phone.close()
客戶端1
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #若是cmd爲空,繼續發
    phone.send(cmd.encode('utf-8'))
    print('====> has send')
    data = phone.recv(1024)
    print('server back res:<%s>'%data)

phone.close()
客戶端2
首先,這裏加的連接循環是爲了防止,當咱們有多個客戶端時,
要關閉其中一個,而不致使整個程序出錯。
在沒作連接循環前,當咱們關閉了其中一個客戶端,服務端那裏是不能在運行的。

輸出結果:
服務端:
really ==== go!!
<socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53898)> ('127.0.0.1', 53898)
client data:<b'1'>
client data:<b'2'>
client data:<b'a'>
client data:<b'b'>
client data:<b'c'>
<socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53911)> ('127.0.0.1', 53911)
<socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53911)> ('127.0.0.1', 53911)
client data:<b'a'>
client data:<b's'>
client data:<b'd'>

客戶端1:
>>:1
server back res:<b'1'>
>>:2
server back res:<b'2'>
>>:3
server back res:<b'3'>
>>:a
server back res:<b'A'>
>>:b
server back res:<b'B'>
>>:c
server back res:<b'C'>


客戶端2:
>>:a
====> has send
server back res:<b'A'>
>>:s
====> has send
server back res:<b'S'>
>>:d
====> has send
server back res:<b'D'>

4.socket模擬ssh遠程執行。

import subprocess
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)
print('really ==== go!!')
while True:  #連接循環
    conn,client_addr=phone.accept()
    print(conn,client_addr)

    while True:  #通訊循環
        try:
            cmd=conn.recv(1080)
            if not cmd: break  #針對linux
            #執行cmd命令,拿到cmd的結果,結果應該是bytes類型
            #。。。
            #發送命令結果
            res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,  # 正確
                                   stderr=subprocess.PIPE  # 錯誤
                                   )
            stdout = res.stdout.read()
            stderr = res.stderr.read()
            conn.send(stdout+stderr)
        except Exception:
            break
    conn.close()
phone.close()
服務端
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8081))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #若是cmd爲空,繼續發
    phone.send(cmd.encode('utf-8'))
    cmd_res = phone.recv(1080)
    print(cmd_res.decode('gbk'))
phone.close()
客戶端
說明:
1.客戶端遠程執行服務端。
2.登陸的是windows系統,用的是‘gbk’ 編碼。


服務端:
really ==== go!!
<socket.socket fd=400, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8081), raddr=('127.0.0.1', 53955)> ('127.0.0.1', 53955)


客戶端:
>>:dir
 驅動器 E 中的卷沒有標籤。
 卷的序列號是 0001-0682

 E:\zbk\work_\work_8.21 socket模擬ssh遠程執行 的目錄

2017/08/21  19:25    <DIR>          .
2017/08/21  19:25    <DIR>          ..
2017/08/21  19:25               333 客戶端1.py
2017/08/21  16:29               367 客戶端2.py
2017/08/21  19:25               966 服務端2.py
2017/08/21  19:05               413 模塊subprocess.py
               4 個文件          2,079 字節
               2 個目錄 266,249,355,264 可用字節

>>:ipconfig /all

Windows IP 配置

   主機名  . . . . . . . . . . . . . : DESKTOP-0QR7V9H
   主 DNS 後綴 . . . . . . . . . . . : 
   節點類型  . . . . . . . . . . . . : 混合
   IP 路由已啓用 . . . . . . . . . . : 否
   WINS 代理已啓用 . . . . . . . . . : 否

以太網適配器 以太網:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開鏈接
   鏈接特定的 DNS 後綴 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller
   物理地址. . . . . . . . . . . . . : 80-FA-5B-3C-8F-54
   DHCP 已啓用 . . . . . . . . . . . : 是
   自動配置已啓用. . . . . . . . . . : 是

無線局域網適配器 本地鏈接* 1:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開鏈接
   鏈接特定的 DNS 後綴 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter
   物理地址. . . . . . . . . . . . . : 70-1C-E7-32-BC-D5
   DHCP 已啓用 . . . . . . . . . . . : 是
   自動配置已啓用. . . . . . . . . . : 是

無線局域網適配器 WLAN:

   鏈接特定的 DNS 後綴 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Intel(R) Dual Band Wireless-
>>:
輸出結果

5.socket  解決粘包問題。

import struct
import subprocess
from socket import *

phone = socket(AF_INET,SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('ready go !!')

while True:
    conn,client.addr=phone.accept()
    print(conn,client_addr)
    while True:
        try:
        cmd = conn.recv(1024)
        if not cmd : break
        res = subprocess.Popen(cmd.decode('utf-8'),shell = True,
                                            stdout = stdout.subprocess.PIPE,
                                            stderr = stderr.subprocess.PIPE,)
        stdout  = res.stdout.read()
        stderr = res.stderr.read()
        header = struct.pack('i',len(stdout)+len(stderr))
        conn.send(header)
        conn.send(stdout)
        conn.send(stderr)
        except Exception:
            break
    conn.close()
phone.close()
服務端
import struct
from socket import *

phone = socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd : continue
    phone.send(cmd.encode('utf-8'))
    header_struct = phone.recv(4)
    unpack_res = struct.unpack('i',header_struct)
    total_size = unpack_res[0]
    total_data = b''
    recv_size = 0
    while recv_size < total_size:
        recv_data = phone.recv(1024)
        recv_size += len(recv_data)
        total_data += recv_data
    print(total_data.decode('gbk'))
phone.close()
客戶端

 6.json 解決粘包問題。

import socket
import subprocess
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8082)) #綁定手機卡
phone.listen(5) #開機

print('starting...')
while True: #連接循環
    conn,client_addr=phone.accept() #等電話 (連接,客戶的的ip和端口組成的元組)
    print('-------->',conn,client_addr)

    #收,發消息
    while True:#通訊循環
        try:
            cmd=conn.recv(1024)
            if not cmd:break #針對linux
            #執行cmd命令,拿到cmd的結果,結果應該是bytes類型
            #。。。。
            res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()
            #製做報頭
            header_dic = {
                'total_size': len(stdout)+len(stderr),
                'filename': None,
                'md5': None}

            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode('utf-8')
            #發送階段
            #先發報頭長度
            conn.send(struct.pack('i',len(header_bytes)))
            #再發報頭
            conn.send(header_bytes)

            #最後發送命令的結果
            conn.send(stdout)
            conn.send(stderr)
        except Exception:
            break
    conn.close() #掛電話
phone.close() #關機
服務端
import socket
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機
phone.connect(('127.0.0.1',8082)) #綁定手機卡

#發,收消息
while True:
    cmd=input('>>: ').strip()
    if not cmd:continue

    phone.send(cmd.encode('utf-8'))
    #先收報頭的長度
    header_len=struct.unpack('i',phone.recv(4))[0]

    #再收報頭
    header_bytes=phone.recv(header_len)
    header_json=header_bytes.decode('utf-8')
    header_dic=json.loads(header_json)
    total_size=header_dic['total_size']

    #最後收數據
    recv_size=0 #10241=10240+1
    total_data=b''
    while recv_size < total_size:
        recv_data=phone.recv(1024)
        recv_size+=len(recv_data)
        total_data+=recv_data
    print(total_data.decode('gbk'))
phone.close()
客戶端
相關文章
相關標籤/搜索