日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python16进制字节序_第 1 章 套接字、IPv4和简单的客户端/服务器编程

發布時間:2025/4/16 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python16进制字节序_第 1 章 套接字、IPv4和简单的客户端/服务器编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第 1 章 套接字、IPv4和簡單的客戶端/服務器編程

本章攻略:

打印設備名和IPv4地址

獲取遠程設備的IP地址

將IPv4地址轉換成不同的格式

通過指定的端口和協議找到服務名

主機字節序和網絡字節序之間相互轉換

設定并獲取默認的套接字超時時間

優雅地處理套接字錯誤

修改套接字發送和接收的緩沖區大小

把套接字改成阻塞或非阻塞模式

重用套接字地址

從網絡時間服務器上獲取并打印當前時間

編寫一個SNTP客戶端

編寫一個簡單的回顯客戶端/服務器應用

1.1 簡介

本章通過一些簡單的攻略介紹Python的核心網絡庫。Python的socket模塊提供了類方法和實例方法,二者的區別在于使用類方法時不需要創建套接字對象實例。這是一種很直觀的方法。例如,打印設備的IP地址不需要創建套接字對象,而只需調用套接字的類方法。但是,如果要把數據發送給服務器程序,那么創建一個套接字對象來處理具體的操作則更加自然。本章介紹的攻略可以分成如下三類:

前幾個攻略使用類方法獲取關于主機、網絡以及目標服務的有用信息;

隨后的幾個攻略使用實例方法,演示了常用的套接字操作,例如處理套接字超時、緩沖區大小和阻塞模式等;

最后,結合使用類方法和實例方法開發客戶端,執行一些實際的任務,例如使設備時間與網絡服務器同步,編寫通用的客戶端/服務器腳本。

你可以使用本章演示的方法編寫自己的客戶端/服務器應用。

1.2 打印設備名和IPv4地址

有時,你需要快速查看設備的某些信息,例如主機名、IP地址和網絡接口的數量等。這些信息使用Python腳本很容易獲取。

1.2.1 準備工作

編寫代碼之前先要在設備上安裝Python。大多數Linux發行版都預裝了Python。如果使用微軟Windows操作系統,可以從Python的網站上下載二進制文件:http://www.python.org/download/。

要了解系統是否已經安裝了Python,可以查閱操作系統的文檔。在設備上安裝好Python之后,可以在命令行中輸入python,嘗試打開Python解釋器。輸入python后應該顯示解釋器提示符>>>,具體的輸出如下所示:

~$ python

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24)

[GCC 4.5.2] on linux2

Type "help", "copyright", "credits" or "license" for more information. >>>

1.2.2 實戰演練

這個攻略很簡短,可以直接寫在Python解釋器中。

首先,使用下面的命令導入Python中的socket庫:

>>> import socket

然后,調用socket庫提供的gethostname()方法,把結果保存在一個變量中,如下所示:

>>> host_name = socket.gethostname()

>>> print "Host name: %s" %host_name

Host name: debian6

>>> print "IP address: %s" %socket.gethostbyname(host_name)

IP address: 127.0.1.1

這些操作可以使用內置的類方法,定義成一個獨立的函數print_machine_info()。

我們要在常用的__main__代碼塊中調用這個函數。運行時,Python會為某些內部變量賦值, 例如__name__。在這里,__name__表示調用程序的進程名。如果在命令行中運行腳本(如后面的命令所示),__name__的值是__main__。但是,如果在其他腳本中導入,情況就不同了。也就是說,如果在命令行中調用這個模塊,會自動運行print_machine_info()函數;如果在其他腳本中導入,用戶就要手動調用這個函數。

代碼清單1-1展示了如何獲取設備的信息,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter -1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import socket

def print_machine_info():

host_name = socket.gethostname()

ip_address = socket.gethostbyname(host_name)

print "Host name: %s" % host_name

print "IP address: %s" % ip_address

