python網路程式設計之setsockopt
1、TCP連線的建立方法
客戶端在建立一個TCP連線時一般需要兩步,而伺服器的這個過程需要四步,具體見下面的比較。
步驟 | TCP客戶端 | TCP伺服器 |
第一步 | 建立socket物件 | 建立socket物件 |
第二步 | 呼叫connect()建立一個和伺服器的連線 | 設定socket選項(可選) |
第三步 | 無 | 繫結到一個埠(也可以是一個指定的網絡卡) |
第四步 | 無 | 偵聽連線 |
下面具體來講這四步的建立方法:
第一步,建立socket物件:這裡與客戶端一樣,依然是:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
第二步,設定和得到socket選項
python定義了setsockopt()和getsockopt(),一個是設定選項,一個是得到設定。這裡主要使用setsockopt(),具體結構如下:
setsockopt(level,optname,value)
level定義了哪個選項將被使用。通常情況下是SOL_SOCKET,意思是正在使用的socket選項。它還可以通過設定一個特殊協議號碼來設定協議選項,然而對於一個給定的作業系統,大多數協議選項都是明確的,所以為了簡便,它們很少用於為移動裝置設計的應用程式。
optname引數提供使用的特殊選項。關於可用選項的設定,會因為作業系統的不同而有少許不同。如果level選定了SOL_SOCKET,那麼一些常用的選項見下表:
選項 |
意義 |
期望值 |
SO_BINDTODEVICE |
可以使socket只在某個特殊的網路介面(網絡卡)有效。也許不能是移動便攜裝置 |
一個字串給出裝置的名稱或者一個空字串返回預設值 |
SO_BROADCAST |
允許廣播地址傳送和接收資訊包。只對UDP有效。如何傳送和接收廣播資訊包 |
布林型整數 |
SO_DONTROUTE |
禁止通過路由器和閘道器往外發送資訊包。這主要是為了安全而用在乙太網上UDP通訊的一種方法。不管目的地址使用什麼IP地址,都可以防止資料離開本地網路 |
布林型整數 |
SO_KEEPALIVE |
可以使TCP通訊的資訊包保持連續性。這些資訊包可以在沒有資訊傳輸的時候,使通訊的雙方確定連線是保持的 |
布林型整數 |
SO_OOBINLINE |
可以把收到的不正常資料看成是正常的資料,也就是說會通過一個標準的對recv()的呼叫來接收這些資料 |
布林型整數 |
SO_REUSEADDR |
當socket關閉後,本地端用於該socket的埠號立刻就可以被重用。通常來說,只有經過系統定義一段時間後,才能被重用。 |
布林型整數 |
本節在學習時,用到了SO_REUSEADDR選項,具體寫法是:
S.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 這裡value設定為1,表示將SO_REUSEADDR標記為TRUE,作業系統會在伺服器socket被關閉或伺服器程序終止後馬上釋放該伺服器的埠,否則作業系統會保留幾分鐘該埠。
下面的方法可以幫助給出該系統下python所支援的socket選項列表:
程式碼如下:
import socket
solist=[x for x in dir(socket) if x.startswith('SO_')]
solist.sort()
for x in solist:
Print x
第三步:繫結socket
繫結即為伺服器要求一個埠號。
S.bind((host,port)),其中host為伺服器ip,通常為空,也可以繫結到一個特定的ip地址。Port為埠號。
第四步:偵聽連線。
利用listen()函式進行偵聽連線。該函式只有一個引數,其指明瞭在伺服器實際處理連線的時候,允許有多少個未決(等待)的連線在佇列中等待。作為一個約定,很多人設定為5。如:s.listen(5)
2、簡單的TCP伺服器例項
這個建立一個簡單的TCP伺服器和客戶端。
伺服器端:TCP響應伺服器,當與客戶端建立連線後,伺服器顯示客戶端ip和埠,同時將接收的客戶端資訊和'I get it!'傳給客戶端,此時等待輸入一個新的資訊傳給客戶端。
客戶端:TCP客戶端,首先輸入伺服器ip地址,然後輸入資訊,回車後會得到伺服器返回資訊,然後等待伺服器向其傳送資訊後退出。
具體程式碼如下:
伺服器端:tcpserver.py
程式碼如下:
# -*- coding: cp936 -*-
##tcp響應伺服器,當與客戶端建立連線後,伺服器顯示客戶端ip和埠,同時將接收的客戶端資訊和'I get it!'傳給客戶端,此時等待輸入一個新的資訊傳給客戶端。
##@小五義
import socket,traceback
host=''
port=12345
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((host,port))
s.listen(1)
while 1:
try:
clientsock,clientaddr=s.accept()
except KeyboardInterrupt:
raise
except:
traceback.print_exc()
continue
try:
print "連線來自:",clientsock.getpeername()
while 1:
data=clientsock.recv(4096)
if not len(data):
break
print clientsock.getpeername()[0]+':'+str(data)
clientsock.sendall(data)
clientsock.sendall("\nI get it!\n")
t=raw_input('input the word:')
clientsock.sendall(t)
except (KeyboardInterrupt,SystemExit):
raise
except:
traceback.print_exc()
try:
clientsock.close()
except KeyboardInterrupt:
raise
except:
traceback.print_exc()
客戶端:tcpclient.py
程式碼如下:
# -*- coding: cp936 -*-
##tcp客戶端,首先輸入伺服器ip地址,然後輸入資訊,回車後會得到伺服器返回資訊,然後等待伺服器向其傳送資訊後退出。
##@小五義
import socket,sys
port=12345
host=raw_input('輸入伺服器ip:')
data=raw_input('輸入要傳送的資訊:')
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
s.connect((host,port))
except:
print '連線錯誤!'
s.send(data)
s.shutdown(1)
print '傳送完成。'
while 1:
buf=s.recv(4096)
if not len(buf):
break
sys.stdout.write(buf)
執行結果:
客戶端輸入hello,伺服器端輸入ok,具體顯示結果是:
伺服器端:
連線來自:('127.0.0.1',1945)
127.0.0.1:hello
Input the world:ok
客戶端:
輸入伺服器ip:127.0.0.1
輸入要傳送的資訊:hello
傳送完成。
hello
I get it!
ok
3、UDP伺服器
UDP伺服器建立與TCP相類似,具體比較如下:
步驟 |
UDP |
TCP |
第一步 |
建立socket物件 |
建立socket物件 |
第二步 |
設定socket選項 |
設定socket選項 |
第三步 |
繫結到一個埠 |
繫結到一個埠 |
第四步 |
Recvfrom() |
偵聽連線listen |
這裡利用UDP建立一個時間伺服器。
程式碼如下:
伺服器端;serverudp.py
程式碼如下:
# -*- coding: cp936 -*-
##UDP伺服器端,客戶端連線後,向其傳送當前時間
##@小五義
import socket,traceback,time,struct
host=''
port=12345
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((host,port))
while 1:
try:
message,address=s.recvfrom(8192)
secs=int(time.time())
reply=struct.pack("!I",secs)
s.sendto(reply,address)
except (KeyboardInterrupt,SystemExit):
raise
except:
traceback.print_exc()
客戶端:clientudp.py
程式碼如下:
# -*- coding: cp936 -*-
##udp客戶端,向伺服器傳送一個空字元後,得到伺服器返回時間
##@小五義
import socket,sys,struct,time
host=raw_input('輸入伺服器地址:')
port=12345
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.sendto('',(host,port))
print "等待回覆……"
buf=s.recvfrom(2048)[0]
if len(buf)!=4:
print "回覆錯誤%d:%s"%(len(buf),buf)
sys.exit(1)
secs=struct.unpack("!I",buf)[0]
print time.ctime(int(secs))
執行結果:
首先執行伺服器端,然後執行客戶端。
C:\>python clientudp.py ##clientudp.py程式存放在在c盤下
輸入伺服器地址:127.0.0.1
等待回覆……
Mon Aug 06 17:09:17 2012