詳解Python Socket網路編程

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

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

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

一、用戶端編程
建立socket

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

#Socket client example in python import socket  #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 的異常,需要捕獲:

#handling errors in python socket programs import socket  #for socketsimport sys #for exit try:  #create an AF_INET, STREAM socket (TCP)  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)except socket.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 位址:

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

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

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

運行該程式:

$ python client.pySocket createdIp of remote host www.google.com is 173.194.38.145Socket Connected to www.google.com on ip 173.194.38.145

發送資料

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

#Send some data to remote servermessage = "GET / HTTP/1.1\r\n\r\n" try :  #Set the whole string  s.sendall(message)except socket.error:  #Send failed  print 'Send failed'  sys.exit() print 'Message send successfully'

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

接收資料

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

#Now receive datareply = s.recv(4096) print reply

一起啟動並執行結果如下:

Socket createdIp of remote host www.google.com is 173.194.38.145Socket Connected to www.google.com on ip 173.194.38.145Message send successfullyHTTP/1.1 302 FoundCache-Control: privateContent-Type: text/html; charset=UTF-8Location: http://www.google.com.sg/?gfe_rd=cr&ei=PlqJVLCREovW8gfF0oG4CQContent-Length: 262Date: Thu, 11 Dec 2014 08:47:58 GMTServer: GFE/2.0Alternate-Protocol: 80:quic,p=0.02302 Moved

302 Moved

The document has movedhere.

關閉 socket

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

s.close()
小結

上面我們學到了如何:

  • 建立 socket
  • 串連到遠程伺服器
  • 發送資料
  • 接收資料
  • 關閉 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 結構作為參數:

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

綁定完成之後,接下來就是監聽串連了。

監聽串連

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

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

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

接收串連

當有用戶端向伺服器發送串連請求時,伺服器會接收串連:

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

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

$ python server.pySocket createdSocket bind completeSocket now listening

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

$ telnet localhost 8888Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.Connection closed by foreign host.

這時服務端輸出會顯示:

$ python server.pySocket createdSocket bind completeSocket now listeningConnected with 127.0.0.1:59954

我們觀察到用戶端已經串連上伺服器了。在建立串連之後,我們可以用來與用戶端進行通訊。下面例子示範的是,伺服器建立串連之後,接收用戶端發送來的資料,並立即將資料發送回去,下面是完整的服務端程式:

import socketimport sys HOST = ''  # Symbolic name meaning all available interfacesPORT = 8888 # Arbitrary non-privileged port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)print 'Socket created' try:  s.bind((HOST, PORT))except socket.error , msg:  print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]  sys.exit()   print 'Socket bind complete' s.listen(10)print 'Socket now listening' #wait to accept a connection - blocking callconn, addr = s.accept() print 'Connected with ' + addr[0] + ':' + str(addr[1]) #now keep talking with the clientdata = conn.recv(1024)conn.sendall(data) conn.close()s.close()

在一個終端中運行這個程式,開啟另一個終端,使用 telnet 串連伺服器,隨便輸入字串,你會看到:

$ telnet localhost 8888Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.happyhappyConnection closed by foreign host.

用戶端(telnet)接收了伺服器的響應。

我們在完成一次響應之後伺服器立即斷開了串連,而像www.google.com 這樣的伺服器總是一直等待接收串連的。我們需要將上面的伺服器程式改造成一直運行,最簡單的辦法是將accept 放到一個迴圈中,那麼就可以一直接收串連了。

保持服務

我們可以將代碼改成這樣讓伺服器一直工作:

import socketimport sys HOST = ''  # Symbolic name meaning all available interfacesPORT = 5000 # Arbitrary non-privileged port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)print 'Socket created' try:  s.bind((HOST, PORT))except socket.error , msg:  print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]  sys.exit()   print 'Socket bind complete' s.listen(10)print 'Socket now listening' #now keep talking with the clientwhile 1:  #wait to accept a connection - blocking call  conn, addr = s.accept()  print 'Connected with ' + addr[0] + ':' + str(addr[1])     data = conn.recv(1024)  reply = 'OK...' + data  if not data:     break     conn.sendall(reply) conn.close()s.close()

現在在一個終端下運行上面的伺服器程式,再開啟三個終端,分別用 telnet 去串連,如果一個終端串連之後不輸入資料其他終端是沒辦法進行串連的,而且每個終端只能服務一次就中斷連線。這從代碼上也是可以看出來的。

這顯然也不是我們想要的,我們希望多個用戶端可以隨時建立串連,而且每個用戶端可以跟伺服器進行多次通訊,這該怎麼修改呢?

處理串連

為了處理每個串連,我們需要將處理的程式與主程式的接收串連分開。一種方法可以使用線程來實現,主服務程式接收串連,建立一個線程來處理該串連的通訊,然後伺服器回到接收其他串連的邏輯上來。

import socketimport sysfrom thread import * HOST = ''  # Symbolic name meaning all available interfacesPORT = 8888 # Arbitrary non-privileged port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)print 'Socket created' #Bind socket to local host and porttry:  s.bind((HOST, PORT))except socket.error , msg:  print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]  sys.exit()   print 'Socket bind complete' #Start listening on sockets.listen(10)print 'Socket now listening' #Function for handling connections. This will be used to create threadsdef clientthread(conn):  #Sending message to connected client  conn.send('Welcome to the server. Type something and hit enter\n') #send only takes string     #infinite loop so that function do not terminate and thread do not end.  while True:         #Receiving from client    data = conn.recv(1024)    reply = 'OK...' + data    if not data:       break       conn.sendall(reply)     #came out of loop  conn.close() #now keep talking with the clientwhile 1:  #wait to accept a connection - blocking call  conn, addr = s.accept()  print 'Connected with ' + addr[0] + ':' + str(addr[1])     #start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function.  start_new_thread(clientthread ,(conn,)) s.close()

再次運行上面的程式,開啟三個終端來與主伺服器建立 telnet 串連,這時候三個用戶端可以隨時接入,而且每個用戶端可以與主伺服器進行多次通訊。

telnet 終端下可能輸出如下:

$ telnet localhost 8888Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.Welcome to the server. Type something and hit enterhiOK...hiasdOK...asdcvOK...cv

要結束 telnet 的串連,按下 Ctrl-] 鍵,再輸入 close 命令。

伺服器終端的輸出可能是這樣的:

$ python server.pySocket createdSocket bind completeSocket now listeningConnected with 127.0.0.1:60730Connected with 127.0.0.1:60731

到目前為止,我們學習了Python 下基本的socket 編程,之後還有相關文章向大家介紹,不要走開。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.