if __name__ == '__main__':

print_machine_info()

若想運行這個腳本,要在命令行中指定源碼文件,如下所示:

$ python 1_1_local_machine_info.py

在我的設備上,顯示了如下輸出:

Host name: debian6

IP address: 127.0.0.1

在你的設備上,輸出的內容根據系統的主機配置會有所不同。

1.2.3 原理分析

import socket語句導入Python提供的一個核心網絡庫。然后調用兩個工具函數:gethostname()和gethostbyname(host_name)。在命令行中可以輸入help(socket.gethostname)查看幫助信息,或者在瀏覽器中訪問http://docs. python.org/3/library/socket.html。在命令行中查看這兩個函數的幫助信息,得到的輸出如下:

gethostname(...)

gethostname() -> string

Return the current host name.

gethostbyname(...)

gethostbyname(host) -> address

Return the IP address (a string of the form '255.255.255.255') for a host.

第一個函數沒有參數,返回所在主機或本地主機的名字。第二個函數接收一個參數hostname,返回對應的IP地址。

1.3 獲取遠程設備的IP地址

有時需要把設備的主機名轉換成對應的IP地址,例如快速查詢域名。本攻略介紹一個簡單的函數來完成這一操作。

1.3.1 實戰演練

如果想知道遠程設備的IP地址,可以使用內置的庫函數gethostbyname(),其參數是遠程設備的主機名。

這里,我們要調用的是類函數gethostbyname()。讓我們來看一下這個簡短的代碼片段。

代碼清單1-2展示了如何獲取遠程設備的IP地址,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

def get_remote_machine_info():

remote_host = 'www.python.org'

try:

print "IP address: %s" %socket.gethostbyname(remote_host)

except socket.error, err_msg:

print "%s: %s" %(remote_host, err_msg)

if __name__ == '__main__':

get_remote_machine_info()

運行上述代碼會得到以下輸出:

$ python 1_2_remote_machine_info.py

IP address of www.python.org: 82.94.164.162

1.3.2 原理分析

這個攻略把gethostbyname()方法包裝在用戶定義的get_remote_machine_info()函數中,還引入了異常處理的概念。如上述代碼所示,我們把主要的函數調用放在try-except塊中,這就意味著,如果執行函數gethostbyname()的過程中發生了錯誤,這個錯誤將由try-except塊處理。

假如我們修改remote_host參數的值,把www.python.org改成一個不存在的域名,例如www.pytgo.org,然后執行下述命令:

$ python 1_2_remote_machine_info.py

www.pytgo.org: [Errno -5] No address associated with hostname

try-except塊捕獲了錯誤,并向用戶顯示了一個錯誤消息,說明域名www.pytgo.org沒有對應的IP地址。

1.4 將IPv4地址轉換成不同的格式

如果要使用低層網絡函數,有時普通的字符串形式的IP地址并不是很有用,需要把它們轉換成打包后的32位二進制格式。

1.4.1 實戰演練

Python的socket庫提供了很多用來處理不同IP地址格式的函數,這里我們使用其中的兩個:inet_aton()和inet_ntoa()。

我們來定義convert_ip4_address()函數,調用inet_aton()和inet_ntoa()轉換IP地址。我們要使用兩個示例IP地址:127.0.0.1和192.168.0.1。

代碼清單1-3展示了如何定義convert_ip4_address()函數,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

from binascii import hexlify

def convert_ip4_address():

for ip_addr in ['127.0.0.1', '192.168.0.1']:

packed_ip_addr = socket.inet_aton(ip_addr)

unpacked_ip_addr = socket.inet_ntoa(packed_ip_addr)

print "IP Address: %s => Packed: %s, Unpacked: %s"\

%(ip_addr, hexlify(packed_ip_addr), unpacked_ip_addr)

if __name__ == '__main__':

convert_ip4_address()

現在,運行這個攻略,會看到以下輸出:

$ python 1_3_ip4_address_conversion.py

