版本2和3间的区别
于2006-11-01 10:24:26修订的的版本2
大小: 10511
编辑: czk
备注:
于2006-11-01 10:35:37修订的的版本3
大小: 24683
编辑: czk
备注:
删除的内容标记成这样。 加入的内容标记成这样。
行号 1: 行号 1:
[[TableOfContents]]
行号 178: 行号 179:
= 多线程 = == Python 中的 socket 编程 ==

在所有具有 socket 的语言中,socket 都是相同的 —— 这是两个应用程序彼此进行通信的管道。

=== 前提条件 ==

不管是使用 Python、Perl、Ruby、Scheme 还是其他有用的语言(此处 有用 的意思是这种语言有 socket 接口)来编写 socket 程序,socket 通常都是相同的。这是两个应用程序彼此进行通信的管道(这两个应用程序可以在同一台机器上,也可以位于两台不同的机器上)。

使用 Python 这种具有 socket 编程功能的语言的区别在于,它有一些辅助的类和方法,可以简化 socket 编程。在本节中,我们将展示 Python 的 socket API。可以使用一个脚本来执行 Python 的解释器,如果您要自己执行 Python,就可以一次只输入一行代码。这样,就可以看到每个方法调用之后的结果了。

下面这个例子展示了如何与 Python 解释器进行交互。此处我们使用了 socket 类方法 gethostbyname 将一个完整的域名(www.ibm.com)解析成一个使用点号分隔的 IP 地址字符串('129.42.19.99'):


