zoukankan      html  css  js  c++  java
  • day26

    Python 网络编程

      计算机网络就是把各个计算机连接起来,让网络中的的计算机可以相互通信,而网络编程就是在程序中实现计算机相互通信。

      举个例子,当你使用电脑访问微博时,你的电脑就和微博的某台服务器通过互联网连接起来了,服务器把网页上的内容作为数据通过互联网传输到你的电脑上。

      更具体一点,由于你电脑上同时也存在着和其他服务器的连接,如qq服务器,所以,更确切的说,网络通信,其实时两台计算机上的进程的通信

      网络编程对所有的编程语言都是一样的,就是在Python程序本身这个进程上,连接别的服务器进程的通信端口进行通信

      接下来作者介绍了Python网络编程的概念和最主要的两种网络类型的编程

    TCP/IP简介

    互联网和计算机网络

      计算机网络要比互联网出现早的多,虽然他们都是把多个计算机连在一块,但是早期的计算机网络的通信协议,每个厂商都有一个,互不兼容,而想要把计算机连在一起,就必须规定他们之间的通信协议,并且只有通信协议相同的才能进行通信,才能真正的连在一起。而互联网Internet由inter和net两个单词组成,本质上就是连接网络的网络。所以要想让所有的计算机连在一起,就必须指定一个统一的、通用的通信协议,这个协议就是互联网协议簇。

      互联网协议包含上百个不同的协议,而其中最重要的就是TCP/IP协议,所以互联网协议又被称为TCP/IP协议

    IP

      好比现实生活中写信需要知道收信人的地址外,计算机之间通信也需要知道对方的地址,而IP就是计算机的地址。

      IP地址形如127.0.0.1但它其实上是一个32位的整数(IPV4),为了方便阅读,就把他分割为4个8位一组的数组表示

      还有一种IP地址就是IPV6.IPv6地址实际上是一个128位整数,它是目前使用的IPv4的升级版,以字符串表示类似于2001:0db8:85a3:0042:1000:8a2e:0370:7334

      在互联网上每台计算机唯一的标识就是IP地址,如果一台计算机接入了两个 或者多个的计算机网络,比如路由器,那么他就有两个或者多个的IP地址。所以IP地址对应的就是计算机的网络接口,通常是网卡

    端口

      网络通信本质上是两台计算机上的两个进程之间的通信,一台计算机上存在着多个进程,如果仅知道对方的IP地址,当对方收到数据后,到底是把它给qq呢,还是给浏览器呢,这就需要端口来区别,每个网络程序都向操作系统申请一个唯一的端口号,这样,两个进程在两台计算机之间建立连接就需要知道IP地址和响应的端口号

      一个进程可以与多个计算机建立连接,因此一个进程可以有多个端口号

    IP协议

      IP协议负责把数据通过网络从一台计算机发送到另一台计算机。数据被切分成一块块,加上其他IP协议需要用到的直接,被打包成IP包发送出去。

      IP包的特点是按块发送,途径多个路由,不保证到达、不保证顺序到达

      由于网络链路的复杂,两台计算机之间可能存在着多条路径,因此路由器就决定如何把一个IP包转发出去。

      

    TCP协议

      TCP协议建立在IP协议上,负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议通过握手建立连接,然后对每个IP包编号,确保对方按顺序收到,如果没有收到,就重发。它保证数据传输的可靠性

      许多常用的更高级的协议都是即与TCP的,比如浏览器的HTTP协议,发送邮件的SMTP协议

      一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。

    TCP编程

      Socket是一个网络编程的抽象概念,表示”打开了一个网络连接”,而打开一个Socket需要知道目标计算机的IP地址和端口,然后指定相应的协议类型即可

      大多数连接使用的都是可靠的TCP连接,在TCP连接中,主动发起请求的称为客户端,被动响应的称为服务端,当客户端向服务端发起连接,如果一切顺利,服务端接受了请求,一个TCP连接就建立起来了

      客户端

      在客户端建立连接需要五步

      第一步:创建一个基于TCP协议的Socket

    # 导入socket库:
    import socket
     
    # 创建一个socket:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#<code class="hljs">AF_INET</code>指定使用IPv4协议,AF_INET6指定IPV6,<code class="hljs">SOCK_STREAM</code>指定使用面向流的TCP协议
    
    

      提供网页服务的服务器必须把端口号固定在80端口,因为80端口是web服务的标准端口,其他服务都有对应的标准端口号,例如SMTP服务是25端口,FTP服务是21端口,等等。端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。

      第二步:连接服务器

    s.connect(('www.sina.com.cn', 80))#注意中间的括号
    

      第三步:建立TCP连接后,我们就可以向新浪服务器发送请求,要求返回首页的内容:

    # 发送数据:
    s.send(b'GET / HTTP/1.1
    Host: www.sina.com.cn
    Connection: close
    
    ')
    

      TCP连接创建的是双向通道,双方都可以同时给对方发送数据,但是谁先发谁后发,怎么协调,会根据具体的协议指定,HTTP协议规定客户端必须先发请求给服务器,服务器收到后才发数据给客户端。

      发送的文本格式必须符合HTTP标准,如果格式没问题,接下来就可以接收新浪服务器返回的数据了:

      第四步:接受数据

    # 接收数据:
    buffer = []
    while True:
        # 每次最多接收1k字节:
        d = s.recv(1024)
        if d:
            buffer.append(d)
        else:
            break
    data = b''.join(buffer)
    

      第五步:当我们接收完数据后,调用close()方法关闭Socket,这样,一次完整的网络通信就结束了:

    # 关闭连接:``s.close()
    

      接收到的数据包括HTTP头和网页本身,我们只需要把HTTP头和网页分离一下,把HTTP头打印出来,网页内容保存到文件:

    header, html = data.split(b'
    
    ', 1)
    print(header.decode('utf-8'))
    # 把接收的数据写入文件:
    with open('sina.html', 'wb') as f:
        f.write(html)
    

      现在,只需要在浏览器中打开这个sina.html文件,就可以看到新浪的首页了。

    服务器

      服务器编程相对复杂一点

      需要绑定端口用来监听连接请求,需要位每个请求新开一个线程来处理

      服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。

      所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。

      但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。

      我们来编写一个简单的服务器程序,它接收客户端连接,把客户端发过来的字符串加上Hello再发回去。

    import socket,threading,time
    def main():
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#首先,创建一个基于IPv4和TCP协议的Socket:
        s.bind(('127.0.0.1',9999))#端口号需要预先指定。
        s.listen(5)#调用listen()方法开始监听端口,传入的参数指定等待连接的最大数量
        print('Waitting for connection...')
        while True:
            sock,addr=s.accept()#accept()会等待并返回一个客户端的连接:
            t=threading.Thread(target=tcplink,args=(sock,addr))
            t.start()
    def tcplink(sock,adds):
        print('Accept new connection form %s:%s'%adds)
        sock.send(b'Welcome')
        while True:
            data=sock.recv(1024)#之后所有的操作都由这个socket
            time.sleep(1)
            if not data or data.decode('utf-8')=='exit':
                break
            sock.send(('hello%s'%data.decode('utf-8')).encode('utf-8'))#这里send不加b''
        sock.close()
        print('connection from %s:%s closed'%addr)
    if __name__ == "__main__":
        main()
    

      我们要绑定监听的地址和端口。服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用0.0.0.0绑定到所有的网络地址,还可以用127.0.0.1绑定到本机地址。127.0.0.1是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。

      因为我们写的这个服务不是标准服务,所以用9999这个端口号。请注意,小于1024的端口号必须要有管理员权限才能绑定

      服务器程序通过一个永久循环来接受来自客户端的连接

      每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接

      要测试这个服务器程序,我们还需要编写一个客户端程序:

    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(('127.0.0.1',9999))
    # print(s.recv(1024).decode('utf-8'))#如果这里不接受会怎么样
    for data in [b'Michael', b'Tracy', b'Sarah']:
        # 发送数据:
        s.send(data)
        print(s.recv(1024).decode('utf-8'))
    s.send(b'exit')
    s.close()
    

      

      用TCP协议进行Socket编程在Python中十分简单,对于客户端,要主动连接服务器的IP和指定端口,对于服务器,要首先监听指定端口,然后,对每一个新的连接,创建一个线程或进程来处理。通常,服务器程序会无限运行下去。

    同一个端口,被一个Socket绑定了以后,就不能被别的Socket绑定了。

    UDP编程

      使用UDP协议时,不需要建立连接,知道目标计算机的IP地址和端口后,可以直接向目标计算机发送数据。它的优点时速度快,缺点时不能保证传输的可靠性

      使用UDP协议也需要区分客户端和服务端。

    客户端

      新建一个基于UDP协议的Socket对象,不需要与服务器建立连接,向服务器发送数据,接受服务器的数据,虽然不需要建立连接,但是需要关闭打开的Socket

    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    # s.connect(('127.0.0.1',9999))
    # print(s.recv(1024).decode('utf-8'))
    for data in [b'Michael', b'Tracy', b'Sarah']:
        # 发送数据:
        s.sendto(data,('127.0.0.1',9999))
        print(s.recv(1024).decode('utf-8'))
    s.close()
    

      

    服务端

      新建一个Socket对象、然后绑定IP地址和端口号。不需要LIsten()方法,可以直接接受数据。可以调用resvfrom(size)返回最多size大小的数据、和对方的地址。可以直接调用sentto()方法向对方发送数据

      多线程省去了

    import socket
     
    def main():
        s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        s.bind(('127.0.0.1',9999))
        while True:
            data,addr=s.recvfrom(1024)
            print('Recived from address%s:%s'%addr)
            s.sendto('Hello %s'%data,addr)
    

      

      此外,UDP的端口号和TCP的端口号不冲突,可以同时将UDP和TCP的端口申请为9999

  • 相关阅读:
    东边日出西边雨
    ZooKeeper学习(一)了解ZooKeeper
    linux学习(七)文件打包和压缩命令
    linux学习(六)Linux yum 命令
    linux学习(五)Linux 文件与目录管理
    linux学习(四)Linux 文件基本属性
    linux学习(三)Linux 系统目录结构
    linux学习(二)认识Linux
    linux学习(一)认识阿里云
    多线程实战【面试题形式】
  • 原文地址:https://www.cnblogs.com/xwjhyy/p/11694102.html
Copyright © 2011-2022 走看看