IP Address: 127.0.0.1 => Packed: 7f000001, Unpacked: 127.0.0.1

IP Address: 192.168.0.1 => Packed: c0a80001, Unpacked: 192.168.0.1

1.4.2 原理分析

在這個攻略中,使用for-in語句把兩個字符串形式的IP地址轉換成打包后的32位二進制格式,而且還調用了binascii模塊中的hexlify函數,以十六進制形式表示二進制數據。

1.5 通過指定的端口和協議找到服務名

如果想找到網絡服務,最好知道該服務運行在TCP或UDP協議的哪個端口上。

1.5.1 準備工作

如果知道網絡服務使用的端口,可以調用socket庫中的getservbyport()函數來獲取服務的名字。調用這個函數時可以根據情況決定是否提供協議名。

1.5.2 實戰演練

我們來定義find_service_name()函數,在Python的for-in循環中調用函數getservbyport(),解析幾個端口,例如80和25。

代碼清單1-4展示了如何定義find_service_name()函數,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

def find_service_name():

protocolname = 'tcp'

for port in [80, 25]:

print "Port: %s => service name: %s" %(port, socket.getservbyport(port, protocolname))

print "Port: %s => service name: %s" %(53, socket.getservbyport(53, 'udp'))

if __name__ == '__main__':

find_service_name()

運行這個腳本,會看到如下輸出:

$ python 1_4_finding_service_name.py

Port: 80 => service name: http

Port: 25 => service name: smtp

Port: 53 => service name: domain

1.5.3 原理分析

在這個攻略中,使用for-in語句遍歷一組變量。在每次遍歷中,獲取端口對應的服務名。

1.6 主機字節序和網絡字節序之間相互轉換

編寫低層網絡應用時,或許需要處理通過電纜在兩臺設備之間傳送的低層數據。在這種操作中,需要把主機操作系統發出的數據轉換成網絡格式,或者做逆向轉換,因為這兩種數據的表示方式不一樣。

1.6.1 實戰演練

Python的socket庫提供了將數據在網絡字節序和主機字節序之間相互轉換的函數。你可能想了解這些函數,例如ntohl()和htonl()。

我們來定義convert_integer()函數,調用ntohl()和htonl()類函數來轉換不同格式的數據。

代碼清單1-5展示了如何定義convert_integer()函數,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter -

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

def convert_integer():

data = 1234

# 32-bit

print "Original: %s => Long host byte order: %s, Network byte order: %s"\

%(data, socket.ntohl(data), socket.htonl(data))

# 16-bit

print "Original: %s => Short host byte order: %s, Network byte order: %s"\

%(data, socket.ntohs(data), socket.htons(data))

if __name__ == '__main__':

convert_integer()

運行這個攻略,會看到以下輸出:

$ python 1_5_integer_conversion.py

Original: 1234 => Long host byte order: 3523477504, Network byte order: 3523477504

Original: 1234 => Short host byte order: 53764, Network byte order: 53764

1.6.2 原理分析

在這個攻略中,我們以整數為例,演示了如何把它轉換成網絡字節序和主機字節序。socket庫中的類函數ntohl()把網絡字節序轉換成了長整形主機字節序。函數名中的n表示網絡;h表示主機;l表示長整形;s表示短整形,即16位。

1.7 設定并獲取默認的套接字超時時間

有時,你需要處理socket庫某些屬性的默認值,例如套接字超時時間。

1.7.1 實戰演練

你可以創建一個套接字對象實例,調用gettimeout()方法獲取默認的超時時間,調用settimeout()方法設定一個超時時間。這種操作在開發服務器應用時很有用。

在test_socket_timeout()函數中,首先創建一個套接字對象,然后使用讀取或者設定實例方法處理超時時間。

代碼清單1-6展示了如何定義test_socket_timeout()函數,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications

import socket

def test_socket_timeout():

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print "Default socket timeout: %s" %s.gettimeout()

