python 基礎 day16

TCP/IP

計算機爲了聯網,就必須規定通訊協議,早期的計算機網絡是由各個廠商規定的一些協議,他們之間互不兼容。

爲了把全世界的電腦能夠連接到一起,那麼就必須規定一套全球通用的協議,爲了完成這個目標,互聯網協議簇就是通訊協議標準,有了internet,任何私有網絡,只要支持這個協議就可以聯入互聯網

因爲互聯網協議包含了上百種協議標準,但是最重要的兩個協議就是TCP和IP協議,所以大家把互聯網協議簡稱TCP/IP協議。

通訊的時候,雙方必須都知道對方的標識,並且這些標識必須都是唯一的,互聯網上的每個計算機唯一標識就是IP地址【10.2.123.34】,如果一臺電腦同時接入多個網絡,那麼它就有多個IP地址,所以IP地址對應的實際上是計算機的網絡接口,通常是網卡。

IP協議負責把數據從一臺電腦通過網絡發送到另一臺電腦,發送的時候,我們的數據被分割成一小塊,一小塊的,然後通過IP包發送出去,IP包的特點就是按塊發送,它不保證到達也不保證順序到達。

  • IP地址實際上是一個32位整數(稱爲IPV4)

  • IPV6地址實際上是一個128位整數,它是IPV4的升級版

TCP協議則是建立在IP協議的基礎之上的,TCP協議負責在兩臺計算機之間建立可靠連接,保證數據包按順序到達,TCP協議會通過握手建立連接,然後對每個IP包進行編號,確保對方按順序收到,若是包丟掉了就自動重發。

一個TCP報文除了含有要傳輸的數據外,還包含了本機IP地址和目標IP地址,源端口與目標端口

端口:在兩臺計算機通信時,只發送IP是不夠的,因爲一臺電腦上可能執行多個網絡程序,一個TCP報文來了之後,應該交給哪個程序來處理,這個就需要使用端口號來進行區分,每個網絡程序都向操作系統申請了一個唯一的端口號。1024

三次握手與四次揮手

三次握手剛好能夠創建一個可靠連接。

TCP編程

socket是網絡編程的一個抽象的概念,通常我們用一個socket表示打開了一個網絡連接,而打開網絡連接需要知道目標計算機的IP地址和端口號,再指定協議即可。

客戶端

大多數連接都是可靠的TCP連接,創建連接時,主動發起連接的是客戶端,被動響應連接的叫服務器。

創建一個基於TCP連接的socket

#導入socket庫
import socket

#創建一個socket對象
#socket.AF_INET指定使用IPV4,若要使用IPV6則指定爲AF_INET6
#SOCK_STREAM指定使用面向流的TCP協議
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#客戶端要主動發起TCP連接必須知道服務器的IP地址和端口號
#一般的網絡地址可以通過域名轉到IP地址,但是服務器的端口號是如何確定的呢?
#作爲服務器,提供什麼樣的服務,其端口號就一定要固定,訪問網頁的端口號80,STMP的服務端口是25,端口號小於1024的是Internet標準服務端口,大於1024的可以隨意使用。
sock.connect(('www.sina.com.cn',80))
#注意:在這裏使用的是一個tuple,包含地址與端口號
#發送數據
#廣泛應用的有HTTP/1.0和HTTP/1.1兩個版本,1.1和1.0相比最大的特點就是增加對長連接的支持。
#HTTP/1.1支持長連接,在一個TCP連接上可以傳送多個HTTP請求和應答,減少建立和關閉連接的消耗和延遲
'''
完整的HTTP請求包括:一個請求行、若干HTTP頭域和可選的實體內容三部分
#請求行,請求行以一個方法符號開頭,以空格分開,後面跟着請求的URI和協議版本,格式如下:
#Method  Request-URI  HTTP-Version CRLF
#其中的Method表示請求方法,Request-URI是同一資源標識符,HTTP-Version表示請求的HTTP協議版本,CRLF表示回車換行。
具體:https://blog.csdn.net/wangyin159/article/details/47438875
 '''
sock.send(b'GET / HTTP/1.1\r\nHost:www.sina.com.cn\r\nConnection:close\r\n\r\n')
#注意:發送的文本格式必須符合HTTP標準,如果格式沒問題,接下來就可以接收數據了

#循環讀取接收
buffer = []
while True:
    #每次最多接收1k字節
    d = sock.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
data = b"".join(buffer)
#關閉連接
s.close()
header,html = data.split(b"\r\n\r\n",1)
print(header.decode("utf-8"))

#把接收的數據寫入文件
with open('sina.html','wb') as f:
    f.writer(html)  
服務器

與客戶端相比,服務器就要複雜一些

