设为首页 收藏本站
查看: 537|回复: 0

[经验分享] Python 中的 socket 模块

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-6-1 08:51:36 | 显示全部楼层 |阅读模式
本文参考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

    1
    2
    3
    In [1]: import socket
    In [2]: socket.gethostname()
    Out[2]: 'God'



    2、gethostbyname() -- map a hostname to its IP number

    1
    2
    3
    4
    5
    6
    7
    8
    In [6]: import socket
    In [8]: hostname=socket.gethostname()
    In [9]: print hostname
    God
    In [10]: socket.gethostbyname(hostname)
    Out[10]: '127.0.1.1'
    In [11]: socket.gethostbyname('www.baidu.com')
    Out[11]: '119.75.218.70'



    3、gethostbyaddr() -- map an IP number or hostname to DNS info

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    In [12]: import socket
    In [13]: socket.gethostbyaddr('God')
    Out[13]: ('God', [], ['127.0.1.1'])

    In [14]: 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: [Errno 2] 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

    1
    2
    3
    4
    5
    In [1]: import socket
    In [2]: socket.getservbyname('http')
    Out[2]: 80
    In [4]: socket.getservbyport(21)
    Out[4]: '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)


    1
    2
    3
    4
    5
    In [20]: add = '127.0.0.1'
    In [21]: packed_addr = socket.inet_aton(add)
    In [23]: addr2 = socket.inet_ntoa(packed_addr)
    In [24]: addr == addr2
    Out[24]: 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地址是否有效
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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

    注意:以上四个函数的参数全部都为数字,返回结构也全部都为数字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    In [11]: data = 1234
    In [19]: print 'data:%s\nntohs:%s\nntohl:%s' %(data,socket.ntohs(data),socket.ntohl(data))
    data:1234
    ntohs:53764
    ntohl:3523477504

    In [20]: 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([family[, type[, proto]]]) -> 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 实例

    1
    2
    In [6]: import socket
    In [7]: 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 identical to the current one

  • |  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([mode, [bufsize]]) -- 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、设定并获取默认的套接字超时时间

    1
    2
    3
    4
    5
    6
    7
    In [6]: import socket
    In [7]: s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    In [9]: s.gettimeout()  # 此时gettimeout 返回None,说明这个socket 没有设置超时处理

    In [10]: s.settimeout(60)
    In [11]: s.gettimeout()
    Out[11]: 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 发送和接收的缓冲区大小
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    In [1]: import socket
    In [3]: s = socket.socket(socket.AF_INET,socket.SOL_SOCKET)
    In [4]: s.getsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF)
    Out[4]: 87380
    In [5]: s.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF)
    Out[5]: 16384
    In [6]: s.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,4096)
    In [7]: s.getsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF)
    Out[7]: 8192
    In [8]: s.setsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF,4096)
    In [9]: s.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF)
    Out[9]: 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)

    1
    2
    In [2]: s = socket.socket(socket.AF_INET,socket.SOL_SOCKET)
    In [3]: s.setblocking(True)




    5、重用套接字地址
    不管连接是被有意还是无意关闭,有时你想始终在同一个端口上运行套接字服务器。某些情
    况下,如果客户端程序需要一直连接指定的服务器端口,这么做就很有用,因为无需改变服务器
    端口
    Example:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #! /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[0],addr[1])
            except KeyboardInterrupt:
                break
            except socket.error,msg:
                print '%s' %msg

    if __name__ == "__main__":
        reuse_socket_addr()



    运行后在另一个终端对8282 端口进行重复的 telnet 操作,看看会出现什么效果
    1
    2
    3
    4
    5
    $ python testsocketreuse.py
    Old sock state: 0
    New sock state: 1
    Listening on port: 8282
    Connected by 127.0.0.1:52733




    退出后再次执行测试的 testsocketreuse.py 脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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: [Errno 98] Address already in use



    此时发现端口已经被重用,无法再次执行,需要等待重用的端口资源释放后此可以执行成功。

    启用端口重用:
    1
    2
        # 此时 socket 没有开启从用
        srv.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)




    此时,再反复的执行此脚本,则没有发生端口重用的现象。


  • 运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
    2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
    3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
    4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
    5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
    6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
    7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
    8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

    所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-72632-1-1.html 上篇帖子: Python 中有关中文编码解码先关 下篇帖子: awk取出指定某天的慢日志(mysql)并使用工具分析
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    扫码加入运维网微信交流群X

    扫码加入运维网微信交流群

    扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

    扫描微信二维码查看详情

    客服E-mail:kefu@iyunv.com 客服QQ:1061981298


    QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


    提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


    本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



    合作伙伴: 青云cloud

    快速回复 返回顶部 返回列表