浙江雁荡山 发表于 2018-8-9 09:29:41

Python 中的 socket 模块

  本文参考PYTHON 网络编程 第一章
  import socket
  help(socket)
  Functions:
  socket() -- create a new socket object
  socketpair() -- create a pair of new socket objects
[*]
  fromfd() -- create a socket object from an open file descriptor
[*]
  gethostname() -- return the current hostname
  gethostbyname() -- map a hostname to its IP number
  gethostbyaddr() -- map an IP number or hostname to DNS info
  getservbyname() -- map a service name and a protocol name to a port number
  getprotobyname() -- map a protocol name (e.g. 'tcp') to a number
  ntohs(), ntohl() -- convert 16, 32 bit int from network to host byte order
  htons(), htonl() -- convert 16, 32 bit int from host to network byte order
  inet_aton() -- convert IP addr string (123.45.67.89) to 32-bit packed format
  inet_ntoa() -- convert 32-bit packed format IP to string (123.45.67.89)
  ssl() -- secure socket layer support (only available if configured)
  socket.getdefaulttimeout() -- get the default timeout value
  socket.setdefaulttimeout() -- set the default timeout value
  create_connection() -- connects to an address, with an optional timeout and optional source address.
  简单的介绍一下这些函数的作用:
  一、socket类方法(直接可以通过socket 类进行调用)
  1、gethostbyname() -- map a hostname to its IP number
In : import socket  
In : socket.gethostname()
  
Out: 'God'
  2、gethostbyname() -- map a hostname to its IP number
In : import socket  
In : hostname=socket.gethostname()
  
In : print hostname
  
God
  
In : socket.gethostbyname(hostname)
  
Out: '127.0.1.1'
  
In : socket.gethostbyname('www.baidu.com')
  
Out: '119.75.218.70'
  3、gethostbyaddr() -- map an IP number or hostname to DNS info
In : import socket  
In : socket.gethostbyaddr('God')
  
Out: ('God', [], ['127.0.1.1'])
  

  
In : socket.gethostbyaddr('119.75.218.70')
  
---------------------------------------------------------------------------
  
herror                                    Traceback (most recent call last)
  
<ipython-input-14-612b876e1ed4> in <module>()
  
----> 1 socket.gethostbyaddr('119.75.218.70')
  

  
herror: Host name lookup failure
  gethostbyname 和 gethostbyaddr 两个函数时要依赖于DNS的(首先会从/etc/hosts获取结果,然后在到dns服务器中获取相应结果)。
  4、协议名 、端口号相关
  getservbyport() --Return the service name from a port number and protocol name.
  getservbyname() -- map a service name and a protocol name to a port number
In : import socket  
In : socket.getservbyname('http')
  
Out: 80
  
In : socket.getservbyport(21)
  
Out: 'ftp'
  注释:getservbyport 和 getservbyname 两种方法获取的信息应该时从/etc/services 中获取
  5、IPV4 地址转换
  inet_aton() -- convert IP addr string (123.45.67.89) to 32-bit packed format
  inet_ntoa() -- convert 32-bit packed format IP to string (123.45.67.89)
In : add = '127.0.0.1'  
In : packed_addr = socket.inet_aton(add)
  
In : addr2 = socket.inet_ntoa(packed_addr)
  
In : addr == addr2
  
Out: True
  inet_ntop(...)
  inet_ntop(af, packed_ip) -> string formatted IP address
  Convert a packed IP address of the given family to string format.
  inet_pton(...)
  inet_pton(af, ip) -> packed IP address string
  Convert an IP address from string format to a packed string suitable for use with low-level network functions.
  通常会使用 inet_pton 去判断一个地址的有效性。例如判断一个IPV6地址是否有效