s.settimeout(100)

print "Current socket timeout: %s" %s.gettimeout()

if __name__ == '__main__':

test_socket_timeout()

運行上述代碼后,你會看到它是如何修改默認超時時間的,如下所示:

$ python 1_6_socket_timeout.py

Default socket timeout: None

Current socket timeout: 100.0

1.7.2 原理分析

在這段代碼片段中,首先創建了一個套接字對象。套接字構造方法的第一個參數是地址族,第二個參數是套接字類型。然后,調用gettimeout()方法獲取套接字超時時間,再調用settimeout()方法修改超時時間。傳給settimeout()方法的參數可以是秒數(非負浮點數)也可以是None。這個方法在處理阻塞式套接字操作時使用。如果把超時時間設為None,則禁用了套接字操作的超時檢測。

1.8 優雅地處理套接字錯誤

在網絡應用中,經常會遇到這種情況:一方嘗試連接,但另一方由于網絡媒介失效或者其他原因無法響應。Python的socket庫提供了一個方法,能通過socket.error異常優雅地處理套接字錯誤。在這個攻略中會舉幾個例子。

1.8.1 實戰演練

我們來編寫幾個try-except代碼塊,每個塊對應一種可能發生的錯誤。為了獲取用戶輸入,可以使用argparse模塊。這個模塊的功能很強大,而不僅是可以使用sys.argv解析命令行參數。這些try-except代碼塊分別演示了常見的套接字操作,例如創建套接字對象、連接服務器、發送數據和等待應答。

下述攻略使用幾行代碼演示了如何處理異常。

代碼清單1-7展示了如何處理socket.error異常,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import sys

import socket

import argparse

def main():

# setup argument parsing

parser = argparse.ArgumentParser(description='Socket Error Examples')

parser.add_argument('--host', action="store", dest="host", required=False)

parser.add_argument('--port', action="store", dest="port", type=int, required=False)

parser.add_argument('--file', action="store", dest="file", required=False)

given_args = parser.parse_args()

host = given_args.host

port = given_args.port

filename = given_args.file

# First try-except block -- create socket

try:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

except socket.error, e:

print "Error creating socket: %s" % e

sys.exit(1)

# Second try-except block -- connect to given host/port

try:

s.connect((host, port))

except socket.gaierror, e:

print "Address-related error connecting to server: %s" % e

sys.exit(1)

except socket.error, e:

print "Connection error: %s" % e

sys.exit(1)

# Third try-except block -- sending data

try:

s.sendall("GET %s HTTP/1.0\r\n\r\n" % filename)

except socket.error, e:

print "Error sending data: %s" % e

sys.exit(1)

while 1:

# Fourth tr-except block -- waiting to receive data from remote host

try:

buf = s.recv(2048)

except socket.error, e:

print "Error receiving data: %s" % e

sys.exit(1)

if not len(buf):

break

# write the received data

sys.stdout.write(buf)

if __name__ == '__main__':

main()

1.8.2 原理分析

在Python中,可以使用argparse模塊把命令行參數傳入腳本以及在腳本中解析命令行參數。這個模塊在Python 2.7中可用。如果使用較舊版本的Python,這個模塊可以到“Python包索引”(Python Package Index,簡稱PyPI)中獲取,使用easy_install或pip安裝。

這個攻略用到了三個命令行參數:主機名、端口號和文件名。上述腳本的使用方法如下:

$ python 1_7_socket_errors.py –host= --port= --file=

如果提供的主機不存在,這個腳本會輸出如下錯誤:

$ python 1_7_socket_errors.py --host=www.pytgo.org --port=8080 --file=1_7_socket_errors.py

Address-related error connecting to server: [Errno -5] No address associated with hostname

如果某個端口上沒有服務,你卻嘗試連接到這個端口,則這個腳本會拋出連接超時異常,如下所示:

