詳解python的網絡編程基礎

一.什麼是網絡編程

網絡編程涉及到一些計算機基礎知識,還跟你的電腦系統有關,mac os/Linux和windows是不同的,由於我用的是windows,所以以下所有都是windows操作系統的適用的,並且裡面的字符編碼windows和mac os也是不同的,這裡我們實現的隻是簡單的服務端發送什麼,客戶端就接收到什麼,之後還會有模擬ssh的遠程命令還有粘包問題,最後也可以實現文件的下載。

網絡編程還涉及到重要的一部分理論知識包括什麼是網絡還有比較重要的五層協議,以我的理解,這些東西就是專業的告訴你,在你的電腦上假設要接收一些文件,這些東西是怎麼傳輸過來的,你的電腦又是怎麼接收的,所以這裡無非就是你的電腦即客戶端和傳輸文件的一方即服務點進行的一個交互,這些我都建議你去聽一下(我感覺以個人能力講這些會有點水平不足)但是大的方面都離不開一種交互。

二.socket

眾所周知,python功能的強大很大一方面就在於它有強大的第三方外部庫,socket這個庫又叫套接字,專業解釋就是應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間可以通訊,進而通過socket這個庫就可以實現我在前面所說的客戶端和服務端的交互。

1.socket的基本語法

socket(family,type,[protocol])

其中的family有三種

socket.AF_UNIX 隻能夠用於單一的Unix系統進程間通信

socket.AF_INET 服務器之間網絡通信

socket.AF_INET6 IPv6

type也有三種

socket.SOCK_STREAM 流式socket , 當使用TCP時選擇此參數

socket.SOCK_DGRAM數據報式socket ,當使用UDP時選擇此參數

socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。

而protocol 指明所要接收的協議類型,通常為0或者不填,基本上是不寫的

2.與socket有關的一些函數

服務端函數

address一般指你的電腦上的ip地址,即你打開Windows的命令提示符,你聯網之後輸入ipconfig命令,裡面的IPV4

s.bind(address)將套接字綁定到地址,即在客戶端中,你要把你的程序綁定一個ip和端口才能實現交互

s.listen(backlog)操作系統可以掛起的最大連接數量。即你的服務端最多能把數據傳給幾個客戶端

s.accept()接受TCP連接並返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據。address是連接客戶端的地址。

客戶端函數

s.connect(address)連接到客戶端address處的套接字

s.recv(bufsize)接受TCP套接字的數據。數據以字符串形式返回,bufsize指定要接收的最大數據量。

公共函數

s.send(string)

發送TCP數據。將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。

s.sendall(string)

完整發送TCP數據。將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。

s.recvfrom(bufsize)

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

s.sendto(string,address)

發送UDP數據。將數據發送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。

s.close()

關閉套接字。

三.程序需求

既然是實現一種交互,那麼生活中打電話和接收電話也可以理解為一種交互,打電話就可以理解為發送數據,收電話就可以理解為接收數據,這樣就有瞭服務端和客戶端,這裡我們以這樣一種場景實現交互。

服務端分析

如果你要發送數據首先你得有一個手機然後引入socket進行交互,然後你要進行一系列的綁定操作即你需要上面的函數來實現,首先你在客戶端模擬一個手機引入socket之後,你需要進行綁定,開機,等待電話鏈接,收發消息,掛電話等功能

註意:

1.在綁定操作過程中,你需要聯網之後輸入你本機的ip地址,即你打開命令提示符你輸入ipconifg命令的IPV4地址

2.在綁定你的地址後後面會加一個端口號,這個端口號是任意的,不過有時候會被占用,被占用則更改以下就好

3.在手法消息中還有一個upper函數,是將客戶端發送過來的數據以大寫的形式在發送給客戶端

4.在客戶端用accept是接受TCP連接並返回(conn,address)

--------------------------------服務端----------------------
import socket
#1.買手機
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# print(phone)         #測試你的手機
 
#2.插卡 綁定手機卡
phone.bind(("192.168.2.18",3234))
 
#3.開機
phone.listen(5)   #5代表最大掛起的連接數
 
