Python網絡編程——UDP與TCP

Python網絡編程——UDP與TCP

<<<CSDN排版不美觀,想看排版美觀的進網絡編程——UDP與TCP>>>

1、用戶數據報協議UDP(User Datagram Protocol)

①用戶數據報協議UDP(User Datagram Protocol)
UDP協議全稱是用戶數據報協議,在網絡中它與TCP協議一樣用於處理數據包,是一種無連接的協議。
②IP(網絡之間互連的協議)
互聯網協議地址(英語:Internet Protocol Address,又譯爲網際協議地址),縮寫爲IP地址(英語:IP Address),是分配給用戶上網使用的網際協議(英語:Internet Protocol, IP)的設備的數字標籤。常見的IP地址分爲IPv4與IPv6兩大類,但是也有其他不常用的小分類。

查看IP信息
Linux在終端輸入 ifconfig ,可以看到 IP 信息
windows在cmd中輸入 ipconfig,可以看到 IP信息

sudo ifconfig ensXX down 關閉ensXX網卡
sudo ifconfig ensXX up 開啓ensXX網卡

私有IP
國際規定有一部分IP地址是用於我們的局域網使用,也就是屬於私網IP,不在公網中使用的,它們的範圍是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255

IP地址127.0.0.1~127.255.255.255用於迴路測試,
如:127.0.0.1可以代表本機IP地址,用http://127.0.0.1就可以測試本機中配置的Web服務器。

③端口
"端口"是英文port的意譯,可以認爲是設備與外界通訊交流的出口。端口可分爲虛擬端口和物理端口,其中虛擬端口指計算機內部或交換機路由器內的端口,不可見。例如計算機中的80端口、21端口、23端口等。物理端口又稱爲接口,是可見端口,計算機背板的RJ45網口,交換機路由器集線器等RJ45端口。
IP可以看成是一間房子,而端口可以看成是房子裏的門
端口號只有整數,範圍是從0到65535

知名端口
知名端口號(well-known port numbers)就是那些由互聯網名稱與數字地址分配機構(ICANN)預留給傳輸控制協議(TCP)和用戶數據包協議(UDP)使用的端口號。範圍是0到1023。
比如:80端口分配給HTTP服務,21端口是分配給FTP服務的
知名端口可以理解成,比如電話號碼,中國110,119,120等等,這些電話都已經按規定分配好了

動態端口
動態端口號(dynamic port numbers),即私人端口號(private port numbers),是可用於任意軟件與任何其他的軟件通信的端口數,使用因特網的傳輸控制協議,或用戶傳輸協議。動態端口的範圍是從1024到65535

查看端口號
用“netstat -an”查看端口狀態
lsof -i [tcp/udp]:2425

④socket套接字
Socket的英文原義是“孔”或“插座”。作爲BSD UNIX的進程通信機制,取後一種意思。通常也稱作"套接字",用於描述IP地址和端口,是一個通信鏈的句柄,可以用來實現不同虛擬機或不同計算機之間的通信。
網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱爲一個socket。建立網絡通信連接至少要一對端口號(socket)。socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網絡開發所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通信的能力。

Python創建socket
import socket
socket.socket(AddressFamily, Type)
Address Family:可以選擇 AF_INET(用於 Internet 進程間通信) 或者 AF_UNIX(用於同一臺機器進程間通信),實際工作中常用AF_INET
Type:套接字類型,可以是 SOCK_STREAM(流式套接字,主要用於 TCP 協議)或者 SOCK_DGRAM(數據報套接字,主要用於 UDP 協議)
套接字使用流程 與 文件的使用流程很類似
創建套接字
使用套接字收/發數據
關閉套接字

創建一個tcp socket(tcp套接字)
import socket

創建tcp的套接字

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

…這裏是使用套接字的功能(省略)…

不用的時候,關閉套接字

s.close()

創建一個udp socket(udp套接字)
import socket

創建udp的套接字

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

…這裏是使用套接字的功能(省略)… # 不用的時候,關閉套接字

s.close()

socket練習
#coding=utf-8
from socket import *

  1. 創建udp套接字

udp_socket = socket(AF_INET, SOCK_DGRAM)

  1. 準備接收方的地址

'192.168.1.103’表示目的ip地址

8080表示目的端口

dest_addr = (‘192.168.1.103’, 8080) # 注意 是元組,ip是字符串,端口是數字

  1. 從鍵盤獲取數據

send_data = input(“請輸入要發送的數據:”)

  1. 發送數據到指定的電腦上的指定程序中

udp_socket.sendto(send_data.encode(‘utf-8’), dest_addr)

  1. 關閉套接字

udp_socket.close()

udp網絡程序-發送、接收數據
#coding=utf-8
from socket import *

  1. 創建udp套接字

udp_socket = socket(AF_INET, SOCK_DGRAM)

  1. 準備接收方的地址