$ python 1_7_socket_errors.py --host=www.python.org --port=8080 --file=1_7_socket_errors.py

這個命令會返回如下錯誤,因為主機www.python.org監聽的不是端口8080:

Connection error: [Errno 110] Connection timed out

不過,如果向正確的主機、正確的端口發起隨意的請求,應用層可能無法捕獲這一異常。例如,運行下述腳本,不會返回錯誤,但輸出的HTML代碼說明了腳本的問題:

$ python 1_7_socket_errors.py --host=www.python.org --port=80 --file=1_7_socket_errors.py

HTTP/1.1 404 Not found

Server: Varnish

Retry-After: 0

content-type: text/html

Content-Length: 77

Accept-Ranges: bytes

Date: Thu, 20 Feb 2014 12:14:01 GMT

Via: 1.1 varnish

Age: 0

Connection: close

unknown domain:

這個攻略用到了四個try-except塊。除第二個塊處理socket.gaierror異常之外,其他塊都處理socket.error異常。socket.gaierror是地址相關的錯誤。除此之外還有兩種異常:socket.herror,C API中拋出的異常;如果在套接字中使用settimeout()方法,套接字超時后會拋出socket.timeout異常。

1.9 修改套接字發送和接收的緩沖區大小

很多情況下,默認的套接字緩沖區大小可能不夠用。此時,可以將默認的套接字緩沖區大小改成一個更合適的值。

1.9.1 實戰演練

我們要使用套接字對象的setsockopt()方法修改默認的套接字緩沖區大小。

首先,定義兩個常量:SEND_BUF_SIZE和RECV_BUF_SIZE。然后在一個函數中調用套接字實例的setsockopt()方法。修改之前,最好先檢查緩沖區大小是多少。注意,發送和接收的緩沖區大小要分開設定。

代碼清單1-8展示了如何修改套接字的發送和接收緩沖區大小,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications

import socket

SEND_BUF_SIZE = 4096

RECV_BUF_SIZE = 4096

def modify_buff_size():

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM )

# Get the size of the socket's send buffer

bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)

print "Buffer size [Before]:%d" %bufsize

sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)

sock.setsockopt(

socket.SOL_SOCKET,

socket.SO_SNDBUF,

SEND_BUF_SIZE)

sock.setsockopt(

socket.SOL_SOCKET,

socket.SO_RCVBUF,

RECV_BUF_SIZE)

bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)

print "Buffer size [After]:%d" %bufsize

if __name__ == '__main__':

modify_buff_size()

運行上述腳本后,會顯示修改套接字緩沖區大小前后的變化。根據你所用操作系統的本地設定,得到的輸出可能有所不同:

$ python 1_8_modify_buff_size.py

Buffer size [Before]:16384

Buffer size [After]:8192

1.9.2 原理分析

在套接字對象上可調用方法getsockopt()和setsockopt()分別獲取和修改套接字對象的屬性。setsockopt()方法接收三個參數:level、optname和value。其中,optname是選項名,value是該選項的值。第一個參數所用的符號常量(SO_*等)可在socket模塊中查看。

1.10 把套接字改成阻塞或非阻塞模式

默認情況下,TCP套接字處于阻塞模式中。也就是說,除非完成了某項操作,否則不會把控制權交還給程序。例如,調用connect() API后,連接操作會阻止程序繼續往下執行,直到連接成功為止。很多情況下,你并不想讓程序一直等待服務器響應或者有異常終止操作。例如,如果編寫了一個網頁瀏覽器客戶端連接服務器,你應該考慮提供取消功能,以便在操作過程中取消連接。這時就要把套接字設置為非阻塞模式。

1.10.1 實戰演練

我們來看一下在Python中有哪些選項。在Python中,套接字可以被設置為阻塞模式或者非阻塞模式。在非阻塞模式中,調用API后,例如send()或recv()方法,如果遇到問題就會拋出異常。但在阻塞模式中,遇到錯誤并不會阻止操作。我們可以創建一個普通的TCP套接字,分別在阻塞模式和非阻塞模式中執行操作實驗。

