美文网首页Python精选
初识python网络编程

初识python网络编程

作者: BerL1n | 来源:发表于2018-11-21 15:35 被阅读104次

    1.了解TCP/IP协议

    Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。而IP是给因特网的每一台联网设备规定一个地址。

    TCP/IP协议是主机接入互网以及接入互联网的两台机器通信的标准,是一個通信合同,比如有两台机器,A 主机 和 B 主机,它们两者之间只是根据合同上的标准来工作就可以啦。TCP/IP 有4层架构:

    image.png

    应用层

    运输层

    网络层

    网络接口层

    2.OSI七层架构

    七层模型,亦称OSI(Open System Interconnection)参考模型,是参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系。它是一个七层的、抽象的模型体,不仅包括一系列抽象的术语或概念,也包括具体的协议。

    分层:

    应用层 (Application):
    网络服务与最终用户的一个接口。

    协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

    表示层(Presentation Layer):
    数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层)

    格式有,JPEG、ASCll、DECOIC、加密格式等

    会话层(Session Layer):
    建立、管理、终止会话。(在五层模型里面已经合并到了应用层)

    对应主机进程,指本地主机与远程主机正在进行的会话

    传输层 (Transport):
    定义传输数据的协议端口号,以及流控和差错校验。

    协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层

    网络层 (Network):
    进行逻辑地址寻址,实现不同网络之间的路径选择。

    协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

    数据链路层 (Link):
    建立逻辑连接、进行硬件地址寻址、差错校验等功能。(由底层网络定义协议)

    将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。

    物理层(Physical Layer):
    建立、维护、断开物理连接。(由底层网络定义协议)

    3.TCP的三段握手和四次断开

    相对于SOCKET开发者,TCP创建过程和链接折除过程是由TCP/IP协议栈自动创建的.因此开发者并不需要控制这个过程.但是对于理解TCP底层运作机制,相当有帮助.

    TCP三次握手

    所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。

    三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息.在socket编程中,客户端执行connect()时。将触发三次握手。

    image

    第一次握手:
    客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。

    第二次握手:
    服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1。

    第三次握手.
    客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1

    TCP四次断开

    TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。

    image

    (1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
    (2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
    (3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
    (4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

    4.socket

    socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).
    说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

    注意:其实socket也没有层的概念,它只是一个facade设计模式的应用,让编程变的更简单。是一个软件抽象层。在网络编程中,我们大量用的都是通过socket实现的。

    Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。

    image.png

    TCP编程

    Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。

    TCP连接简图: 三次握手,数据传输,四次挥手

    image

    socket中TCP的三次握手建立连接详解

    我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:

    • 客户端向服务器发送一个SYN J
    • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
    • 客户端再向服务器发一个确认ACK K+1

    只有就完了三次握手,但是这个三次握手发生在socket的那几个函数中呢?请看下图:

    图1、socket中发送的TCP三次握手

    从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

    总结:客户端的connect在三次握手的第二次返回,而服务器端的accept在三次握手的第三次返回。

    socket中TCP的四次握手释放(断开)连接详解

    上面介绍了socket中TCP的三次握手建立过程,及其涉及的socket函数。现在我们介绍socket中的四次握手释放连接的过程,请看下图:

    图2、socket中发送的TCP四次握手断开

    图示过程如下:

    • 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;

    • 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;

    • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;

    • 接收到这个FIN的源发送端TCP对它进行确认。

    这样每个方向上都有一个FIN和ACK。

    5. Python3 网络编程

    Python 提供了两个级别访问的网络服务。:

    • 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
    • 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。

    什么是 Socket?

    Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。

    socket和file的区别:

    • file模块是针对某个指定文件进行【打开】【读写】【关闭】
    • socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
    image

    服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

    socket()函数

    Python 中,我们用 socket()函数来创建套接字,语法格式如下:

    socket.socket([family[, type[, proto]]])
    

    参数
    family: 套接字家族可以使AF_UNIX或者AF_INET
    type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM
    protocol: 一般不填默认为0.

    Socket 对象(内建)方法

    函数 描述
    - 服务端套接字
    s.bind() 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
    s.listen() 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
    s.accept() 被动接受TCP客户端连接,(阻塞式)等待连接的到来
    - 客户端套接字
    s.connect() 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
    s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    - 公共用途的套接字函数
    s.recv() 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
    s.send() 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
    s.sendall() 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
    s.recvform() 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
    s.sendto() 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
    s.close() 关闭套接字
    s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
    s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
    s.setsockopt(level,optname,value) 设置给定套接字选项的值。
    s.getsockopt(level,optname[.buflen]) 返回套接字选项的值。
    s.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
    s.gettimeout() 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
    s.fileno() 返回套接字的文件描述符。
    s.setblocking(flag) 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
    s.makefile() 创建一个与该套接字相关连的文件

    简单实例

    服务端

    我们使用 socket 模块的 socket 函数来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。

    现在我们可以通过调用 bind(hostname, port) 函数来指定服务的 port(端口)。

    接着,我们调用 socket 对象的 accept 方法。该方法等待客户端的连接,并返回 connection 对象,表示已连接到客户端。

    完整代码如下:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    # 文件名:server.py
    
    import socket               # 导入 socket 模块
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         # 创建 socket 对象
    host = socket.gethostname() # 获取本地主机名
    port = 12345               # 设置端口
    s.bind((host, port))        # 绑定端口
    
    s.listen(5)                 # 设置最大连接数,超过后排队,等待客户端连接
    while True:
        c, addr = s.accept()     # 建立客户端连接。
        print ("连接地址: %s" % str(addr))
    
        msg = '欢迎访问python教程!' + "\r\n"
    
        c.send(msg.encode('utf-8'))
        c.close()                # 关闭连接
    

    客户端

    接下来我们写一个简单的客户端实例连接到以上创建的服务。端口号为 12345。

    socket.connect(hosname, port ) 方法打开一个 TCP 连接到主机为 hostname 端口为 port 的服务商。连接后我们就可以从服务端后期数据,记住,操作完成后需要关闭连接。

    完整代码如下:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    # 文件名:client.py
    
    import socket               # 导入 socket 模块
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         # 创建 socket 对象
    host = socket.gethostname() # 获取本地主机名
    port = 12345                # 设置端口号
    
    s.connect((host, port))  # 连接服务,指定主机和端口
    
    msg = s.recv(1024)   # 接收小于 1024 字节的数据
    s.close()
    
    print (msg.decode('utf-8'))
    

    先执行server端,然后打开client端就能看到结果,再看server端就会显示ip地址

    client端:

    image.png

    server端:

    image.png

    实例二:

    服务端

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    # 文件名:server.py
    
    import socket               # 导入 socket 模块
    
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8000)) #绑定手机卡
    phone.listen(5)
    print('---------')
    while True:
        conn,addr=phone.accept()  #等电话
    
        print("电话线路是",conn)
        print("客户端的手机号是",addr)
        while True:  #通讯循环
            try:
                msg=conn.recv(1024)  #收消息
                if not msg:break
                print('客户端发来的消息是:',msg.decode('utf-8'))
                conn.send(msg.upper())#发消息
            except Exception:
                break
        conn.close()
    phone.close()
    

    客户端

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    # 文件名:client.py
    
    import socket               # 导入 socket 模块
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8000))
    while True:
        msg=input(">>>:").strip()
        if not msg: continue
        phone.send(msg.encode('utf-8'))  #发送给服务端的消息
        data=phone.recv(1024)
        print('收到服务端发来的消息',data.decode('utf-8'))
    phone.close()
    
    
    

    先开启服务端,再开启客户端,发送信息

    客户端信息:


    image.png

    服务端信息:


    image.png

    参考文档:
    https://www.cnblogs.com/jcchoiling/p/5929640.html
    https://www.cnblogs.com/baomanji/p/6802731.html
    https://www.cnblogs.com/zhangyux/p/6109284.html
    https://www.imooc.com/article/17052

    相关文章

      网友评论

        本文标题:初识python网络编程

        本文链接:https://www.haomeiwen.com/subject/fpfqaftx.html