dest_addr = (‘192.168.236.129’, 8080)

  1. 從鍵盤獲取數據

send_data = input(“請輸入要發送的數據:”)

  1. 發送數據到指定的電腦上

udp_socket.sendto(send_data.encode(‘utf-8’), dest_addr)

  1. 等待接收對方發送的數據

recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字節數

  1. 顯示對方發送的數據

接收到的數據recv_data是一個元組

第1個元素是對方發送的數據

第2個元素是對方的ip和端口

print(recv_data[0].decode(‘gbk’))
print(recv_data[1])

  1. 關閉套接字

udp_socket.close()

編碼與解碼
編碼
str.encode()
解碼
bytes.decode()

綁定端口
#coding=utf-8
from socket import *

  1. 創建套接字

udp_socket = socket(AF_INET, SOCK_DGRAM)

2. 綁定本地的相關信息,如果一個網絡程序不綁定,則系統會隨機分配

local_addr = (’’, 7788) # ip地址和端口號,ip一般不用寫,表示本機的任何一個ip
udp_socket.bind(local_addr)

3. 等待接收對方發送的數據

recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字節數

4. 顯示接收到的數據

print(recv_data[0].decode(‘gbk’))

5. 關閉套接字

udp_socket.close()
發送端可以不綁定端口號,防止佔用其他的端口,發送時採用隨機分配端口的策略;而接收端(服務端)必須綁定端口號

UDP聊天案例
import socket
def send_msg(udp_socket):
“”“獲取鍵盤數據,並將其發送給對方”""
# 1. 從鍵盤輸入數據
msg = input("\n請輸入要發送的數據:")
# 2. 輸入對方的ip地址
dest_ip = input("\n請輸入對方的ip地址:")
# 3. 輸入對方的port
dest_port = int(input("\n請輸入對方的port:"))
# 4. 發送數據
udp_socket.sendto(msg.encode(“utf-8”), (dest_ip, dest_port))
def recv_msg(udp_socket):
“”“接收數據並顯示”""
# 1. 接收數據
recv_msg = udp_socket.recvfrom(1024)
# 2. 解碼
recv_ip = recv_msg[1]
recv_msg = recv_msg[0].decode(“utf-8”)
# 3. 顯示接收到的數據
print(">>>%s:%s" % (str(recv_ip), recv_msg))
def main():
# 1. 創建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 綁定本地信息
udp_socket.bind(("", 7890))
while True:
# 3. 選擇功能
print("="*30)
print(“1:發送消息”)
print(“2:接收消息”)
print("="*30)
op_num = input(“請輸入要操作的功能序號:”)
# 4. 根據選擇調用相應的函數
if op_num == “1”:
send_msg(udp_socket)
elif op_num == “2”:
recv_msg(udp_socket)
else:
print(“輸入有誤,請重新輸入…”)
if name == “main”:
main()

⑤瞭解:
單工(Simplex Communication)
單工(Simplex Communication)模式的數據傳輸是單向的。通信雙方中,一方固定爲發送端,一方則固定爲接收端。信息只能沿一個方向傳輸,使用一根傳輸線

半雙工(Half Duplex)
半雙工(Half Duplex)數據傳輸指數據可以在一個信號載體的兩個方向上傳輸,但是不能同時傳輸。例如,在一個局域網上使用具有半雙工傳輸的技術,一個工作站可以在線上發送數據,然後立即在線上接收數據,這些數據來自數據剛剛傳輸的方向。像全雙工傳輸一樣,半雙工包含一個雙向線路(線路可以在兩個方向上傳遞數據)

全雙工(Full Duplex)
全雙工(Full Duplex)是通訊傳輸的一個術語。通信允許數據在兩個方向上同時傳輸,它在能力上相當於兩個單工通信方式的結合。全雙工指可以同時(瞬時)進行信號的雙向傳輸(A→B且B→A)。指A→B的同時B→A,是瞬時同步的

2、傳輸控制協議TCP(Transmission Control Protocol)
①TCP(Transmission Control Protocol 傳輸控制協議)
TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連接的、可靠的、基於字節流的傳輸層通信協議,由IETF的RFC 793定義。在簡化的計算機網絡OSI模型中,它完成第四層傳輸層所指定的功能,用戶數據報協議(UDP)是同一層內另一個重要的傳輸協議。
TCP通信需要經過 創建連接、傳輸數據、終止連接
這種連接是一對一的,因此TCP不適用於廣播的應用程序,基於廣播的應用程序請使用UDP協議

可靠傳輸機制
1)TCP採用發送應答機制
TCP發送的每個報文段都必須得到接收方的應答才認爲這個TCP報文段傳輸成功

