• Python网络编程


    thanks to http://www.liaoxuefeng.com

    网络中的各种协议

    1. IP协议:IP协议是重要的网络协议,对应了每台计算机的唯一标识:IP地址,实际上就是每台计算器链接网络的接口,通常是网卡。IP协议负责把数据从一台计算机通过网络传到另外一台计算机,数据被分割成小块,然后通过IP包(IP包包含数据、源IP地址和目标IP地址、源端口和目标端口),通过两台计算机之间连接的线路发送出去,但是IP协议并不保证数据准确到达,同时不保证顺序到达。

    2. TCP协议:TCP协议建立在IP协议之上,将两台计算机通过握手,建立可靠的连接,并且对每个IP包编号,确保数据顺序可靠到达,如果发生丢包,则自动重发。在TCP协议的基础上,有更多高级的协议:HTTP、SMTP等常见协议。

    3. UDP协议:UDP协议不需要建立可靠连接,直接发送包,但是不保证能够准确到达。UDP协议的优点就是速度快,在某一些不要求可靠达到的数据,可以使用UDP协议。

    TCP客户端编程

    建立网络连接的基础是创建一个socket,socket指定了目标计算机的IP地址和端口号,以及协议类型。

    在客户端发起TCP连接,需要指定socket的协议为TCP/IP协议:

    import socket
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 

    socket类在实例化时,接受4个参数:family=AF_INET,type=SOCK_STREAM,proto=0,_sock=None

    第四个参数_sock是一个_realsocket 对象,如果指定了该对象,则会从该对象中继承family、type、photo属性,并且继承delegate方法:("recv", "recvfrom", "recv_into", "recvfrom_into","send", "sendto”)。 _realsocket类是c实现的,所以只能看到头文件_realsocket.py。

    前三个参数依次为:地址协议、流协议、接口协议(?)

    创建了socket对象之后,通过connet方法建立连接:

    s.connet((‘www.baidu.com’),80)

    connet方法要求传入一个地址,如果是IP类的socket,则需要传入一个元组,包含地址(会被dns解析)和端口

    建立连接之后,就可以发送请求。TCP协议是双向连接的,双方都可以发送数据,但是具体发送的规则有更加高层的协议,如http协议决定(http协议规则:必须有客户端发送请求给服务器,服务器收到后才发数据给客户端):

    s.send('GET / HTTP/1.1 Host: www.baidu.com Connection: close ')

    如上发送一条HTTP 1.1协议请求,获取百度首页的内容,请求发送后,百度服务器就会响应请求,并且向我们发送首页的内容,通过recv方法接收服务器发来的信息,并且指定单次接收的最大字节数:

    buffer = []
    while True:
        d = s.recv(1024)
        if d:
            buffer.append(d)
        else:
            break
    data = ''.join(buffer)

    如上data中包含了从服务器中接收的全部数据,包括一个http头,和真正需要接收的网页本身,再发送请求时,我们以两个回车换行来分割http头和接收到的网页,所以将其从新分离即可得到百度首页的内容:

    header, html = data.split(' ', 1)

    将其写到文件中:

    with open('baidu.html', 'wb') as f:
        f.write(html)

    用浏览器打开baidu.html,即可看到百度首页的内容了

    总脚本如下:

    __author__ = 'liangzb'
    import socket
    s = socket.socket(family=2,type=socket.SOCK_STREAM)
    print type(s.family)
    s2 = socket.socket(_sock=s)
    print s2.family
    s.connect(('www.baidu.com',80))
    s.send('GET / HTTP/1.1 Host: www.baidu.com Connection: close ')
    buffer = []
    while True:
        d = s.recv(1024)
        if d:
            buffer.append(d)
        else:
            breakdata = ''.join(buffer)
    s.close()
    # print data
    header, html = data.split(' ', 1)
    # print header
    with open('baidu.html', 'w') as f:
        f.write(html)

    TCP服务端编程

    服务器和客户端的区别就是服务器需要接受来自不同客户端的请求,并且分别作出响应,为了实现该过程,需要先建立一个绑定,绑定服务器的ip地址和端口:

    s.bind(('127.0.0.1', 9999))

    之后,调用listen方法监听来自该端口消息,传入参数指定最大等待连接的数量:

    listen(max_connection)

    接下来,服务器应该保持运行状态,启用一个死循环来接收来自客户端的连接,accept方法可以接收来自客户端的连接,并且返回客户端的socket对象和地址。需要注意的是,对于每一个请求,都应该新开一条线程或者进程(win下只能开线程)来处理:

    while True:
        sock, addr = s.accept()
        t = threading.Thread(target=tcplink, args=(sock, addr))
        t.start()

    编写处理函数来响应请求:

    def tcplink(sock, addr):
        print 'Accept new connection from %s:%s...' % addr
        sock.send('Welcome!')
        while True:
            data = sock.recv(1024)
            time.sleep(1)
            if data == 'exit' or not data:
                break
            sock.send('Hello, %s!' % data)
        sock.close()
        print 'Connection from %s:%s closed.' % addr

    服务端总脚本如下:

    __author__ = 'liangzb'
    import threading
    import socket
    import time

    def tcplink(sock, addr):
        print 'Accept new connection from %s:%s...' % addr
        sock.send('Welcome!')
        while True:
            data = sock.recv(1024)
            time.sleep(1)
            if data == 'exit' or not data:
                break
            sock.send('Hello, %s!' % data)
        sock.close()
        print 'Connection from %s:%s closed.' % addr

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('127.0.0.1', 9999))
    s.listen(5)
    print 'Waiting for connection...'

    while True:
        sock, addr = s.accept()
        t = threading.Thread(target=tcplink, args=(sock, addr))
        t.start()

    可以通过另外一个客户端给该服务端发送消息来测试,注意,这里绑定的ip地址是127.0.0.1,是本地地址,只能是本机访问

    客户端程序参考:

    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('127.0.0.1', 9999))
    print s.recv(1024)
    for data in ['Michael', 'Tracy', 'Sarah']:
        s.send(data)
        print s.recv(1024)
    s.send('exit')
    s.close()

    源代码来自:http://www.liaoxuefeng.com/ ,thanks to liaoxuefeng for share

    运行效果:

    NewImage

    UDP编程

    协议是不确定连接可靠性的,socket流协议应该设置为:SOCK_DGRAM:

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

    由于UDP协议的特征,UDP的服务端在建立连接时,不需要调用listen方法,而是直接对接收到的请求作出回应,而客户端的信息,则又recvfrom方法返回的元组得到。在发送消息时,也只需要通过sendto方法,将客户端的地址作为第二个参数传入。

    而在UDP协议的客户端,同样不需要先经过连接,而是直接将准备好的请求发送到某一个地址,利用sendto方法,将服务端的地址作为第二个参数传入,其他接收方式则和TCP相同。

  • 相关阅读:
    解决Unsupported major.minor version 51.0问题的感悟
    python 自己实现for循环:
    去除(UTF8)格式文本中的Bom
    python range与xrange
    Permission denied: make_sock: could not bind to address处理
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    centos下postgresql的安装与配置 20101217 12:39:15
    android软键盘 android:windowSoftInputMode
    android 代码实现安装卸载apk
    Android有效解决加载大图片时内存溢出的问题
  • 原文地址:https://www.cnblogs.com/lyon2014/p/4541929.html
Copyright © 2020-2023  润新知