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

  • 相关阅读:
    Java 简单算法--打印乘法口诀(只使用一次循环)
    Java简单算法--求100以内素数
    ubuntu 16.04 chrome flash player 过期
    java 网络API访问 web 站点
    java scoket (UDP通信模型)简易聊天室
    leetcode1105 Filling Bookcase Shelves
    leetcode1140 Stone Game II
    leetcode1186 Maximum Subarray Sum with One Deletion
    leetcode31 Next Permutation
    leetcode834 Sum of Distances in Tree
  • 原文地址:https://www.cnblogs.com/xwjhyy/p/11694102.html
Copyright © 2011-2022 走看看