• Python socket


    Python的网络编程主要支持两种网络协议:TCP和UDP。这两种协议都通过叫Socket的编程抽象进行处理。Socket起源于Unix,是类似于文件的存在,可以像文件一样进行I/O、打开、关闭等操作,最主要的是它可以实现网络上不同主机的进程间通信,所以基本上Socket是任何一种网络通讯中最基础的内容。

    Python中建立一个套接字很简单:

    1
    2
    import socket
    = socket.socket(family, type)

    地址族

    family为地址族,该族指定要使用的网络协议,主要使用的有:

    • AF_INET:IPv4协议(TCP,UDP)
    • AF_INET6:IPv6协议(TCP,UDP)
    • AF_UNIX:UNIX域协议,用于同一台机器的进程间通讯

    套接字类型

    type为套接字类型,指定给定的协议组中使用的通信类型:

    • SOCK_STREAM:用于TCP
    • SOCK_DGRAM:用于UDP

    TCP和UDP都是基于Client/Server的编程模型,所以Socket编程也分为客户端和服务器端,以TCP为例:

    TCP客户端编程

    要获取远程主机的ip地址,可以使用socket标准库提供的gethostbyname()方法:

    1
    2
    3
    >>> import socket
    >>> socket.gethostbyname('www.baidu.com')
    '115.239.211.112'

    socket套接字实例s可用于客户端的方法有以下几个:

    • s.connect(addr):连接服务器端套接字。addr格式取决于地址族,对于IPv4来说,是一个包含ip地址与端口的元组,(host, port)。连接失败会报socket.error错误。
    • s.sendall(string):尝试发送所有数据,成功则返回None,失败则报异常。
    • s.recv(bufsize):接收数据,bufsize指定接收的最大数据量。
    • s.close():关闭套接字

    OK,现在可以用socket向远程主机发送一个HTTP GET请求了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # -*- coding: utf-8 -*-
    import socket
     
    = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #建立套接字
    host = 'www.baidu.com'
    port = 80
    ip = socket.gethostbyname(host)  #获取ip
    s.connect((ip, port))  #建立连接
    message = 'GET / HTTP/1.1 '
    s.sendall(message)  #发送GET请求
    = s.recv(4096)    #接收数据
    print r
    s.close()    #关闭套接字

    返回:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    HTTP/1.1 302 Moved Temporarily
    Date: Wed, 10 Jan 2018 18:56:45 GMT
    Content-Type: text/html
    Content-Length: 225
    Connection: Keep-Alive
    Location: http://www.baidu.com/search/error.html
    Server: BWS/1.1
    X-UA-Compatible: IE=Edge,chrome=1
    BDPAGETYPE: 3
    Set-Cookie: BDSVRTM=0; path=/

    下面我们可以实现自己的服务器。

    TCP服务器端编程

    Socket实例与服务器端编程有关的方法有以下几个:

    • s.bind(addr):addr也是(host, port)形式的元组,将套接字绑定到特定的地址和端口上。空字符串表示任意地址,'broadcast'可以用做发送广播信息。
    • s.listen(backlog):开始监听连接,backlog为最大挂起连接次数。
    • s.accept:返回元组(conn,addr),conn为新的套接字,可以用来发送和接收数据。addr是客户端的套接字地址。
    • s.recv()、s.sendall()和s.close()与客户端同。

    现在写一个将客户端发送来的信息发送回去的服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # -*- coding: utf-8 -*-
    import socket
    import sys
      
    HOST = ''  
    PORT = 8088
     
    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(5)
    print '开始监听'
    conn, addr = s.accept()
    print 'Connected with ' + addr[0+ ':' + str(addr[1])
    data = conn.recv(1024)
    conn.sendall(data)
    conn.close()
    s.close()

    运行:

    1
    2
    >>>
    开始监听

    服务器开始监听连接了。修改一下刚才写的客户端程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # -*- coding: utf-8 -*-
    import socket
     
    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    host = 'localhost'
    port = 8088
    s.connect((host, port))  #建立连接
    message = 'GET / HTTP/1.1 '
    s.sendall(message)  #发送GET请求
    = s.recv(4096)    #接收数据
    print r
    s.close()    #关闭套接字

    运行,连接本地的服务器,服务器端输出:

    1
    2
    3
    >>>
    开始监听
    Connected with 127.0.0.1:60933

    连接成功。客户端输出:

    1
    2
    >>>
    GET / HTTP/1.1

    发送的消息被返回了。

    这就是一个最简单的服务器了。上述服务器只能处理一次连接,这显然不是我们想看到的,保持一直运行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # -*- coding: utf-8 -*-
    import socket
    import sys
      
    HOST = ''  
    PORT = 8088
     
    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(5)
    print '开始监听'
    while True:
        conn, addr = s.accept()
        print 'Connected with ' + addr[0+ ':' + str(addr[1])
        data = conn.recv(1024)
        conn.sendall(data)
        conn.close()
    s.close()

    现在就可以使用客户端无限连接了:

    1
    2
    3
    4
    5
    6
    >>>
    开始监听
    Connected with 127.0.0.1:61240
    Connected with 127.0.0.1:61242
    Connected with 127.0.0.1:61245
    Connected with 127.0.0.1:61250

    服务器端多线程处理连接

    现在服务器端虽然可以处理无限多个连接,但只能一个一个的处理,后面的客户端连接只能等待前面的连接完成才能发送数据。要同时处理多个连接,可以使用多线程。服务器端接收到新的连接后,开启一个线程处理新连接,主线程去建立下一个连接。

    服务器端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # -*- coding: utf-8 -*-
    import socket
    import threading
     
    HOST = ''  
    PORT = 8088
     
    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(5)
    print '开始监听'
     
    def runThread(conn):
        data = conn.recv(1024)
        print data
        conn.sendall(data)
        conn.close()
     
    while True:
        conn, addr = s.accept()
        print 'Connected with ' + addr[0+ ':' + str(addr[1])
        = threading.Thread(target=runThread, args=(conn,))
        t.daemon = True
        t.start()

    客户端启动多个连接:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # -*- coding: utf-8 -*-
    import socket
    import time
    import threading
     
    def run():
        = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        host = 'localhost'
        port = 8088
        s.connect((host, port))
        message = 'GET / HTTP/1.1 '
        s.sendall(message)
        print s.recv(4096)   
        s.close()
     
                     
    if __name__ == '__main__':
        for in xrange(4):
            = threading.Thread(target=run)
            t.start()

    运行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    开始监听
    Connected with 127.0.0.1:61772
    GET / HTTP/1.1
     
    Connected with 127.0.0.1:61773
    GET / HTTP/1.1
     
    Connected with 127.0.0.1:61774
    GET / HTTP/1.1
     
    Connected with 127.0.0.1:61775
    GET / HTTP/1.1

    UDP编程

    UDP与TCP的不同之处在于UDP是不用建立连接的。

    在此需要使用s.recvfrom()与s.sendto()方法,前者与s.recv()相同,但返回(data, addr)的元组,addr为数据发送端的套接字地址,后者发送数据时需要加入要发送的远程地址。

    服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # -*- coding: utf-8 -*-
    import socket
     
    = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind(('', 10000))
    while True:
        data, addr = s.recvfrom(1024)
        print '接收到%s的连接'%str(addr)
        s.sendto(data, addr)

    客户端:

    1
    2
    3
    4
    5
    6
    7
    8
    # -*- coding: utf-8 -*-
    import socket
     
    = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.sendto('Hello World', ('localhost'10000))
    r, addr = s.recvfrom(1024)
    print r
    s.close()

    运行:

    1
    2
    3
    4
    >>>
    接收到('127.0.0.1'64112)的连接
    >>>
    Hello World

      

  • 相关阅读:
    CentOS7 安装 Mysql 服务
    git 第一次 push 遇到问题
    为什么PHP(CLI)同一个错误信息会打印两次?
    python密码输入模块getpass
    Linux安装JDK详细步骤
    嘿嘿嘿,开始自学mysql
    Bable实现由ES6转译为ES5
    AJAX
    模板层
    lshw查看系统硬件信息
  • 原文地址:https://www.cnblogs.com/itrena/p/8289601.html
Copyright © 2020-2023  润新知