轉載請注明:@小五義 http://www.cnblogs.com/xiaowuyi
服務器和客戶端程序很類似,上節學習了客戶端程序,這一節將仔細學習一下利用socket建立TCP服務器和UDP服務器。
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!'傳給客戶端,此時等待輸入一個新的信息傳給客戶端。 ##@小五義 http://www.cnblogs.com/xiaowuyi 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地址,然后輸入信息,回車后會得到服務器返回信息,然后等待服務器向其發送信息后退出。 ##@小五義 http://www.cnblogs.com/xiaowuyi 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服務器端,客戶端連接后,向其發送當前時間 ##@小五義 http://www.cnblogs.com/xiaowuyi 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客戶端,向服務器發送一個空字符后,得到服務器返回時間 ##@小五義 http://www.cnblogs.com/xiaowuyi 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