服務器進程需要先綁定一個端口並且監聽來自其他客戶端的鏈接,如果某個客戶端鏈接過來了,服務器就與該客戶端建立socket鏈接,之後的通訊就依賴於這個socket

因爲一臺服務器可以響應多個客戶端的請求,這樣若是來標識唯一的一個socket,則依賴下面四項:服務器地址,服務器端口,客戶端地址,以及客戶端端口。

還有,因爲服務器需要同時響應請求,所以,每個連接都需要開啓一個新的線程來處理,否則服務器一次就只能服務一個客戶端了。

服務器

import socket
import threading

#創建一個socket對象,指定協議
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#監聽端口
s.bind(('127.0.0.1',9999))
#注意:綁定端口的號的時候儘量不要使用小於1024的
#監聽端口,傳入的參數指等待連接的最大數量
s.listen(6)
def tcpLink(sock,addr):
    #打印連接成功
    print("Accept a new connection %s"% addr)
    #服務器發送數據到客戶端
    sock.send(b"Welcome to server")
    #循環接收客戶端發來的請求數據
    while True:
 		#接收數據,每次接收1024個字節
        data = sock.recv(1024)
        if not data or data.decode("utf-8") == 'end':
            break
        sock.send(("Server %s"%data.decode("utf-8")).encode("utf-8"))
     sock.close()
    print("end form %s"%addr)
    

#服務器通過一個永久的循環來接受來自客戶端的連接
while True:
    #接受一個新的連接
    sock,addr = s.accept()
    #創建一個新的線程來處理TCP連接
    t = threading.Thread(target=tcpLink,args=(sock,addr))
	#開啓線程
    t.start()

客戶端

import socket

#創建一個socket對象
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立連接
s.connect('127.0.0.1',9999)
#接收歡迎消息
print(s.recv(1024).decode("utf-8"))

for data in [b'Hello',b'Hi',b'nihao']:
    #發送數據
    s.send(data)
    print(s.recv(1024).decode('utf-8'))
#最後發送結束的標識
s.send(b'end')
#關閉連接
s.close()

UDP編程

TCP是,建立可靠的連接,並且通訊雙方都可以以流的形式發送數據,相對於TCP,UDP則是面向無連接的協議。

使用UDP協議時,不需要建立連接,只需要知道對方的IP地址和端口號,就可以直接發送數據包,但是能不能到達就不知道了

  • 優點:速度快,對於不要求可靠到達的數據,就可以使用UDP協議

  • 缺點:不可靠

服務器:

import socket
#創建一個socket對象
#socket.SOCK_DGRAM指定socket的類型是UDP
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#綁定端口號
s.bind(("127.0.0.1",9999))
print("bind udp ....")
while True:
    #接收數據,返回數據和客戶端的地址與端口
    data,addr = s.recvfrom(1024)
    print("recevied from %s"%addr)
    #參數一:要發送的數據  參數二:發送的地址
	s.sendto(b"udp server %s"%data,addr)

客戶端:

import socket

#創建一個socket對象
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
for data in [b'Hello',b'Hi',b'nihoa']:
    #發送數據
    s.sendto(data,('127.0.0.1',9999))
    #接收數據
    print(s.recv(1024).decode("utf-8"))
#關閉連接
s.close()

發郵件

登錄163郵箱 —> 設置 —>POP3/SMTP/IMAP —>客戶端授權密碼 —>開啓 —> 重置授權碼

注意: 授權碼儘量和郵件的登錄密碼不同[],將下面的password改爲授權碼

若要發郵件首先需要知道:

  • 服務器地址,發送郵箱地址,發送郵箱密碼,要發送的內容,對方的郵箱地址

  • 關於郵件:標題,發送者

打開服務連接-》登錄郵箱-》發送郵件-》退出郵箱

#發郵件的庫
import smtplib
#郵件文本
from email.mime.text import MIMEText

#STMP服務器
SMTPServer = 'smtp.163.com'

#發郵箱的地址
sender = '[email protected]'
#發送者郵箱密碼:授權碼
password = '1q2w3e'
#設置發送文本的內容
message = 'hello world'

#轉爲郵件文本
msg = MIMEText(message)
#標題
msg['Subject'] = '來自星星的我'
#發送者
msg['From'] = sender

#收件人
msg['To'] ='[email protected]'

#打開SMTP服務器,端口號一般爲25
mailServer = smtplib.SMTP(SMTPServer,25)
#登錄郵箱
mailServer.login(sender,password)
#發送郵件
mailServer.sendmail(sender,['[email protected]'],msg.as_string())
#退出郵箱
mailserver.quit()

說明:有時候會被識別爲垃圾郵件[其他郵箱都可以用,只要找到授權碼即可]

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