為了能在阻塞模式中處理套接字,首先要創建一個套接字對象。然后,調用setblocking(1)把套接字設為阻塞模式,或者調用setblocking(0)把套接字設為非阻塞模式。最后,把套接字綁定到指定的端口上,監聽進入的連接。

代碼清單1-9展示了如何把套接字設為阻塞模式或非阻塞模式,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications

import socket

def test_socket_modes():

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.setblocking(1)

s.settimeout(0.5)

s.bind(("127.0.0.1", 0))

socket_address = s.getsockname()

print "Trivial Server launched on socket: %s" %str(socket_address)

while(1):

s.listen(1)

if __name__ == '__main__':

test_socket_modes()

運行這個攻略后,會啟動一個簡易服務器,開啟阻塞模式,如下述命令所示:

$ python 1_9_socket_modes.py

Trivial Server launched on socket: ('127.0.0.1', 51410)

1.10.2 原理分析

在這個攻略中,我們把1傳給setblocking()方法,啟用套接字的阻塞模式。類似地,可以把0傳給這個方法,把套接字設為非阻塞模式。

這個功能在后面的一些攻略中會用到,到時再詳細說明其真正作用。

1.11 重用套接字地址

不管連接是被有意還是無意關閉,有時你想始終在同一個端口上運行套接字服務器。某些情況下,如果客戶端程序需要一直連接指定的服務器端口,這么做就很有用,因為無需改變服務器端口。

1.11.1 實戰演練

如果在某個端口上運行一個Python套接字服務器,連接一次之后便終止運行,就不能再使用這個端口了。如果再次連接,程序會拋出如下錯誤:

Traceback (most recent call last):

File "1_10_reuse_socket_address.py", line 40, in

reuse_socket_addr()

File "1_10_reuse_socket_address.py", line 25, in reuse_socket_addr

srv.bind( ('', local_port) )

File "", line 1, in bind

socket.error: [Errno 98] Address already in use

這個問題的解決方法是啟用套接字重用選項SO_REUSEADDR。

創建套接字對象之后,我們可以查詢地址重用的狀態,比如說舊狀態。然后,調用setsockopt()方法,修改地址重用狀態的值。再按照常規的步驟,把套接字綁定到一個地址上,監聽進入的客戶端連接。在這個例子中,我們要捕獲KeyboardInterrupt異常,這樣按下Ctrl+C鍵后,Python腳本會終止運行,但不會顯示任何異常消息。

代碼清單1-10展示了如何重用套接字地址,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications

import socket

import sys

def reuse_socket_addr():

sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )

# Get the old state of the SO_REUSEADDR option

old_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)

print "Old sock state: %s" %old_state

# Enable the SO_REUSEADDR option

sock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )

new_state = sock.getsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR )

print "New sock state: %s" %new_state

local_port = 8282

srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

srv.bind( ('', local_port) )

srv.listen(1)

print ("Listening on port: %s " %local_port)

while True:

try:

connection, addr = srv.accept()

print 'Connected by %s:%s' % (addr[0], addr[1])

except KeyboardInterrupt:

break

except socket.error, msg:

print '%s' % (msg,)

if __name__ == '__main__':

reuse_socket_addr()

這個攻略的輸出如下所示:

$ python 1_10_reuse_socket_address.py

Old sock state: 0

New sock state: 1

Listening on port: 8282

1.11.2 原理分析

你可以在一個終端窗口運行這個腳本,然后在另一個終端窗口中輸入telnet localhost 8282,嘗試連接這個服務器。關閉服務器程序后,還可以使用同一個端口再次連接。然而,如果你把設定SO_REUSEADDR的那行代碼注釋掉,服務器將不會再次運行腳本。

1.12 從網絡時間服務器獲取并打印當前時間