def validip6addr(address):  
    """
  
    Returns True if `address` is a valid IPv6 address.
  

  
      >>> validip6addr('::')
  
      True
  
      >>> validip6addr('aaaa:bbbb:cccc:dddd::1')
  
      True
  
      >>> validip6addr('1:2:3:4:5:6:7:8:9:10')
  
      False
  
      >>> validip6addr('12:10')
  
      False
  
    """
  
    try:
  
      socket.inet_pton(socket.AF_INET6, address)
  
    except (socket.error, AttributeError):
  
      return False
  

  
    return True
  6、主机字节序和网络字节序之间相互转换(这种需要转换的数据仅仅限制于数字)
  编写低层网络应用时,或许需要处理通过电缆在两台设备之间传送的低层数据。在这种操作中,需要把主机操作系统发出的数据转换成网络格式,或者做逆向转换,因为这两种数据的表示
  方式不一样。(数据在电缆中的表示方式和在计算机中的表示方式是不一样的)
  ntohs(), ntohl() -- convert 16, 32 bit int from network to host byte order
  htons(), htonl() -- convert 16, 32 bit int from host to network byte order
  注意:以上四个函数的参数全部都为数字,返回结构也全部都为数字
In : data = 1234  
In : print 'data:%s\nntohs:%s\nntohl:%s' %(data,socket.ntohs(data),socket.ntohl(data))
  
data:1234
  
ntohs:53764
  
ntohl:3523477504
  

  
In : print 'data:%s\nhtons:%s\nhtonl:%s' %(data,socket.htons(data),socket.htonl(data))
  
data:1234
  
htons:53764
  
htonl:3523477504
  以上所有的函数都属于socket 类方法,直接通过socket 类即可调用
  二、socket 实例调用
  1、创建一个socket 实例
  socket(]]) -> socket object
  Open a socket of the given type.The family argument specifies the address family; it defaults to AF_INET.The type argument specifies
  whether this is a stream (SOCK_STREAM, this is the default) or datagram (SOCK_DGRAM) socket.The protocol argument defaults to 0,
  specifying the default protocol.Keyword arguments are accepted.
  根据上面的英文解释:
  family 默认值为 AF_INET 通常为默认即可
  type   选择SOCK_STREAM(TCP) 和 SOCK_DGRAM(UDP) ,默认值为SOCK_STREAM
  proto 默认为0 ,通常我们不用关系
  建立一个tcp 的 socket 实例
In : import socket  
In : s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  OK,有了socket 实例,就可以使用很多socket 实例的方法了。这些方法有那些呢?
  |Methods of socket objects (keyword arguments not allowed):
  |
  |accept() -- accept a connection, returning new socket and client address
  |bind(addr) -- bind the socket to a local address
  |close() -- close the socket
  |connect(addr) -- connect the socket to a remote address
  |connect_ex(addr) -- connect, return an error code instead of an exception

  |dup() -- return a new socket object>  |fileno() -- return underlying file descriptor
  |getpeername() -- return remote address
[*]
  |getsockname() -- return local address
  |getsockopt(level, optname[, buflen]) -- get socket options
  |gettimeout() -- return timeout or None
  |listen(n) -- start listening for incoming connections
  |makefile(]) -- return a file object for the socket
[*]
  |recv(buflen[, flags]) -- receive data
  |recv_into(buffer[, nbytes[, flags]]) -- receive data (into a buffer)
  |recvfrom(buflen[, flags]) -- receive data and sender's address
  |recvfrom_into(buffer[, nbytes, [, flags])
  |    -- receive data and sender's address (into a buffer)
  |sendall(data[, flags]) -- send all data
  |send(data[, flags]) -- send data, may not send all of it
  |sendto(data[, flags], addr) -- send data to a given address
  |setblocking(0 | 1) -- set or clear the blocking I/O flag
  |setsockopt(level, optname, value) -- set socket options
  |settimeout(None | float) -- set or clear the timeout
  |shutdown(how) -- shut down traffic in one or both directions
  2、设定并获取默认的套接字超时时间
In : import socket  
In : s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  
In : s.gettimeout()# 此时gettimeout 返回None,说明这个socket 没有设置超时处理
  

  
In : s.settimeout(60)
  
In : s.gettimeout()
  
Out: 60.0
  3、修改、查看套接字属性 setsockopt 和 getsockopt
  通过socket 实例 s获取 的帮助如下:
  help(s.setsockopt)
  setsockopt(...) method of socket._socketobject instance
  setsockopt(level, option, value)
  Set a socket option.See the Unix manual for level and option.The value argument can either be an integer or a string.
  help(s.getsockopt)
  getsockopt(...) method of socket._socketobject instance
  getsockopt(level, option[, buffersize]) -> value
  Get a socket option.See the Unix manual for level and option. If a nonzero buffersize argument is given, the return value is a string of that length; otherwise it is an integer.
  其中 setsockopt 和getsockopt 中的 level、option 参数需要查看uninx 帮助手册去和获得相关信息
  man 7 socket 可以看到相应的相关信息,其中有一段如下信息:
  Socket options
  The socket options listed below can be set by using setsockopt(2) and read with getsockopt(2) with the socket level set to SOL_SOCKET for all sock‐ets.Unless otherwise noted, optval is a pointer to an int.
  介绍了 level 为 SOL_SOCKET 以及响应的option 字段信息
  Example: 设置socket 发送和接收的缓冲区大小
In : import socket  
In : s = socket.socket(socket.AF_INET,socket.SOL_SOCKET)
  
In : s.getsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF)
  
