標籤:ndt 退出 tcpl rom ext 儲存 機器 訊息 包含
Socket網路編程一,Socket編程(1)Socket方法介紹
- Socket是網路編程的一個抽象概念。通常我們用一個Socket表示“開啟了一個網路連結“,而開啟一個Socket需要知道目標電腦的IP地址和連接埠號碼,再指定協議類型即可。
- 通訊端是一個雙向的通訊通道的端點。通訊端可能在溝通過程,進程之間在同一台機器上,或在不同的電腦之間的進程
- 要建立一個通訊端,必須使用Socket模組的socket.socket()方法
- 在socket模組中的一般文法:
s = socket.socket(socket_family,socket_type,protocol=0)
(3)TCP介紹
- 大多數串連都是可靠的TCP串連。建立TCP串連時,主動發起串連的叫用戶端,被動響應串連的叫伺服器
- 例如在瀏覽器中訪問新浪時,我們自己的電腦就是用戶端,瀏覽器會主動向新浪的伺服器發起串連。如果一切順利,新浪的伺服器接受了我們的串連,一個TCP串連就建立起來了,後面的通訊就是發送網頁內容了
(4)TCP編程示範-用戶端
- 要建立一個基於TCP串連的Socket,代碼示範:
1 import socket2 3 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)4 s.connect((‘www.sina.com.cn‘,80))
- 建立TCP串連後,就可以向伺服器發送請求,要求返回首頁的內容,發送的文字格式設定必須符合HTTP標準,然後接收伺服器返回的資料,最後關閉串連
(5)TCP編程示範-伺服器
- 和用戶端編程相比,伺服器編程就要複雜一些,伺服器處理序首先要綁定一個連接埠並監聽來自其他用戶端的串連。如果某個用戶端串連過來了,伺服器就與該用戶端建立Socket串連,隨後的通訊就靠這個Socket串連了
- 編寫一個簡單的伺服器程式,它接收用戶端串連,把用戶端發過來的字串加上Hello再發回去,代碼示範:
1 import socket 2 3 Host = ‘locakhost‘ #監聽的IP地址 4 port = 8888 #監聽的連接埠 5 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #建立通訊端 6 s.bind(Host,port) #綁定IP地址和連接埠 7 s.listen(5) #開始監聽 8 conn,addr = s.accept() #接受一個新串連 9 data = conn.recv(1024) #接收用戶端字串10 conn.sendall(data+‘Hello‘) #發送字串給用戶端
- 需要注意的是:同一個連接埠,被一個Socket綁定了以後,就不能被別的Socket綁定了
(6)UDP介紹
- TCP是建立可靠串連,並且通訊雙方都可以以流的形式發送資料。相對TCP,UDP則是面向不需連線的協議
- 使用UDP協議時,不需要建立串連,只需要知道對方的IP地址和連接埠號碼,就可以直接發資料包。但是,能不能到達並不清楚。
- 雖然用UDP傳輸資料不可靠,但它的優點是和TCP比,速度快,對於不要求可靠到達的資料,就可以使用UDP協議
(7)UDP編程示範
- 通過UDP協議傳輸資料。和TCP類似,使用UDP的通訊雙方也分為用戶端和伺服器。伺服器首先需要綁定連接埠,代碼示範:
1 s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)2 s.bind((‘127.0.0.1‘,9999)) #連接埠綁定
- 用戶端使用UDP時,首先仍然建立基於UDP的Socket,但是不需要調用connect(),直接通過sendto()給伺服器發資料,代碼示範:
1 s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)2 for data in [‘Michael‘,‘Tracy‘,‘Sarah‘]:3 s.sendto(data)
- 需要注意的是:伺服器綁定UDP連接埠和TCP連接埠互不衝突,UDP的9999連接埠與TCP的9999連接埠可以各自綁定
二,TCP編程舉例
Socket是網路編程的一個抽象概念。通常我們用一個Socket表示“開啟了一個網路連結”,而開啟一個Socket需要知道目標電腦的IP地址和連接埠號碼,再指定協議類型即可。
舉個例子,當我們在瀏覽器中訪問新浪時,我們自己的電腦就是用戶端,瀏覽器會主動向新浪的伺服器發起連結。如果一切順利,新浪的伺服器接收了我們的串連,一個TCP串連就建立起來了,後面的通訊就是發送網頁內容。
所以,我們要建立一個基於TCP串連的Socket,可以這樣做:
1 # 匯入socket庫2 import socket3 # 建立一個socket:4 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)5 # 建立串連6 s.connect((‘www.sina.com.cn‘,80))
建立Socket時,AF_INET指定使用IPv4協議,如果要用更先進的IPv6,就指定為AF_INET6。SOCK_STREAM指定使用面向流的TCP協議,這樣,一個Socket對象就建立成功,但是還沒有建立串連。
用戶端要主動發起TCP串連,必須知道伺服器的IP地址和連接埠號碼。新浪網站的IP地址可以用網域名稱www.sina.com.cn自動轉換到IP地址,而Web服務的標準連接埠80.
因此,我們串連新浪伺服器的代碼如下:
s = connect((‘www.sina.com.cn‘,80))
注意參數是一個tuple,包含地址和連接埠號碼。
建立TCP串連後,我們就可以向新浪伺服器發送請求,要求返回首頁的內容:
# 發送資料:s.send(‘GET / HTTP/1.1\r\nHost:www.sina.com.cn\r\nConnection: close\r\n\r\n‘)
TCP串連建立的是雙向通道,雙方都可以同時給對方發資料。但是誰先發誰後發,怎麼協調,要根據具體的協議來決定。例如,HTTP協議規定用戶端必須先發請求給伺服器,伺服器收到後才發資料給用戶端。
發送的文字格式設定必須符合HTTP標準,如果格式沒問題,接下來就可以接收新浪伺服器返回的資料了:
1 # 接收資料: 2 buffer = [] 3 while True: 4 # 每次最多接收1K位元組: 5 d = s.recv(1024) 6 if d: 7 buffer.append(d) 8 else: 9 break10 data = ‘‘.join(buffer)
接收資料時,調用recv(max)方法,一次最多接收指定的位元組數,因此,在一個while迴圈中反覆接收,直到recv()返回空資料,表示接收完畢,退出迴圈。
當我們接收完資料後,調用close()方法關閉Socket,這樣,一次完整的網路通訊就結束了:
# 關閉串連s.close()
接收到的資料包括HTTP頭和網頁本身,我們只需要把HTTP頭和網頁分離一下,把HTTP頭列印出來,網頁內容儲存到檔案:
header,html = data.split(‘\r\n\r\n‘,1)print header# 把接收的資料寫入檔案:with open(‘sina.html‘,‘wb‘) as f: f.write(html)
現在,只需要在瀏覽器中開啟這個sina.html檔案,就可以看到新浪的首頁了。
和用戶端編程相比,伺服器編程就要複雜一些。
伺服器處理序首先要綁定一個連接埠並監聽來自其他用戶端的串連。如果某個用戶端串連過來了,伺服器就與該用戶端建立Socket串連,隨後的通訊就靠這個Socket串連了。
所以,伺服器會開啟固定連接埠(比如80)監聽,每來一個用戶端串連,就建立該Socket串連。由於伺服器會開啟固定連接埠(比如80)監聽,每來一個用戶端串連,就建立該Socket串連。由於伺服器會有大量來自用戶端的串連,所以,伺服器要能夠區分一個Socket串連是和哪個用戶端綁定的。一個Socket依賴4項:伺服器位址,伺服器連接埠,用戶端地址,用戶端連接埠來唯一確定一個Socket。
但是伺服器還需要同時響應多個用戶端請求,所以,每個串連都需要一個新的進程或者新的線程來處理,否則,伺服器一次就只能服務一個用戶端了。
我們來編寫一個簡單的伺服器程式,它接收用戶端串連,把用戶端發過來的字串加上Hello再發回去。
首先,建立一個基於IPv4和TCP協議的Socket:
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
然後,我們要綁定監聽的地址和連接埠。伺服器可能有多塊網卡,可以綁定到某一塊網卡的IP地址上,也可以0.0.0.0綁定到所有的網路地址,還可以用127.0.0.1綁定到本機地址。127.0.0.1是一個特殊的IP地址,表示本機地址,如果綁定到這個地址,用戶端必須同時在本機運行才能串連,也就是說,外部的電腦無法串連進來。
連接埠號碼需要預先指定。因為我們寫的這個服務不是標準服務,所以用9999這個連接埠號碼。請注意,小於1024的連接埠號碼必須要有管理員權限才能綁定:
# 監聽連接埠s.bind((‘127.0.0.1‘,9999))
緊接著,調用listen()方法開始監聽連接埠,傳入的參數指定等待串連的最大數量:
s.listen(5)print ‘Waiting for connection...‘
接下來,伺服器程式通過一個永久迴圈來接受來自用戶端的串連,accept()會等待並返回一個用戶端串連:
while TAG: # 接受一個新串連 conn,addr = s.accept() # 建立一個新線程處理TCP串連 t = threading.Thread(target=tcplink,args=(conn,addr)) t.start()
每個串連都必須建立新線程(或進程)來處理,否則,單線程在處理串連的過程中,無法接受其他用戶端的串連:
def tcplink(conn,addr): print (‘Accept new connection form {0}‘.format(addr)) conn.send(‘Welcome!‘) while True: data = conn.recv(1024) time.sleep(1) if data == ‘exit‘ or not data: break socket.send(‘Hello,{0}!‘.format(data)) conn.close() print (‘Connection from {0} closed.‘.format(addr))
串連建立後,伺服器首先發一條歡迎訊息,然後等待用戶端資料,並加上Hello再發送給用戶端。如果用戶端發送了exit字串,就直接關閉串連
要測試這個伺服器程式,我們還需要編寫一個用戶端程式:
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()
然後我們開啟兩個命令列視窗,一個運行伺服器程式,另一個運行用戶端程式,就可以看到效果。
需要注意的是,用戶端程式運行完畢就退出了,而伺服器程式會永遠運行下去,必須按Ctrl+C退出程式。
用TCP協議進行Socket編程在Python中十分簡單,對於用戶端,要主動串連伺服器的IP和指定連接埠,對於伺服器,要首先監聽連接埠,然後,對每一個新的串連,建立一個線程或進程來處理。通常,伺服器程式會無限運行下去。
同一個連接埠,被一個Socket綁定了以後,就不能被別的Socket綁定了。
從零開始學Python第八周:網路編程基礎(socket)