很多程序要求設備的時間精準,例如Unix系統中的make命令。設備上的時間可能不夠準確,需要和網絡中的時間服務器同步。

1.12.1 準備工作

你可以編寫一個Python客戶端,讓設備上的時間和某個網絡時間服務器同步。要完成這一操作,需要使用ntplib,通過“網絡時間協議”(Network Time Protocol,簡稱NTP)處理客戶端和服務器之間的通信。如果你的設備中沒有安裝ntplib,可以使用pip或easy_install從PyPI中安裝,命令如下:

pip install ntplib

1.12.2 實戰演練

我們先要創建一個NTPClient實例,然后在這個實例上調用request()方法,把NTP服務器的地址傳入方法。

代碼清單1-11展示了如何從網絡時間服務器上獲取當前時間并打印出來,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications

import ntplib

from time import ctime

def print_time():

ntp_client = ntplib.NTPClient()

response = ntp_client.request('pool.ntp.org')

print ctime(response.tx_time)

if __name__ == '__main__':

print_time()

在我的設備上,運行這個攻略后得到的輸出如下:

$ python 1_11_print_machine_time.py

Thu Mar 5 14:02:58 2012

1.12.3 原理分析

在這個攻略中,我們編寫了一個NTP客戶端,向NTP服務器pool.ntp.org發起了一個NTP請求。響應使用ctime()函數打印出來。

1.13 編寫一個SNTP客戶端

與前一個攻略不同,有時并不需要從NTP服務器上獲取精確的時間。遇到這種情況,就可以使用NTP的簡化版本,叫作“簡單網絡時間協議”。

1.13.1 實戰演練

讓我們不使用任何第三方庫編寫一個簡單的SNTP客戶端。

首先,定義兩個常量:NTP_SERVER和TIME1970。NTP_SERVER是客戶端要連接的服務器地址,TIME1970指1970年1月1日(也叫Epoch)。在http://www.epochconverter.com/上可以查看Epoch時間值,或者把時間轉換成Epoch時間值。這個客戶端通過UDP協議創建一個UDP套接字(SOCK_DGRAM),用于連接服務器。然后,客戶端要在一個數據包中把數據'\x1b' + 47 * '\0'發給SNTP服務器。UDP客戶端分別使用sendto()和recvfrom()方法發送和接收數據。

服務器返回的時間信息打包在一個數組中,客戶端需要使用struct模塊取出數據。我們所需的數據是數組中的第11個元素。最后,我們要從取出的數據上減掉TIME1970,得到真正的當前時間。

代碼清單1-12展示了如何編寫這個SNTP客戶端,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications

import socket

import struct

import sys

import time

NTP_SERVER = "0.uk.pool.ntp.org"

TIME1970 = 2208988800L

def sntp_client():

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

data = '\x1b' + 47 * '\0'

client.sendto(data, (NTP_SERVER, 123))

data, address = client.recvfrom( 1024 )

if data:

print 'Response received from:', address

t = struct.unpack( '!12I', data )[10]

t -= TIME1970

print '\tTime=%s' % time.ctime(t)

if __name__ == '__main__':

sntp_client()

這個攻略通過SNTP協議從網絡時間服務器上獲取當前時間并打印出來,如下所示:

$ python 1_12_sntp_client.py

Response received from: ('87.117.251.2', 123)

Time=Tue Feb 25 14:49:38 2014

1.13.2 原理分析

這個SNTP客戶端創建一個套接字連接,然后通過協議發送數據。從NTP服務器(這里使用的是0.uk.pool.ntp.org)收到數據后,使用struct模塊取出數據。最后,減去1970年1月1日對應的時間戳,再使用Python內置的time模塊提供的ctime()方法打印時間。

1.14 編寫一個簡單的回顯客戶端/服務器應用

嘗試過Python中socket模塊的基本API后,現在我們來編寫一個套接字服務器和客戶端。這里,你將有機會利用在前述攻略中掌握的基本知識。