Out: 87380
  
In : s.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF)
  
Out: 16384
  
In : s.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,4096)
  
In : s.getsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF)
  
Out: 8192
  
In : s.setsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF,4096)
  
In : s.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF)
  
Out: 8192
  4、把套接字改成阻塞或非阻塞模式
  默认情况下,TCP套接字处于阻塞模式中。也就是说,除非完成了某项操作,否则不会把控制权交还给程序
  通过socket 实例 s获取 的帮助如下:
  setblocking(...) method of socket._socketobject instance
  setblocking(flag)
  Set the socket to blocking (flag is true) or non-blocking (false).
  setblocking(True) is equivalent to settimeout(None);
  setblocking(False) is equivalent to settimeout(0.0)
In : s = socket.socket(socket.AF_INET,socket.SOL_SOCKET)  
In : s.setblocking(True)
  5、重用套接字地址
  不管连接是被有意还是无意关闭,有时你想始终在同一个端口上运行套接字服务器。某些情
  况下,如果客户端程序需要一直连接指定的服务器端口,这么做就很有用,因为无需改变服务器
  端口
  Example:
#! /usr/bin/env python  
# _*_ coding: utf-8 _*_
  

  
import socket
  
import sys
  

  
def reuse_socket_addr():
  
    sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  
    old_state = sock.getsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR)
  
    print "Old sock state: %s" %old_state
  

  
    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)
  
    # 此时 socket 没有开启从用
  
    srv.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,0)
  
    srv.bind(('',local_port))
  
    srv.listen(1)
  
    print "Listening on port: %s " %local_port
  

  
    while True:
  
      try:
  
            conn, addr = srv.accept()
  
            print "Connected by %s:%s" %(addr,addr)
  
      except KeyboardInterrupt:
  
            break
  
      except socket.error,msg:
  
            print '%s' %msg
  

  
if __name__ == "__main__":
  
    reuse_socket_addr()
  运行后在另一个终端对8282 端口进行重复的 telnet 操作,看看会出现什么效果
$ python testsocketreuse.py  
Old sock state: 0
  
New sock state: 1
  
Listening on port: 8282
  
Connected by 127.0.0.1:52733
  退出后再次执行测试的 testsocketreuse.py 脚本
Old sock state: 0  
New sock state: 1
  
Traceback (most recent call last):
  
File "testsocketreuse.py", line 33, in <module>
  
    reuse_socket_addr()
  
File "testsocketreuse.py", line 19, in reuse_socket_addr
  
    srv.bind(('',local_port))
  
File "/usr/lib/python2.7/socket.py", line 224, in meth
  
    return getattr(self._sock,name)(*args)
  
socket.error: Address already in use
  此时发现端口已经被重用,无法再次执行,需要等待重用的端口资源释放后此可以执行成功。
  启用端口重用:
    # 此时 socket 没有开启从用  
    srv.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  此时,再反复的执行此脚本,则没有发生端口重用的现象。
页: [1]
查看完整版本: Python 中的 socket 模块