2)超時重傳
發送端發出一個報文段之後就啓動定時器,如果在定時時間內沒有收到應答就重新發送這個報文段。
TCP爲了保證不發生丟包,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然後接收端實體對已成功收到的包發回一個相應的確認(ACK);如果發送端實體在合理的往返時延(RTT)內未收到確認,那麼對應的數據包就被假設爲已丟失將會被進行重傳

3)錯誤校驗
TCP用一個校驗和函數來檢驗數據是否有錯誤;在發送和接收時都要計算校驗和

  1. 流量控制和阻塞管理
    流量控制用來避免主機發送得過快而使接收方來不及完全收下

TCP客戶端
from socket import *

創建socket

tcp_client_socket = socket(AF_INET, SOCK_STREAM)

目的信息

server_ip = input(“請輸入服務器ip:”)
server_port = int(input(“請輸入服務器port:”))

鏈接服務器

tcp_client_socket.connect((server_ip, server_port))

提示用戶輸入數據

send_data = input(“請輸入要發送的數據:”)
tcp_client_socket.send(send_data.encode(“gbk”))

接收對方發送過來的數據,最大接收1024個字節

recvData = tcp_client_socket.recv(1024)
print(‘接收到的數據爲:’, recvData.decode(‘gbk’))

關閉套接字

tcp_client_socket.close()

TCP服務端
流程:
socket創建套接字
bind綁定IP和Port
listen使套接字變爲可以被動鏈接狀態
accept等待客戶端的鏈接
recv/send接收發送數據
from socket import *

創建socket

tcp_server_socket = socket(AF_INET, SOCK_STREAM)

本地信息

address = (’’, 7788)

綁定

tcp_server_socket.bind(address)

使用socket創建的套接字默認的屬性是主動的,使用listen將其變爲被動的,這樣就可以接收別人的鏈接了

tcp_server_socket.listen(128)

如果有新的客戶端來鏈接服務器,那麼就產生一個新的套接字專門爲這個客戶端服務

client_socket用來爲這個客戶端服務

tcp_server_socket就可以省下來專門等待其他新客戶端的鏈接

client_socket, clientAddr = tcp_server_socket.accept()

接收對方發送過來的數據

recv_data = client_socket.recv(1024) # 接收1024個字節
print(‘接收到的數據爲:’, recv_data.decode(‘gbk’))

發送一些數據到客戶端

client_socket.send(“thank you !”.encode(‘gbk’))

關閉爲這個客戶端服務的套接字,只要關閉了,就意味着爲不能再爲這個客戶端服務了,如果還需要服務,只能再次重新連接

client_socket.close()

文件下載器
服務器
from socket import *
import sys
def get_file_content(file_name):
“”“獲取文件的內容”""
try:
with open(file_name, “rb”) as f:
content = f.read()
return content
except:
print(“沒有下載的文件:%s” % file_name)
def main():
if len(sys.argv) != 2:
print(“請按照如下方式運行:python3 xxx.py 7890”)
return
else:
# 運行方式爲python3 xxx.py 7890
port = int(sys.argv[1])
# 創建socket
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
# 本地信息
address = (’’, port)
# 綁定本地信息
tcp_server_socket.bind(address)
# 將主動套接字變爲被動套接字
tcp_server_socket.listen(128)
while True:
# 等待客戶端的鏈接,即爲這個客戶端發送文件
client_socket, clientAddr = tcp_server_socket.accept()
# 接收對方發送過來的數據
recv_data = client_socket.recv(1024) # 接收1024個字節
file_name = recv_data.decode(“utf-8”)
print(“對方請求下載的文件名爲:%s” % file_name)
file_content = get_file_content(file_name)
# 發送文件的數據給客戶端
# 因爲獲取打開文件時是以rb方式打開,所以file_content中的數據已經是二進制的格式,因此不需要encode編碼
if file_content:
client_socket.send(file_content)
# 關閉這個套接字
client_socket.close()
# 關閉監聽套接字
tcp_server_socket.close()
if name == “main”:
main()

客戶端
from socket import *
def main():
# 創建socket
tcp_client_socket = socket(AF_INET, SOCK_STREAM)
# 目的信息
server_ip = input(“請輸入服務器ip:”)
server_port = int(input(“請輸入服務器port:”))
# 鏈接服務器
tcp_client_socket.connect((server_ip, server_port))
# 輸入需要下載的文件名
file_name = input(“請輸入要下載的文件名:”)
# 發送文件下載請求
tcp_client_socket.send(file_name.encode(“utf-8”))
# 接收對方發送過來的數據,最大接收1024個字節(1K)
recv_data = tcp_client_socket.recv(1024)
# print(‘接收到的數據爲:’, recv_data.decode(‘utf-8’))
# 如果接收到數據再創建文件,否則不創建
if recv_data:
with open("[接收]"+file_name, “wb”) as f:
f.write(recv_data)

# 關閉套接字
tcp_client_socket.close()

if name == “main”:
main()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章