1.14.1 實戰演練

在這個例子中,不管服務器從客戶端收到什么輸入,都會將其回顯出來。我們要使用Python中的argparse模塊,在命令行中指定TCP端口。服務器腳本和客戶端腳本都要用到這個參數。

我們先來編寫服務器。首先創建一個TCP套接字對象。然后設定啟用重用地址,這樣想運行多少次服務器就能運行多少次。我們把套接字綁定在本地設備的指定端口上。在監聽階段,把backlog參數傳入listen()方法中,讓服務器在隊列中監聽多個客戶端。最后,等待客戶端連接,向服務器發送一些數據。收到數據后,服務器會把數據回顯給客戶端。

代碼清單1-13a展示了如何編寫回顯應用的服務器,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import socket

import sys

import argparse

host = 'localhost'

data_payload = 2048

backlog = 5

def echo_server(port):

""" A simple echo server """

# Create a TCP socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Enable reuse address/port

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind the socket to the port

server_address = (host, port)

print "Starting up echo server on %s port %s" % server_address

sock.bind(server_address)

# Listen to clients, backlog argument specifies the max no. of queued connections

sock.listen(backlog)

while True:

print "Waiting to receive message from client"

client, address = sock.accept()

data = client.recv(data_payload)

if data:

print "Data: %s" %data

client.send(data)

print "sent %s bytes back to %s" % (data, address)

# end connection

client.close()

if __name__ == '__main__':

parser = argparse.ArgumentParser(description='Socket Server Example')

parser.add_argument('--port', action="store", dest="port", type=int, required=True)

given_args = parser.parse_args()

port = given_args.port

echo_server(port)

在客戶端代碼中,我們要創建一個客戶端套接字,然后使用命令行參數中指定的端口連接服務器。客戶端把消息Test message. This will be echoed發送給服務器之后,立即就會在幾個數據片段中收到返回的消息。這里用到了兩個try-except塊,捕獲交互過程中發生的任何異常。

代碼清單1-13b展示了如何編寫回顯程序的客戶端,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import socket

import sys

import argparse

host = 'localhost'

def echo_client(port):

""" A simple echo client """

# Create a TCP/IP socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect the socket to the server

server_address = (host, port)

print "Connecting to %s port %s" % server_address

sock.connect(server_address)

# Send data

try:

# Send data

message = "Test message. This will be echoed"

print "Sending %s" % message

sock.sendall(message)

# Look for the response

amount_received = 0

amount_expected = len(message)

while amount_received < amount_expected:

data = sock.recv(16)

amount_received += len(data)

print "Received: %s" % data

except socket.errno, e:

print "Socket error: %s" %str(e)

except Exception, e:

print "Other exception: %s" %str(e)

finally:

print "Closing connection to the server"

sock.close()

if __name__ == '__main__':

parser = argparse.ArgumentParser(description='Socket Server Example')

parser.add_argument('--port', action="store", dest="port", type=int, required=True)

given_args = parser.parse_args()

port = given_args.port

echo_client(port)

1.14.2 原理分析

為了查看客戶端和服務器之間的交互,要在一個終端里啟動如下服務器腳本:

$ python 1_13a_echo_server.py --port=9900

Starting up echo server on localhost port 9900

Waiting to receive message from client

然后,在另一個終端里運行客戶端,如下所示:

$ python 1_13b_echo_client.py --port=9900

Connecting to localhost port 9900

Sending Test message. This will be echoed

Received: Test message. Th

Received: is will be echoe

Received: d

Closing connection to the server

連接到本地主機后,服務器還會輸出以下消息:

Data: Test message. This will be echoed

sent Test message. This will be echoed bytes back to ('127.0.0.1', 42961)

Waiting to receive message from client

總結

以上是生活随笔為你收集整理的python16进制字节序_第 1 章 套接字、IPv4和简单的客户端/服务器编程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。