1. 程式人生 > >Python Socket 網路程式設計

Python Socket 網路程式設計

Socket 是程序間通訊的一種方式,它與其他程序間通訊的一個主要不同是:它能實現不同主機間的程序間通訊,我們網路上各種各樣的服務大多都是基於 Socket 來完成通訊的,例如我們每天瀏覽網頁、QQ 聊天、收發 email 等等。要解決網路上兩臺主機之間的程序通訊問題,首先要唯一標識該程序,在 TCP/IP 網路協議中,就是通過 (IP地址,協議,埠號) 三元組來標識程序的,解決了程序標識問題,就有了通訊的基礎了。

本文主要介紹使用 Python 進行 TCP Socket 網路程式設計,假設你已經具有初步的網路知識及 Python 基本語法知識。

TCP 是一種面向連線的傳輸層協議,TCP Socket 是基於一種 Client-Server 的程式設計模型,服務端監聽客戶端的連線請求,一旦建立連線即可以進行傳輸資料。那麼對 TCP Socket 程式設計的介紹也分為客戶端和服務端:

客戶端程式設計

建立 socket

首先要建立 socket,用 Python 中 socket 模組的函式 socket 就可以完成:

Python
12345678 #Socket client example in pythonimportsocket#for sockets#create an AF_INET, STREAM socket (TCP)s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)print'Socket Created'

函式 socket.socket 建立一個 socket,返回該 socket 的描述符,將在後面相關函式中使用。該函式帶有兩個引數:

  • Address Family:可以選擇 AF_INET(用於 Internet 程序間通訊) 或者 AF_UNIX
    (用於同一臺機器程序間通訊)
  • Type:套接字型別,可以是 SOCKET_STREAM(流式套接字,主要用於 TCP 協議)或者 SOCKET_DGRAM(資料報套接字,主要用於 UDP 協議)

注:由於本文主要概述一下 Python Socket 程式設計的過程,因此不會對相關函式引數、返回值進行詳細介紹,需要了解的可以檢視相關手冊

錯誤處理

如果建立 socket 函式失敗,會丟擲一個 socket.error 的異常,需要捕獲:

Python
12345678910111213 #handling errors in python socket programsimportsocket#for socketsimportsys#for exittry:#create an AF_INET, STREAM socket (TCP)s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)exceptsocket.error,msg:print'Failed to create socket. Error code: '+str(msg[0])+' , Error message : '+msg[1]sys.exit();print'Socket Created'

那麼到目前為止已成功建立了 socket,接下來我們將用這個 socket 來連線某個伺服器,就連 www.google.com 吧。

連線伺服器

本文開始也提到了,socket 使用 (IP地址,協議,埠號) 來標識一個程序,那麼我們要想和伺服器進行通訊,就需要知道它的 IP地址以及埠號。

獲得遠端主機的 IP 地址

Python 提供了一個簡單的函式 socket.gethostbyname 來獲得遠端主機的 IP 地址:

Python
123456789101112 host='www.google.com'port=80try:remote_ip=socket.gethostbyname(host)exceptsocket.gaierror:#could not resolveprint'Hostname could not be resolved. Exiting'sys.exit()print'Ip address of '+host+' is '+remote_ip

現在我們知道了伺服器的 IP 地址,就可以使用連線函式 connect 連線到該 IP 的某個特定的埠上了,下面例子連線到 80 埠上(是 HTTP 服務的預設埠):

Python
1234 #Connect to remote servers.connect((remote_ip,port))print'Socket Connected to '+host+' on ip '+remote_ip

執行該程式:

Python
1234 $python client.pySocketcreatedIp of remote host www.google.com is173.194.38.145SocketConnected to www.google.com on ip173.194.38.145

傳送資料

上面說明連線到 www.google.com 已經成功了,接下面我們可以向伺服器傳送一些資料,例如傳送字串 GET / HTTP/1.1rnrn,這是一個 HTTP 請求網頁內容的命令。

Python
123456789101112 #Send some data to remote servermessage="GET / HTTP/1.1rnrn"try:#Set the whole strings.sendall(message)exceptsocket.error:#Send failedprint'Send failed'sys.exit()print'Message send successfully'

