博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

socket 模块

Posted on 2015-07-06 12:25  bw_0927  阅读(146)  评论(0)    收藏  举报

http://pythonic.zoomquiet.io/data/20051121170755/

 

>>> import socket
>>> socket.gethostbyname('www.ibm.com')
'129.42.19.99'
>>>
streamSock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )

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

要关闭一个已经连接的 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 )

 

服务器 socket

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

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


清单 5. 使用服务器 socket


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

 

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


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


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

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


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

 

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


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


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

 

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

清单 9. 简单的 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. 简单的 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 接收消息时,必须同时返回数据源。

清单 11. 简单的 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 数据报回显客户机


					
import socket

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

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

socket 选项

socket 在缺省情况下有一些标准的行为,但是可以使用一些选项来修改 socket 的行为。我们可以使用 setsockopt 方法来修改 socket 的选项,并使用 getsockopt 方法来读取 socket 选项的值。

清单 13. 使用 socket 选项


					
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 )

# Get the state of the SO_REUSEADDR option
state = sock.getsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR )

# Enable the SO_REUSEADDR option
sock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )

 

清单 14. 等待 stdin 的输入


					
rlist, wlist, elist = select.select( [sys.stdin], [], [] )

print sys.stdin.read()

 

传递给 select 的参数是几个列表,分别表示读事件、写事件和错误事件。select 方法返回三个列表,其中包含满足条件的对象(读、写和异常)。在这个例子中,返回的 rlist 应该是 [sys.stdin],说明数据在 stdin 上可用了。然后就可以使用 read 方法来读取这些数据。

select 方法也可以处理 socket 描述符。在下面的例子(请参阅清单 15)中,我们创建了两个客户机 socket,并将其连接到一个远程端上。然后使用 select 方法来确定哪个 socket 可以读取数据了。接着可以读取这些数据,并将其显示到 stdout 上。

清单 15. 展示处理多个 socket 的 select 方法


					
import socket
import select

sock1 = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
sock2 = socket.socket( socket.AF_INET, socket.SOCK_STREAM )

sock1.connect( ('192.168.1.1', 25) )
sock2.connect( ('192.168.1.1', 25) )

while 1:

  # Await a read event
  rlist, wlist, elist = select.select( [sock1, sock2], [], [], 5 )

  # Test for timeout
  if [rlist, wlist, elist] == [ [], [], [] ]:

    print "Five seconds elapsed.\n"

  else:

    # Loop through each socket in rlist, read and print the available data
    for sock in rlist:

      print sock.recv( 100 )