清单 3. 从解释器命令行中使用 socket
{{{
[camus]$ python
Python 2.4 (#1, Feb 20 2005, 11:25:45)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type "help", "copyright", "credits" or "license" for more
   information.
>>> import socket
>>> socket.gethostbyname('www.ibm.com')
'129.42.19.99'
>>>
}}}
在导入 socket 模块之后,我调用了 gethostbyname 类方法将这个域名解析成 IP 地址。

现在,我们要讨论基本的 socket 方法,并通过 socket 进行通信。您应该熟悉 Python 解释器。

=== 创建和销毁 socket ===

要新创建一个 socket,可以使用 socket 类的 socket 方法。这是一个类方法,因为还没有得到可以应用方法的 socket 对象。socket 方法与 BSD API 类似,下面是创建流(TCP) socket 和数据报(UDP)socket 的方法:

清单 4. 创建流和数据报 socket
{{{#!python
streamSock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
}}}

在这种情况中,会返回一个 socket 对象。AF_INET 符号(第一个参数)说明您正在请求的是一个 Internet 协议(IP) socket,具体来说是 IPv4。第二个参数是传输协议的类型(SOCK_STREAM 对应 TCP socket,SOCK_DGRAM 对应 UDP socket)。如果底层操作系统支持 IPv6,那么还可以指定 socket.AF_INET6 来创建一个 IPv6 socket。

要关闭一个已经连接的 socket,可以使用 close 方法:
{{{#!python
streamSock.close()
}}}
最后,可以使用 del 语句删除一个 socket:
{{{#!python
del streamSock
}}}
这个语句可以永久地删除 socket 对象。之后再试图引用这个对象就会产生错误。

=== Socket 地址 ===

socket 地址是一个组合,包括一个接口地址和一个端口号。由于 Python 可以很简单地表示元组,因此地址和端口也可以这样表示。下面表示的是接口地址 192.168.1.1 和端口 80:
{{{#!python
( '192.168.1.1', 80 )
}}}
也可以使用完整的域名,例如:
{{{#!python
( 'www.ibm.com', 25 )
}}}
这个例子非常简单,当然比使用 C 编写相同功能的程序时对 sockaddr_in 进行操作要简单很多。下面的讨论给出了 Python 中地址的例子。

 
 
 第 4 页, 总 共 10 页

文档选项
  

未显示需要 JavaScript 的文档选项

对本教程的评价
  

帮助我们改进这些内容

Python 中的 socket 编程

在所有具有 socket 的语言中,socket 都是相同的 —— 这是两个应用程序彼此进行通信的管道。

前提条件

不管是使用 Python、Perl、Ruby、Scheme 还是其他有用的语言(此处 有用 的意思是这种语言有 socket 接口)来编写 socket 程序,socket 通常都是相同的。这是两个应用程序彼此进行通信的管道(这两个应用程序可以在同一台机器上,也可以位于两台不同的机器上)。

使用 Python 这种具有 socket 编程功能的语言的区别在于,它有一些辅助的类和方法,可以简化 socket 编程。在本节中,我们将展示 Python 的 socket API。可以使用一个脚本来执行 Python 的解释器,如果您要自己执行 Python,就可以一次只输入一行代码。这样,就可以看到每个方法调用之后的结果了。

下面这个例子展示了如何与 Python 解释器进行交互。此处我们使用了 socket 类方法 gethostbyname 将一个完整的域名(www.ibm.com)解析成一个使用点号分隔的 IP 地址字符串('129.42.19.99'):

清单 3. 从解释器命令行中使用 socket


     
[camus]$ python
Python 2.4 (#1, Feb 20 2005, 11:25:45)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type "help", "copyright", "credits" or "license" for more
   information.
>>> import socket
>>> socket.gethostbyname('www.ibm.com')
'129.42.19.99'
>>>


在导入 socket 模块之后,我调用了 gethostbyname 类方法将这个域名解析成 IP 地址。

现在,我们要讨论基本的 socket 方法,并通过 socket 进行通信。您应该熟悉 Python 解释器。


 回页首


创建和销毁 socket

要新创建一个 socket,可以使用 socket 类的 socket 方法。这是一个类方法,因为还没有得到可以应用方法的 socket 对象。socket 方法与 BSD API 类似,下面是创建流(TCP) socket 和数据报(UDP)socket 的方法:

清单 4. 创建流和数据报 socket


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

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


在这种情况中,会返回一个 socket 对象。AF_INET 符号(第一个参数)说明您正在请求的是一个 Internet 协议(IP) socket,具体来说是 IPv4。第二个参数是传输协议的类型(SOCK_STREAM 对应 TCP socket,SOCK_DGRAM 对应 UDP socket)。如果底层操作系统支持 IPv6,那么还可以指定 socket.AF_INET6 来创建一个 IPv6 socket。

要关闭一个已经连接的 socket,可以使用 close 方法:

streamSock.close()

最后,可以使用 del 语句删除一个 socket:

del streamSock

这个语句可以永久地删除 socket 对象。之后再试图引用这个对象就会产生错误。


 回页首


Socket 地址

socket 地址是一个组合,包括一个接口地址和一个端口号。由于 Python 可以很简单地表示元组,因此地址和端口也可以这样表示。下面表示的是接口地址 192.168.1.1 和端口 80:

( '192.168.1.1', 80 )

也可以使用完整的域名,例如:

( 'www.ibm.com', 25 )

这个例子非常简单,当然比使用 C 编写相同功能的程序时对 sockaddr_in 进行操作要简单很多。下面的讨论给出了 Python 中地址的例子。

=== 服务器 socket ===

服务器 socket 通常会在网络上提供一个服务。由于服务器和客户机的 socket 是使用不同的方式创建的,因此我们将分别进行讨论。

在创建 socket 之后,可以使用 bind 方法来绑定一个地址,listen 方法可以将其设置为监听状态,最后 accept 方法可以接收一个新的客户机连接。下面展示了这种用法:

清单 5. 使用服务器 socket
{{{#!python
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
sock.bind( ('', 2525) )
sock.listen( 5 )
newsock, (remhost, remport) = sock.accept()
}}}

对于这个服务器来说,使用地址 ('', 2525) 就意味着接口地址中使用了通配符 (''),这样可以接收来自这个主机上的任何接口的连接。还说明要绑定到端口 2525 上。

注意此处 accept 方法不但要返回一个新的 socket 对象,它表示了客户机连接(newsock);而且还要返回一个地址对(socket 端的远程地址和端口号)。Python 的 SocketServer 模块可以对这个过程进一步进行简化,正如上面展示的一样。

虽然也可以创建数据报服务器,不过这是无连接的,因此没有对应的 accept 方法。下面的例子创建一个数据报服务器 socket:

清单 6. 创建一个数据报服务器 socket
{{{#!python
sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
sock.bind( ('', 2525) )
}}}

后面对于 socket I/O 的讨论将说明 I/O 是如何为流 socket 和数据报 socket 工作的。

现在,让我们来看一下客户机是如何创建 socket 并将其连接到服务器上的。

=== 客户机 socket ===

客户机 socket 的创建和连接机制与服务器 socket 相似。在创建 socket 之前,都需要一个地址 —— 不是本地绑定到这个 socket 上(就像服务器 socket 的情况那样),而是标识这个 socket 应该连接到什么地方。假设在这个主机的 IP 地址 '192.168.1.1' 和端口 2525 上有一个服务器。下面的代码可以创建一个新的 socket,并将其连接到定义的服务器上:

清单 7. 创建一个流 socket 并将其连接到服务器上
{{{#!python
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
sock.connect( ('192.168.1.1', 2525) )
}}}

对于数据报 socket 来说,处理过程稍有不同。回想一下,数据报从本质上来说都是没有连接的。可以这样考虑:流 socket 是两个点之间的通信管道,而数据报 socket 是基于消息的,可以同时与多个点进行通信。下面是一个数据报客户机的例子。

清单 8. 创建一个数据报 socket 并将其连接到服务器上
{{{#!python
sock = socket.socket( socket.AF_INET, sock.sock_DGRAM )
sock.connect( ('192.168.1.1', 2525) )
}}}

尽管我们使用了 connect 方法,但是此处是有区别的:在客户机和服务器之间并不存在真正的 连接。此处的连接是对以后 I/O 的一个简化。通常在数据报 socket 中,必须在所发送的数据中提供目标地址的信息。通过使用 connect,我们可以使用客户机对这些信息进行缓存,并且 send 方法的使用可以与流 socket 情况一样(只不过不需要目标地址)。可以再次调用 connect 来重新指定数据报客户机消息的目标。

=== 流 socket I/O ===

通过流 socket 发送和接收数据在 Python 中是很简单的。有几个方法可以用来通过流 socket 传递数据(例如 send、recv、read 和 write)。

第一个例子展示了流 socket 的服务器和客户机。在这个例子中,服务器会回显从客户机接收到的信息。

回显流服务器如清单 9 所示。在创建一个新的流 socket 之前,需要先绑定一个地址(接收来自任何接口和 45000 端口的连接),然后调用 listen 方法来启用到达的连接。这个回显服务器然后就可以循环处理各个客户机连接了。它会调用 accept 方法并阻塞(即不会返回),直到有新的客户机连接到它为止,此时会返回新的客户机 socket,以及远程客户机的地址信息。使用这个新的客户机 socket,我们可以调用 recv 来从另一端接收一个字符串,然后将这个字符串写回这个 socket。然后立即关闭这个 socket。

清单 9. 简单的 Python 流回显服务器
{{{#!python
import socket

srvsock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
srvsock.bind( ('', 23000) )
srvsock.listen( 5 )

while 1:
  clisock, (remhost, remport) = srvsock.accept()
  str = clisock.recv(100)
  clisock.send( str )
  clisock.close()
}}}

清单 10 显示了与清单 9 的回显服务器对应的客户机。在创建一个新的流程 socket 之前,需要使用 connect 方法将这个 socket 连接到服务器上。当连接之后(connect 方法返回),客户机就会使用 send 方法输出一条简单的文本消息,然后使用 recv 方法等待回显。print 语句用来显示所读取的内容。当这个过程完成之后,就执行 close 方法关闭 socket。

清单 10. 简单的 Python 流回显客户机
{{{#!python
import socket

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

clisock.connect( ('', 23000) )

clisock.send("Hello World\n")
print clisock.recv(100)

clisock.close()
}}}


=== 数据报 socket I/O ===

数据报 socket 天生就是无连接的,这意味着通信需要提供一个目标地址。类似,当通过一个 socket 接收消息时,必须同时返回数据源。recvfrom 和 sendto 方法可以支持其他地址,正如您在数据报回显服务器和客户机实现中可以看到的一样。

清单 11 显示了数据报回显服务器的代码。首先创建一个 socket,然后使用 bind 方法绑定到一个地址上。然后进入一个无限循环来处理客户机的请求。recvfrom 方法从一个数据报 socket 接收消息,并返回这个消息以及发出消息的源地址。这些信息然后会被传入 sendto 方法,将这些消息返回到源端。

清单 11. 简单的 Python 数据报回显服务器
{{{#!python
import socket

dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
dgramSock.bind( ('', 23000) )

while 1:
  msg, (addr, port) = dgramSock.recvfrom( 100 )
  dgramSock.sendto( msg, (addr, port) )
}}}

数据报客户机更加简单。在创建数据报 socket 之后,我们使用 sendto 方法将一条消息发送到一个指定的地址。(记住:数据报是无连接的。)在 sendto 完成之后,我们使用 recv 来等待回显的响应,然后打印所收到的信息。注意此处我们并没有使用 recvfrom,这是因为我们对两端的地址信息并不感兴趣。

清单 12. 简单的 Python 数据报回显客户机
{{{#!python
import socket

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

dgramSock.sendto( "Hello World\n", ('', 23000) )
print dgramSock.recv( 100 )
dgramSock.close()
}}}


= The End =

TableOfContents

Socket编程

本教程将向您介绍如何使用 Python 开发基于 socket 的网络应用程序。在本教程中,您将首先学习一些 Python 的基础知识,并了解 Python 是如何成为一种很好的网络编程语言的。然后您将着重了解 Python 的基本 socket 特性,我们使用了一个样例聊天程序作为参考;并了解一下可以提供异步通信的其他高级类。

1. 开始之前

1.1. 关于本教程

Python 是一个流行的面向对象的脚本语言,其语法非常简单,有大量的开发人员采用它。它是一种通用的语言,可以在各种环境中使用。它也是初学者常用的一种语言,就像是 20 世纪 70 年代的 BASIC 一样。

本教程重点介绍了 Python 语言在网络编程方面的知识。我定义了 Python 的一些基本 socket 特性,以及可以提供异步 socket 的另外一些高级类。我还详细介绍了 Python 的应用层协议类,展示了如何构建 Web 客户机、邮件服务器和客户机等等。

在本教程中我们还开发了一个简单的聊天服务器,从而展示 Python 在 socket 应用程序方面的强大功能。

您应该对标准的 BSD socket API 有基本的了解,并且具有使用 GNU/Linux® 环境的经验。如果熟悉面向对象的概念会很有帮助。

1.2. 前提条件

本教程以及本教程中所展示的例子使用的都是 2.4 版本的 Python。可以从 Python Web 站点上下载这个版本(请参阅 参考资料 中的链接)。要编译 Python 解释器,需要使用 GNU C 编译器(gcc)和 configure/make 工具(任何标准的 GNU/Linux 发行版中都包含这个工具)。

您应该对标准的 BSD Sockets API 有基本的了解,并且具有使用 GNU/Linux 环境的经验。如果熟悉面向对象的概念将会很有帮助。

2. Python 简介

首先,让我们来体验一下 Python。

2.1. 什么是 Python?

Python 是一种通用的面向对象脚本语言,可以在各种平台上使用。Python 是在 20 世纪 90 年代在 Amsterdam 的 CWI 诞生的,目前由 Python 软件基金会进行资助。

Python 具有迷人的可移植特性,几乎在所有操作系统上都可以找到它。

Python 是一种解释性的语言,很容易进行扩展。可以使用 C 或 C++ 函数通过添加包含函数、变量和类型的新模块来对 Python 进行扩展。

也可以在 C 或 C++ 程序中非常方便地嵌入 Python 程序,这样就可以对应用程序进行扩展,使其具有脚本的功能。

Python 最有用的一点是它具有大量的扩展模块。这些模块提供了一些标准的功能,例如字符串或列表处理;还有一些应用级的模块,用来进行视频和图像处理、声音处理和网络处理。

2.2. 体验 Python

下面我们先对 Python 是什么建立一个直观印象。

作为一种解释性语言,Python 很容易使用,并且能够快速验证我们的想法和开发原型软件。Python 程序可以作为一个整体进行解释,也可以一行行地解释。

可以在第一次运行 Python 时测试一下下面的 Python 代码,然后一次只输入一行试试。在 Python 启动之后,会显示一个提示符(>>>),可以在这里输入命令。 注意在 Python 中,缩进非常重要,因此代码前面的空格不能忽略:

清单 1. 可以试验的几个 Python 例子

   1 # Open a file, read each line, and print it out
   2 for line in open('file.txt'):
   3   print line
   4 
   5 
   6 # Create a file and write to it
   7 file = open("test.txt", "w")
   8 file.write("test line\n")
   9 file.close()
  10 
  11 
  12 # Create a small dictionary of names and ages and manipulate
  13 family = {'Megan': 13, 'Elise': 8, 'Marc': 6}
  14 
  15 # results in 8
  16 family['Elise']
  17 
  18 # Remove the key/value pair
  19 del family['Elise']
  20 
  21 
  22 # Create a list and a function that doubles its input.  Map the
  23 #    function to each of the elements of the list (creating a new
  24 #    list as a result).
  25 arr = [1, 2, 3, 4, 5]
  26 def double(x): return x*x
  27 map(double, arr)
  28 
  29 
  30 # Create a class, inherit by another, and then instantiate it and
  31 #    invoke its methods.
  32 class Simple:
  33   def __init__(self, name):
  34     self.name = name
  35 
  36   def hello(self):
  37     print self.name+" says hi."
  38 
  39 class Simple2(Simple):
  40   def goodbye(self):
  41     print self.name+" says goodbye."
  42 
  43 me = Simple2("Tim")
  44 me.hello()
  45 me.goodbye()

2.3. 为什么使用 Python?

我们要学习和使用 Python 的一个原因是它非常流行。Python 用户的数量以及使用 Python 编写的应用程序的不断增长使这种努力是值得的。

在很多开发领域中都可以看到 Python 的踪迹,它被用来构建系统工具,用作程序集成的黏合剂,用来开发 Internet 应用程序和快速开发原型。

Python 与其他脚本语言相比也有一定的优势。它的语法非常简单,概念非常清晰,这使得 Python 非常容易学习。在使用复杂的数据结构(例如列表、词典和元组)时,Python 也非常简单,而且可描述性更好。Python 还可以对语言进行扩充,也可以由其他语言进行扩充。

我发现 Python 的语法使它比 Perl 的可读性和可维护性更好,但是比 Ruby 要差。与 Ruby 相比,Python 的优点在于它有大量的库和模块可以使用。使用这些库和模块,只需要很少的代码就可以开发功能丰富的程序。

Python 使用缩进格式来判断代码的作用域,这有些讨厌,但是 Python 本身的简单性使这个问题已经微不足道了。

现在,让我们开始进入 Python 中的 socket 编程世界。

3. Python socket 模块

3.1. 基本的 Python socket 模块

Python 提供了两个基本的 socket 模块。第一个是 Socket,它提供了标准的 BSD Sockets API。第二个是 SocketServer,它提供了服务器中心类,可以简化网络服务器的开发。Python 使用一种异步的方式来实现这种功能,您可以提供一些插件类来处理服务器中应用程序特有的任务。表 1 列出了本节所涉及的类和模块。

表 1. Python 类和模块

类/模块

说明

Socket

低层网络接口(每个 BSD API)

SocketServer

提供简化网络服务器开发的类

让我们来看一下这些模块,以便理解它们是如何工作的。

3.2. socket 模块

Socket 模块提供了 UNIX® 程序员所熟悉的基本网络服务(也称为 BSD API)。这个模块中提供了在构建 socket 服务器和客户机时所需要的所有功能。

这个 API 与标准的 C API 之间的区别在于它是面向对象的。在 C 中,socket 描述符是从 socket 调用中获得的,然后会作为一个参数传递给 BSD API 函数。在 Python 中,socket 方法会向应用 socket 方法的对象返回一个 socket 对象。表 2 给出了几个类方法,表 3 显示了一部分实例方法。

表 2. Socket 模块的类方法

类方法

说明

Socket

低层网络接口(每个 BSD API)

socket.socket(family, type)

创建并返回一个新的 socket 对象

socket.getfqdn(name)

将使用点号分隔的 IP 地址字符串转换成一个完整的域名

socket.gethostbyname(hostname)

将主机名解析为一个使用点号分隔的 IP 地址字符串

socket.fromfd(fd, family, type)

从现有的文件描述符创建一个 socket 对象

  • 表 3. Socket 模块的实例方法

实例方法

说明

sock.bind( (adrs, port) )

将 socket 绑定到一个地址和端口上

sock.accept()

返回一个客户机 socket(带有客户机端的地址信息)

sock.listen(backlog)

将 socket 设置成监听模式,能够监听 backlog 外来的连接请求

sock.connect( (adrs, port) )

将 socket 连接到定义的主机和端口上

sock.recv( buflen[, flags] )

从 socket 中接收数据,最多 buflen 个字符

sock.recvfrom( buflen[, flags] )

从 socket 中接收数据,最多 buflen 个字符,同时返回数据来源的远程主机和端口号

sock.send( data[, flags] )

通过 socket 发送数据

sock.sendto( data[, flags], addr )

通过 socket 发送数据

sock.close()

关闭 socket

sock.getsockopt( lvl, optname )

获得指定 socket 选项的值

sock.setsockopt( lvl, optname, val )

设置指定 socket 选项的值

类方法 和 实例方法 之间的区别在于,实例方法需要有一个 socket 实例(从 socket 返回)才能执行,而类方法则不需要。

3.3. SocketServer 模块

SocketServer 模块是一个十分有用的模块,它可以简化 socket 服务器的开发。有关这个模块的使用的讨论已经远远超出了本教程的范围,但是我将展示一下它的基本用法,然后您可以参阅 参考资料 一节中给出的链接。

考虑清单 2 中给出的例子。此处,我们实现了一个简单的 “Hello World” 服务器,当客户机连接它时,它就会显示这样一条消息。我首先创建一个请求处理程序,它继承了 SocketServer.StreamRequestHandler 类。我们定义了一个名为 handle 的方法,它处理服务器的请求。服务器所做的每件事情都必须在这个函数的上下文中进行处理(最后,关闭这个 socket)。这个过程的工作方式非常简单,但是您可以使用这个类来实现一个简单的 HTTP 服务器。在 handle 方法中,我们打一个招呼就退出了。

现在连接处理程序已经准备就绪了,剩下的工作是创建 socket 服务器。我们使用了 SocketServer.TCPServer 类,并提供了地址和端口号(要将服务器绑定到哪个端口上)以及请求处理方法。结果是一个 TCPServer 对象。调用 serve_forever 方法启动服务器,并使其对这个连接可用。

清单 2. 用 SocketServer 模块实现一个简单的服务器 {{{!python import SocketServer

class hwRequestHandler( SocketServer.StreamRequestHandler ):

  • def handle( self ):
    • self.wfile.write("Hello World!\n")

server = SocketServer.TCPServer( ("", 2525), hwRequestHandler ) server.serve_forever() }}}

就是这样!Python 允许这种机制的任何变种,包括 UDPServers 以及派生进程和线程的服务器。请参阅 参考资料 一节中更多信息的链接。

4. Python 中的 socket 编程

在所有具有 socket 的语言中,socket 都是相同的 —— 这是两个应用程序彼此进行通信的管道。

=== 前提条件 ==

不管是使用 Python、Perl、Ruby、Scheme 还是其他有用的语言(此处 有用 的意思是这种语言有 socket 接口)来编写 socket 程序,socket 通常都是相同的。这是两个应用程序彼此进行通信的管道(这两个应用程序可以在同一台机器上,也可以位于两台不同的机器上)。

使用 Python 这种具有 socket 编程功能的语言的区别在于,它有一些辅助的类和方法,可以简化 socket 编程。在本节中,我们将展示 Python 的 socket API。可以使用一个脚本来执行 Python 的解释器,如果您要自己执行 Python,就可以一次只输入一行代码。这样,就可以看到每个方法调用之后的结果了。

下面这个例子展示了如何与 Python 解释器进行交互。此处我们使用了 socket 类方法 gethostbyname 将一个完整的域名(www.ibm.com)解析成一个使用点号分隔的 IP 地址字符串('129.42.19.99'):

清单 3. 从解释器命令行中使用 socket

[camus]$ python
Python 2.4 (#1, Feb 20 2005, 11:25:45)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type "help", "copyright", "credits" or "license" for more
   information.
>>> import socket
>>> socket.gethostbyname('www.ibm.com')
'129.42.19.99'
>>>

在导入 socket 模块之后,我调用了 gethostbyname 类方法将这个域名解析成 IP 地址。

现在,我们要讨论基本的 socket 方法,并通过 socket 进行通信。您应该熟悉 Python 解释器。

4.1. 创建和销毁 socket

要新创建一个 socket,可以使用 socket 类的 socket 方法。这是一个类方法,因为还没有得到可以应用方法的 socket 对象。socket 方法与 BSD API 类似,下面是创建流(TCP) socket 和数据报(UDP)socket 的方法:

清单 4. 创建流和数据报 socket

   1 streamSock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
   2 dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )

在这种情况中,会返回一个 socket 对象。AF_INET 符号(第一个参数)说明您正在请求的是一个 Internet 协议(IP) socket,具体来说是 IPv4。第二个参数是传输协议的类型(SOCK_STREAM 对应 TCP socket,SOCK_DGRAM 对应 UDP socket)。如果底层操作系统支持 IPv6,那么还可以指定 socket.AF_INET6 来创建一个 IPv6 socket。

要关闭一个已经连接的 socket,可以使用 close 方法:

   1 streamSock.close()

最后,可以使用 del 语句删除一个 socket:

   1 del streamSock

这个语句可以永久地删除 socket 对象。之后再试图引用这个对象就会产生错误。

4.2. Socket 地址

socket 地址是一个组合,包括一个接口地址和一个端口号。由于 Python 可以很简单地表示元组,因此地址和端口也可以这样表示。下面表示的是接口地址 192.168.1.1 和端口 80:

   1 ( '192.168.1.1', 80 )

也可以使用完整的域名,例如:

   1 ( 'www.ibm.com', 25 )

这个例子非常简单,当然比使用 C 编写相同功能的程序时对 sockaddr_in 进行操作要简单很多。下面的讨论给出了 Python 中地址的例子。

  • 第 4 页, 总 共 10 页

文档选项

未显示需要 JavaScript 的文档选项

对本教程的评价

帮助我们改进这些内容

Python 中的 socket 编程

在所有具有 socket 的语言中,socket 都是相同的 —— 这是两个应用程序彼此进行通信的管道。

前提条件

不管是使用 Python、Perl、Ruby、Scheme 还是其他有用的语言(此处 有用 的意思是这种语言有 socket 接口)来编写 socket 程序,socket 通常都是相同的。这是两个应用程序彼此进行通信的管道(这两个应用程序可以在同一台机器上,也可以位于两台不同的机器上)。

使用 Python 这种具有 socket 编程功能的语言的区别在于,它有一些辅助的类和方法,可以简化 socket 编程。在本节中,我们将展示 Python 的 socket API。可以使用一个脚本来执行 Python 的解释器,如果您要自己执行 Python,就可以一次只输入一行代码。这样,就可以看到每个方法调用之后的结果了。

下面这个例子展示了如何与 Python 解释器进行交互。此处我们使用了 socket 类方法 gethostbyname 将一个完整的域名(www.ibm.com)解析成一个使用点号分隔的 IP 地址字符串('129.42.19.99'):

清单 3. 从解释器命令行中使用 socket

[camus]$ python Python 2.4 (#1, Feb 20 2005, 11:25:45) [GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2 Type "help", "copyright", "credits" or "license" for more

  • information.

>>> import socket >>> socket.gethostbyname('www.ibm.com') '129.42.19.99' >>>

在导入 socket 模块之后,我调用了 gethostbyname 类方法将这个域名解析成 IP 地址。

现在,我们要讨论基本的 socket 方法,并通过 socket 进行通信。您应该熟悉 Python 解释器。

  • 回页首

创建和销毁 socket

要新创建一个 socket,可以使用 socket 类的 socket 方法。这是一个类方法,因为还没有得到可以应用方法的 socket 对象。socket 方法与 BSD API 类似,下面是创建流(TCP) socket 和数据报(UDP)socket 的方法:

清单 4. 创建流和数据报 socket

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

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

在这种情况中,会返回一个 socket 对象。AF_INET 符号(第一个参数)说明您正在请求的是一个 Internet 协议(IP) socket,具体来说是 IPv4。第二个参数是传输协议的类型(SOCK_STREAM 对应 TCP socket,SOCK_DGRAM 对应 UDP socket)。如果底层操作系统支持 IPv6,那么还可以指定 socket.AF_INET6 来创建一个 IPv6 socket。

要关闭一个已经连接的 socket,可以使用 close 方法:

streamSock.close()

最后,可以使用 del 语句删除一个 socket:

del streamSock

这个语句可以永久地删除 socket 对象。之后再试图引用这个对象就会产生错误。

  • 回页首

Socket 地址

socket 地址是一个组合,包括一个接口地址和一个端口号。由于 Python 可以很简单地表示元组,因此地址和端口也可以这样表示。下面表示的是接口地址 192.168.1.1 和端口 80:

( '192.168.1.1', 80 )

也可以使用完整的域名,例如:

( 'www.ibm.com', 25 )

这个例子非常简单,当然比使用 C 编写相同功能的程序时对 sockaddr_in 进行操作要简单很多。下面的讨论给出了 Python 中地址的例子。

4.3. 服务器 socket

服务器 socket 通常会在网络上提供一个服务。由于服务器和客户机的 socket 是使用不同的方式创建的,因此我们将分别进行讨论。

在创建 socket 之后,可以使用 bind 方法来绑定一个地址,listen 方法可以将其设置为监听状态,最后 accept 方法可以接收一个新的客户机连接。下面展示了这种用法:

清单 5. 使用服务器 socket

   1 sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
   2 sock.bind( ('', 2525) )
   3 sock.listen( 5 )
   4 newsock, (remhost, remport) = sock.accept()

对于这个服务器来说,使用地址 (, 2525) 就意味着接口地址中使用了通配符 (),这样可以接收来自这个主机上的任何接口的连接。还说明要绑定到端口 2525 上。

注意此处 accept 方法不但要返回一个新的 socket 对象,它表示了客户机连接(newsock);而且还要返回一个地址对(socket 端的远程地址和端口号)。Python 的 SocketServer 模块可以对这个过程进一步进行简化,正如上面展示的一样。

虽然也可以创建数据报服务器,不过这是无连接的,因此没有对应的 accept 方法。下面的例子创建一个数据报服务器 socket:

清单 6. 创建一个数据报服务器 socket

   1 sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
   2 sock.bind( ('', 2525) )

后面对于 socket I/O 的讨论将说明 I/O 是如何为流 socket 和数据报 socket 工作的。

现在,让我们来看一下客户机是如何创建 socket 并将其连接到服务器上的。

4.4. 客户机 socket

客户机 socket 的创建和连接机制与服务器 socket 相似。在创建 socket 之前,都需要一个地址 —— 不是本地绑定到这个 socket 上(就像服务器 socket 的情况那样),而是标识这个 socket 应该连接到什么地方。假设在这个主机的 IP 地址 '192.168.1.1' 和端口 2525 上有一个服务器。下面的代码可以创建一个新的 socket,并将其连接到定义的服务器上:

清单 7. 创建一个流 socket 并将其连接到服务器上

   1 sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
   2 sock.connect( ('192.168.1.1', 2525) )

对于数据报 socket 来说,处理过程稍有不同。回想一下,数据报从本质上来说都是没有连接的。可以这样考虑:流 socket 是两个点之间的通信管道,而数据报 socket 是基于消息的,可以同时与多个点进行通信。下面是一个数据报客户机的例子。

清单 8. 创建一个数据报 socket 并将其连接到服务器上

   1 sock = socket.socket( socket.AF_INET, sock.sock_DGRAM )
   2 sock.connect( ('192.168.1.1', 2525) )

尽管我们使用了 connect 方法,但是此处是有区别的:在客户机和服务器之间并不存在真正的 连接。此处的连接是对以后 I/O 的一个简化。通常在数据报 socket 中,必须在所发送的数据中提供目标地址的信息。通过使用 connect,我们可以使用客户机对这些信息进行缓存,并且 send 方法的使用可以与流 socket 情况一样(只不过不需要目标地址)。可以再次调用 connect 来重新指定数据报客户机消息的目标。

4.5. 流 socket I/O

通过流 socket 发送和接收数据在 Python 中是很简单的。有几个方法可以用来通过流 socket 传递数据(例如 send、recv、read 和 write)。

第一个例子展示了流 socket 的服务器和客户机。在这个例子中,服务器会回显从客户机接收到的信息。

回显流服务器如清单 9 所示。在创建一个新的流 socket 之前,需要先绑定一个地址(接收来自任何接口和 45000 端口的连接),然后调用 listen 方法来启用到达的连接。这个回显服务器然后就可以循环处理各个客户机连接了。它会调用 accept 方法并阻塞(即不会返回),直到有新的客户机连接到它为止,此时会返回新的客户机 socket,以及远程客户机的地址信息。使用这个新的客户机 socket,我们可以调用 recv 来从另一端接收一个字符串,然后将这个字符串写回这个 socket。然后立即关闭这个 socket。

清单 9. 简单的 Python 流回显服务器

   1 import socket
   2 
   3 srvsock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
   4 srvsock.bind( ('', 23000) )
   5 srvsock.listen( 5 )
   6 
   7 while 1:
   8   clisock, (remhost, remport) = srvsock.accept()
   9   str = clisock.recv(100)
  10   clisock.send( str )
  11   clisock.close()

清单 10 显示了与清单 9 的回显服务器对应的客户机。在创建一个新的流程 socket 之前,需要使用 connect 方法将这个 socket 连接到服务器上。当连接之后(connect 方法返回),客户机就会使用 send 方法输出一条简单的文本消息,然后使用 recv 方法等待回显。print 语句用来显示所读取的内容。当这个过程完成之后,就执行 close 方法关闭 socket。

清单 10. 简单的 Python 流回显客户机

   1 import socket
   2 
   3 clisock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
   4 
   5 clisock.connect( ('', 23000) )
   6 
   7 clisock.send("Hello World\n")
   8 print clisock.recv(100)
   9 
  10 clisock.close()

4.6. 数据报 socket I/O

数据报 socket 天生就是无连接的,这意味着通信需要提供一个目标地址。类似,当通过一个 socket 接收消息时,必须同时返回数据源。recvfrom 和 sendto 方法可以支持其他地址,正如您在数据报回显服务器和客户机实现中可以看到的一样。

清单 11 显示了数据报回显服务器的代码。首先创建一个 socket,然后使用 bind 方法绑定到一个地址上。然后进入一个无限循环来处理客户机的请求。recvfrom 方法从一个数据报 socket 接收消息,并返回这个消息以及发出消息的源地址。这些信息然后会被传入 sendto 方法,将这些消息返回到源端。

清单 11. 简单的 Python 数据报回显服务器

   1 import socket
   2 
   3 dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
   4 dgramSock.bind( ('', 23000) )
   5 
   6 while 1:
   7   msg, (addr, port) = dgramSock.recvfrom( 100 )
   8   dgramSock.sendto( msg, (addr, port) )

数据报客户机更加简单。在创建数据报 socket 之后,我们使用 sendto 方法将一条消息发送到一个指定的地址。(记住:数据报是无连接的。)在 sendto 完成之后,我们使用 recv 来等待回显的响应,然后打印所收到的信息。注意此处我们并没有使用 recvfrom,这是因为我们对两端的地址信息并不感兴趣。

清单 12. 简单的 Python 数据报回显客户机

   1 import socket
   2 
   3 dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
   4 
   5 dgramSock.sendto( "Hello World\n", ('', 23000) )
   6 print dgramSock.recv( 100 )
   7 dgramSock.close()

The End

Python网络编程 (2008-02-23 15:35:48由localhost编辑)

ch3n2k.com | Copyright (c) 2004-2020 czk.