傳送完資料之後,客戶端還需要接受伺服器的響應。

接收資料

函式 recv 可以用來接收 socket 的資料:

Python
1234 #Now receive datareply=s.recv(4096)printreply

一起執行的結果如下:

Python
12345678910111213141516171819 SocketcreatedIp of remote host www.google.com is173.194.38.145SocketConnected to www.google.com on ip173.194.38.145Message send successfullyHTTP/1.1302FoundCache-Control:privateContent-Type:text/html;charset=UTF-8Location:http://www.google.com.sg/?gfe_rd=cr&ei=PlqJVLCREovW8gfF0oG4CQContent-Length:262Date:Thu,11Dec201408:47:58GMTServer:GFE/2.0Alternate-Protocol:80:quic,p=0.02<HTML><HEAD><meta http-equiv="content-type"content="text/html;charset=utf-8"><TITLE>302Moved</TITLE></HEAD><BODY><H1>302Moved</H1>The document has moved<AHREF="http://www.google.com.sg/?gfe_rd=cr&ei=PlqJVLCREovW8gfF0oG4CQ">here</A>.</BODY></HTML>

關閉 socket

當我們不想再次請求伺服器資料時,可以將該 socket 關閉,結束這次通訊:

Python
1 s.close()

小結

上面我們學到了如何:

  1. 建立 socket
  2. 連線到遠端伺服器
  3. 傳送資料
  4. 接收資料
  5. 關閉 socket

當我們開啟 www.google.com 時,瀏覽器所做的就是這些,知道這些是非常有意義的。在 socket 中具有這種行為特徵的被稱為CLIENT,客戶端主要是連線遠端系統獲取資料。

socket 中另一種行為稱為SERVER,伺服器使用 socket 來接收連線以及提供資料,和客戶端正好相反。所以 www.google.com 是伺服器,你的瀏覽器是客戶端,或者更準確地說,www.google.com 是 HTTP 伺服器,你的瀏覽器是 HTTP 客戶端。

那麼上面介紹了客戶端的程式設計,現在輪到伺服器端如果使用 socket 了。

伺服器端程式設計

伺服器端主要做以下工作:

  • 開啟 socket
  • 繫結到特定的地址以及埠上
  • 監聽連線
  • 建立連線
  • 接收/傳送資料

上面已經介紹瞭如何建立 socket 了,下面一步是繫結。

繫結 socket

函式 bind 可以用來將 socket 繫結到特定的地址和埠上,它需要一個 sockaddr_in 結構作為引數:

Python
12345678910111213141516 importsocketimportsysHOST=''# Symbolic name meaning all available interfacesPORT=8888# Arbitrary non-privileged ports=socket.socket(socket.AF_INET,socket.SOCK_STREAM)print'Socket created'try:s.bind((HOST,PORT))exceptsocket.error,msg:print'Bind failed. Error Code : '+str(msg[0])+' Message '+msg[1]sys.exit()print'Socket bind complete'

繫結完成之後,接下來就是監聽連線了。

監聽連線

函式 listen 可以將 socket 置於監聽模式:

Python
12 s.listen(10)print'Socket now listening'

該函式帶有一個引數稱為 backlog,用來控制連線的個數。如果設為 10,那麼有 10 個連線正在等待處理,此時第 11 個請求過來時將會被拒絕。

接收連線

當有客戶端向伺服器傳送連線請求時,伺服器會接收連線:

Python
12345 #wait to accept a connection - blocking callconn,addr=s.accept()#display client informationprint'Connected with '+addr[0]+':'+str(addr[1])

執行該程式的,輸出結果如下:

Python
1234 $python server.pySocketcreatedSocketbind completeSocketnow listening

此時,該程式在 8888 埠上等待請求的到來。不要關掉這個程式,讓它一直執行,現在客戶端可以通過該埠連線到 socket。我們用 telnet 客戶端來測試,開啟一個終端,輸入 telnet localhost 8888

Python
12345 $telnet localhost8888Trying127.0.0.1...Connected to localhost.Escape character is'^]'.Connection closed by foreign host.

這時服務端輸出會顯示:

Python