socket簡介
1.本地的進程間通信(IPC)有很多種方式,例如
- 隊列
- 同步(互斥鎖、條件變量等)
以上通信方式都是在一台機器上不同進程之間的通信方式,那么問題來了
網絡中進程之間如何通信?
2. 網絡中進程之間如何通信
首要解決的問題是如何唯一標識一個進程,否則通信無從談起!
在本地可以通過進程PID來唯一標識一個進程,但是在網絡中這是行不通的。
其實TCP/IP協議族已經幫我們解決了這個問題,網絡層的“ip地址”可以唯一標識網絡中的主機,而傳輸層的“協議+端口”可以唯一標識主機中的應用程序(進程)。
這樣利用ip地址,協議,端口
就可以標識網絡的進程了,網絡中的進程通信就可以利用這個標志與其它進程進行交互
3. 什么是socket
socket(簡稱 套接字
) 是進程間通信的一種方式,它與其他進程間通信的一個主要不同是:
它能實現不同主機間的進程間通信,我們網絡上各種各樣的服務大多都是基於 Socket 來完成通信的
例如我們每天瀏覽網頁、QQ 聊天、收發 email 等等
4. 創建socket
在 Python 中 使用socket 模塊的函數 socket 就可以完成:
socket.socket(AddressFamily, Type)
說明:
函數 socket.socket 創建一個 socket,返回該 socket 的描述符,該函數帶有兩個參數:
- Address Family:可以選擇 AF_INET(用於 Internet 進程間通信) 或者 AF_UNIX(用於同一台機器進程間通信),實際工作中常用AF_INET
- Type:套接字類型,可以是 SOCK_STREAM(流式套接字,主要用於 TCP 協議)或者 SOCK_DGRAM(數據報套接字,主要用於 UDP 協議)
創建一個tcp socket(tcp套接字)
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print 'Socket Created'
創建一個udp socket(udp套接字)
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) print 'Socket Created'
UDP介紹
UDP ---用戶數據報協議,是一個無連接的簡單的面向數據報的運輸層協議。UDP不提供可靠性,它只是把應用程序傳給IP層的數據報發送出去,但是並不能保證它們能到達目的地。由於UDP在傳輸數據報前不用在客戶和服務器之間建立一個連接,且沒有超時重發等機制,故而傳輸速度很快。
UDP是一種面向無連接的協議,每個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網絡上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。
UDP特點:
UDP是面向無連接的通訊協議,UDP數據包括目的端口號和源端口號信息,由於通訊不需要連接,所以可以實現廣播發送。 UDP傳輸數據時有大小限制,每個被傳輸的數據報必須限定在64KB之內。 UDP是一個不可靠的協議,發送方所發送的數據報並不一定以相同的次序到達接收方。
【適用情況】
UDP是面向消息的協議,通信時不需要建立連接,數據的傳輸自然是不可靠的,UDP一般用於多點通信和實時的數據業務,比如
- 語音廣播
- 視頻
- TFTP(簡單文件傳送)
- SNMP(簡單網絡管理協議)
- RIP(路由信息協議,如報告股票市場,航空信息)
- DNS(域名解釋)
注重速度流暢
UDP操作簡單,而且僅需要較少的監護,因此通常用於局域網高可靠性的分散系統中client/server應用程序。例如視頻會議系統,並不要求音頻視頻數據絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會更合理一些。
udp網絡程序-發送數據
創建一個udp客戶端程序的流程是簡單,具體步驟如下:
- 創建客戶端套接字
- 發送/接收數據
- 關閉套接字
代碼如下:
#coding=utf-8
from socket import *
#1. 創建套接字 udpSocket = socket(AF_INET, SOCK_DGRAM) #2. 准備接收方的地址 sendAddr = ('192.168.1.103', 8080) #3. 從鍵盤獲取數據 sendData = raw_input("請輸入要發送的數據:") #4. 發送數據到指定的電腦上 udpSocket.sendto(sendData, sendAddr) #5. 關閉套接字 udpSocket.close()
運行現象:
在Ubuntu中運行腳本:
在windows中運行“網絡調試助手”:
udp網絡程序-發送、接收數據
1. 創建udp網絡程序-接收數據
#coding=utf-8
from socket import *
#1. 創建套接字 udpSocket = socket(AF_INET, SOCK_DGRAM) #2. 准備接收方的地址 sendAddr = ('192.168.1.103', 8080) #3. 從鍵盤獲取數據 sendData = raw_input("請輸入要發送的數據:") #4. 發送數據到指定的電腦上 udpSocket.sendto(sendData, sendAddr) #5. 等待接收對方發送的數據 recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字節數 #6. 顯示對方發送的數據 print(recvData) #7. 關閉套接字 udpSocket.close()
python腳本:
網絡調試助手截圖:
udp網絡程序-端口問題
會變的端口號
重新運行多次腳本,然后在“網絡調試助手”中,看到的現象如下:
說明:
- 每重新運行一次網絡程序,上圖中紅圈中的數字,不一樣的原因在於,這個數字標識這個網絡程序,當重新運行時,如果沒有確定到底用哪個,系統默認會隨機分配
- 記住一點:這個網絡程序在運行的過程中,這個就唯一標識這個程序,所以如果其他電腦上的網絡程序如果想要向此程序發送數據,那么就需要向這個數字(即端口)標識的程序發送即可
udp綁定信息
1. 綁定信息
還記得在上一節課中,如果一個網絡程序在每次運行的時候端口是隨機變化的么?
一般情況下,在一天電腦上運行的網絡程序有很多,而各自用的端口號很多情況下不知道,為了不與其他的網絡程序占用同一個端口號,往往在編程中,udp的端口號一般不綁定
但是如果需要做成一個服務器端的程序的話,是需要綁定的,想想看這又是為什么呢?
如果報警電話每天都在變,想必世界就會亂了,所以一般服務性的程序,往往需要一個固定的端口號,這就是所謂的端口綁定
2. 綁定示例
#coding=utf-8
from socket import *
#1. 創建套接字 udpSocket = socket(AF_INET, SOCK_DGRAM) #2. 綁定本地的相關信息,如果一個網絡程序不綁定,則系統會隨機分配 bindAddr = ('', 7788) # ip地址和端口號,ip一般不用寫,表示本機的任何一個ip udpSocket.bind(bindAddr) #3. 等待接收對方發送的數據 recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字節數 #4. 顯示接收到的數據 print recvData #5. 關閉套接字 udpSocket.close()
運行結果:
測試端
本程序
3. 總結
- 一個udp網絡程序,可以不綁定,此時操作系統會隨機進行分配一個端口,如果重新運行次程序端口可能會發生變化
- 一個udp網絡程序,也可以綁定信息(ip地址,端口號),如果綁定成功,那么操作系統用這個端口號來進行區別收到的網絡數據是否是此進程的
udp網絡通信過程
udp應用:echo服務器
1. 運行現象
測試端
echo服務器端
2. 參考代碼
#coding=utf-8
from socket import *
#1. 創建套接字 udpSocket = socket(AF_INET, SOCK_DGRAM) #2. 綁定本地的相關信息 bindAddr = ('', 7788) # ip地址和端口號,ip一般不用寫,表示本機的任何一個ip udpSocket.bind(bindAddr) num = 1 while True: #3. 等待接收對方發送的數據 recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字節數 #4. 將接收到的數據再發送給對方 udpSocket.sendto(recvData[0], recvData[1]) #5. 統計信息 print('已經將接收到的第%d個數據返回給對方,內容為:%s'%(num,recvData[0])) num+=1 #5. 關閉套接字 udpSocket.close()
udp應用:聊天室
1. 運行現象
測試端
聊天室端
2. 參考代碼
#coding=utf-8
from socket import *
from time import ctime
#1. 創建套接字 udpSocket = socket(AF_INET, SOCK_DGRAM) #2. 綁定本地的相關信息 bindAddr = ('', 7788) # ip地址和端口號,ip一般不用寫,表示本機的任何一個ip udpSocket.bind(bindAddr) while True: #3. 等待接收對方發送的數據 recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字節數 #4. 打印信息 print('【%s】%s:%s'%(ctime(),recvData[1][0],recvData[0])) #5. 關閉套接字 udpSocket.close()
udp總結
1. udp是TCP/IP協議族中的一種協議能夠完成不同機器上的程序間的數據通信
2. udp服務器、客戶端
- udp的服務器和客戶端的區分:往往是通過
請求服務
和提供服務
來進行區分 - 請求服務的一方稱為:客戶端
- 提供服務的一方稱為:服務器
3. udp綁定問題
- 一般情況下,服務器端,需要綁定端口,目的是為了讓其他的客戶端能夠正確發送到此進程
- 客戶端,一般不需要綁定,而是讓操作系統隨機分配,這樣就不會因為需要綁定的端口被占用而導致程序無法運行的情況