#4.等電話鏈接
print("starting")
conn,client_add = phone.accept()
 
# 5.收發消息
data=conn.recv(1024)         #1024代表接收數據的最大數,單位是bits
print("客戶端數據",data)
 
conn.send(data.upper())
 
#6.掛電話
conn.close()
 
#7.關機
phone.close()

客戶端分析

服務端分析完,客戶端和服務端是一一對應的,在收發消息這裡,客戶端給服務端發送一個小寫的hello,服務端就又會給客戶端回一個大寫的HELLO

-------------------------------客戶端-----------------------------
import socket
#1.買手機
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# print(phone)
 
#2.撥號
phone.connect(("192.168.2.18",3234))
 
#3.發收消息
 
phone.send("hells".encode("utf"))
data=phone.recv(1024)
print(data)
 
 
phone.close()

四.代碼升級

加上通信循環

上面的代碼我們可以看出,我們實現的太簡單瞭,我們隻是固定的讓它收發消息,我們如何讓這個程序在客戶端和服務端之間循環並且收發你想要的東西呢,這是我們加入輸入和循環就可以

----------------------------服務端------------------------
import socket
 
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# print(phone)
phone.bind(("172.20.10.4",3234))
phone.listen(5)   print("starting")
conn,client_add = phone.accept()
print(client_add)
 
while True:
    data=conn.recv(1024)        
    print("客戶端數據",data)
 
    conn.send(data.upper())
 
conn.close()
phone.close()
----------------------------------客戶端------------------------
import socket
 
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# print(phone)
phone.connect(("172.20.10.4",3234))
 
while True:
    msg = input(">>>:").strip()
    phone.send(msg.encode("utf-8"))
    data=phone.recv(1024)
    print(data)
 
phone.close()

大傢可以看到,我們隻是在客戶端裡面加入瞭循環輸入功能如何在服務端裡加入循環,這樣就可以實現一種循環輸入

  加上連接循環以及完善

我們以上的程序都是一個客戶端對應一個服務端,但是真正應該服務端可以對應多個客戶端收發數據。我們主要修改的是服務端代碼,客戶端代碼應該是變化不大的,除瞭客戶端應該加一個判斷是否有數據發送。

1.我們沒有學習並發編程,所以我們的代碼用循環來實現即你的服務端接受完一個客戶端的數據以後,你可以繼續去接收另一個客戶端的數據

2.除此之外我們還加上瞭一行代碼用於判斷你的端口是否被占用這樣就可以減少出錯

3.我在加上一個客戶端之後我們會出現另外一個問題就是我們到底需不需要另一個客戶端傳輸的數據或者說我們的多個客戶端到底有沒有都發送數據,所以我們這裡需要一個try和except先去判斷一下

--------------------------------服務端-----------------------
import socket
 
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)  #判斷接口是否被占用
phone.bind(("172.20.10.4",3234))
phone.listen(5)   
print("starting")
while True:       #沒有學習並發編程 沒辦法執行代碼後返回到這繼續執行,所以用循環解決
    conn,client_add = phone.accept()
    print(client_add)
    while True:
        try:
            data=conn.recv(1024)         
            if not data:break         
            print("客戶端數據",data)
            conn.send(data.upper())
        except ConnectionResetError :
            break
            conn.close()
phone.close()
-----------------客戶端1-----------------
import socket
 
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(("172.20.10.4",3234))
 
while True:
    msg = input(">>>:").strip()
    if not msg: continue          #若輸入空則繼續循環
    phone.send(msg.encode("utf-8"))
    # print("if send none")
    data=phone.recv(1024)
    print(data)
    # print(data.decode("utf-8"))
 
phone.close()
-----------------------------客戶端2---------------------
import socket
 
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(("172.20.10.4",3234))
 
while True:
    msg = input(">>>:").strip()
    if not msg: continue          #若輸入空則繼續循環
    phone.send(msg.encode("utf-8"))
    # print("if send none")
    data=phone.recv(1024)
    print(data)
    # print(data.decode("utf-8"))
 
phone.close()

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: