標籤:
thanks to?http://www.liaoxuefeng.com
網路中的各種協議
1. IP協議:IP協議是重要的網路通訊協定,對應了每台電腦的唯一標識:IP地址,實際上就是每台計算機連結網路的介面,通常是網卡。IP協議負責把資料從一台電腦通過網路傳到另外一台電腦,資料被分割成小塊,然後通過IP包(IP包包含資料、源IP地址和目標IP地址、源連接埠和目標連接埠),通過兩台電腦之間串連的線路發送出去,但是IP協議並不保證資料準確到達,同時不保證順序到達。
2. TCP協議:TCP協議建立在IP協議之上,將兩台電腦通過握手,建立可靠的串連,並且對每個IP包編號,確保資料順序可靠到達,如果發生丟包,則自動重發。在TCP協議的基礎上,有更多進階的協議:HTTP、SMTP等常見協議。
3. UDP協議:UDP協議不需要建立可靠串連,直接發送包,但是不保證能夠準確到達。UDP協議的優點就是速度快,在某一些不要求可靠達到的資料,可以使用UDP協議。
TCP用戶端編程
建立網路連接的基礎是建立一個socket,socket指定了目標電腦的IP地址和連接埠號碼,以及協議類型。
在用戶端發起TCP串連,需要指定socket的協議為TCP/IP協議:
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)?
socket類在執行個體化時,接受4個參數:family=AF_INET,type=SOCK_STREAM,proto=0,_sock=None
第四個參數_sock是一個_realsocket 對象,如果指定了該對象,則會從該對象中繼承family、type、photo屬性,並且繼承delegate方法:("recv", "recvfrom", "recv_into", "recvfrom_into","send", "sendto”)。 _realsocket類是c實現的,所以只能看到標頭檔_realsocket.py。
前三個參數依次為:地址協議、流協議、介面協議(?)
建立了socket對象之後,通過connet方法建立串連:
s.connet((‘www.baidu.com’),80)
connet方法要求傳入一個地址,如果是IP類的socket,則需要傳入一個元組,包含地址(會被dns解析)和連接埠
建立串連之後,就可以發送請求。TCP協議是雙向串連的,雙方都可以發送資料,但是具體發送的規則有更加高層的協議,如http協議決定(http協議規則:必須有用戶端發送請求給伺服器,伺服器收到後才發資料給用戶端):
s.send(‘GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n‘)
如上發送一條HTTP 1.1協議請求,擷取百度首頁的內容,請求發送後,百度伺服器就會響應請求,並且向我們發送首頁的內容,通過recv方法接收伺服器發來的資訊,並且指定單次接收的最大位元組數:
buffer = []
while True:
? ??d = s.recv(1024)
? ? if d:
? ? ? ? buffer.append(d)
? ? else:
? ? ? ? break
data = ‘‘.join(buffer)
如上data中包含了從伺服器中接收的全部資料,包括一個http頭,和真正需要接收的網頁本身,再發送請求時,我們以兩個斷行符號換行來分割http頭和接收到的網頁,所以將其從新分離即可得到百度首頁的內容:
header, html = data.split(‘\r\n\r\n‘, 1)
將其寫到檔案中:
with open(‘baidu.html‘, ‘wb‘) as f:
? ? f.write(html)
用瀏覽器開啟baidu.html,即可看到百度首頁的內容了
總指令碼如下:
__author__ = ‘liangzb‘
import socket
s = socket.socket(family=2,type=socket.SOCK_STREAM)
print type(s.family)
s2 = socket.socket(_sock=s)
print s2.family
s.connect((‘www.baidu.com‘,80))
s.send(‘GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n‘)
buffer = []
while True:
? ? d = s.recv(1024)
? ? if d:
? ? ? ? buffer.append(d)
? ? else:
? ? ? ? breakdata = ‘‘.join(buffer)
s.close()
# print data
header, html = data.split(‘\r\n\r\n‘, 1)
# print header
with open(‘baidu.html‘, ‘w‘) as f:
? ? f.write(html)
TCP服務端編程
伺服器和用戶端的區別就是伺服器需要接受來自不同用戶端的請求,並且分別作出響應,為了實現該過程,需要先建立一個綁定,綁定伺服器的ip地址和連接埠:
s.bind((‘127.0.0.1‘, 9999))
之後,調用listen方法監聽來自該連接埠訊息,傳入參數指定最大等待串連的數量:
listen(max_connection)
接下來,伺服器應該保持運行狀態,啟用一個死迴圈來接收來自用戶端的串連,accept方法可以接收來自用戶端的串連,並且返回用戶端的socket對象和地址。需要注意的是,對於每一個請求,都應該新開一條線程或者進程(win下只能開線程)來處理:
while True:
? ? sock, addr = s.accept()
? ? t = threading.Thread(target=tcplink, args=(sock, addr))
? ? t.start()
編寫處理函數來響應請求:
def tcplink(sock, addr):
? ? print ‘Accept new connection from %s:%s...‘ % addr
? ? sock.send(‘Welcome!‘)
? ? while True:
? ? ? ? data = sock.recv(1024)
? ? ? ? time.sleep(1)
? ? ? ? if data == ‘exit‘ or not data:
? ? ? ? ? ? break
? ? ? ? sock.send(‘Hello, %s!‘ % data)
? ? sock.close()
? ? print ‘Connection from %s:%s closed.‘ % addr
服務端總指令碼如下:
__author__ = ‘liangzb‘
import threading
import socket
import time
def tcplink(sock, addr):
? ? print ‘Accept new connection from %s:%s...‘ % addr
? ? sock.send(‘Welcome!‘)
? ? while True:
? ? ? ? data = sock.recv(1024)
? ? ? ? time.sleep(1)
? ? ? ? if data == ‘exit‘ or not data:
? ? ? ? ? ? break
? ? ? ? sock.send(‘Hello, %s!‘ % data)
? ? sock.close()
? ? print ‘Connection from %s:%s closed.‘ % addr
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((‘127.0.0.1‘, 9999))
s.listen(5)
print ‘Waiting for connection...‘
while True:
? ? sock, addr = s.accept()
? ? t = threading.Thread(target=tcplink, args=(sock, addr))
? ? t.start()
可以通過另外一個用戶端給該服務端發送訊息來測試,注意,這裡綁定的ip地址是127.0.0.1,是本地地址,只能是本機訪問
用戶端程式參考:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((‘127.0.0.1‘, 9999))
print s.recv(1024)
for data in [‘Michael‘, ‘Tracy‘, ‘Sarah‘]:
? ? s.send(data)
? ? print s.recv(1024)
s.send(‘exit‘)
s.close()
原始碼來自:http://www.liaoxuefeng.com/ ,thanks to liaoxuefeng for share
運行效果:
UDP編程
協議是不確定串連可靠性的,socket流協議應該設定為:SOCK_DGRAM:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
由於UDP協議的特徵,UDP的服務端在建立串連時,不需要調用listen方法,而是直接對接收到的請求作出回應,而用戶端的資訊,則又recvfrom方法返回的元組得到。在發送訊息時,也只需要通過sendto方法,將用戶端的地址作為第二個參數傳入。
而在UDP協議的用戶端,同樣不需要先經過串連,而是直接將準備好的請求發送到某一個地址,利用sendto方法,將服務端的地址作為第二個參數傳入,其他接收方式則和TCP相同。
Python網路編程