• 网络编程之TCP三次握手与四次挥手、基于TCP协议的套接字编程


    TCP三次握手和四次挥手

    背景描述

    通过OSI七层网络模型中IP层的介绍,我们知道网络层,可以实现两个主机之间的通信。其实并不然,因为真正的实体是在主机的进程,是一个主机中的一个进程与另一个主机的进程在交换数据。

    IP协议只是把数据发送到目的主机,但是并没有交给主机的具体应用程序。而端到端的通信才是应用程序之间的通信。

    UDP:在传送数据时不需要进行连接,远地的主机在收到UDP报文后也不需要进行确认,虽然UDP不提供数据的可靠交付,但是这样会节省好多资源,开销,使得它的速度比较快,比如一些实时性要求较高的服务,经常使用UDP。对应的应用层的协议主要有DNS,TFTP,DHCP,SNMP,NFS等。

    TCP: 提供面向连接的服务,在传输数据之前,必须先建立连接,传输结束后释放连接。因此TCP是一种可靠的数据运输服务,但是这样就会增加不可避免的花销。对应的应用层的协议有,SMTP,TELNET,HTTP,FTP等。

    常用的熟知端口号

    应用程序 熟知端口 传输层协议
    FTP 21,20 TCP
    TFTP 69 UDP
    TELNET 23 TCP
    SMTP 25 TCP
    DNS 53 UDP
    HTTP 80 TCP
    SSH 22 TCP

    TCP概述

    TCP把连接作为最基本的对象,每一条TCP连接都有两个端点,这种端点我们叫做套接字(socket),它的定义为端口号拼接到IP地址即构成套接字,例如,若IP为192.3.4.26,而端口号为80,

    那么得到的套接字为192.3.4.26:80

    TCP连接的建立(三次握手)

    • 最开始的时候,客户端和服务端都是处于CLOSED关闭状态。主动打开连接的为客户端,被动打开的连接是服务器。如果要进入来连接,会经过如下步骤:
    1. TCP服务器进程先创建传输控制块TCP,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
    2. 客户端先创建传输控制块TCP,然后向服务器发连接请求报文,报文里包括SYN报头;
    3. 服务端接收到客户端传入的报文,如果同意连接,就会发出确认报文,此时报文中包括SYN和ACK报头;
    4. 客户端接收到服务端传来的报文,如果确认连接,就会返回给ACK报头的报文给服务端,此时客户端进入连接状态;
    5. 服务端接收到客户端传入的确认连接报文后,也进去连接状态,这样双发就建立了连接,可以进行通信了。

    TCP四次挥手

    • 数据传输完毕后,双方就可以释放连接。最开始的时候,客户端和服务端都处于ESTABLISHED(连接)状态,然后客户端主动关闭,服务端被动关闭。
    1. 客户端进程发出连接释放报文,并且停止发送数据。报文包括FIN报头;
    2. 服务端接收到包括FIN报头的释放报文,发出确认报文,ACK报头,但是此时服务端可能还会传输数据给客户端;
    3. 客户端接受到服务器返回的包含ACK报头的报文后,等待把最后的数据传输完毕;
    4. 数据传输完毕后,服务端就会像客户端发送报文,包含FIN,ACK报头,等待客户端确认;
    5. 客户端接收到服务端发送的释放连接报文请求后,如果要释放连接,就会返回给服务端一个ACK报头的报文,客户端进入等待状态。此时客户端的连接还没有释放,必须经过2MSL的时间后,才会释放连接;
    6. 服务端接收到客户端发送的包含ACK报头的报文,直接释放连接,进入CLOSDED(关闭)状态。

    如果已建立连接,客户端突然断开,会怎么办呢?

    TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

    基于TCP协议的套接字编程

    什么是Socket

    socket是应用层与TCP/IP协议通信的中间软件抽象层,他是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议隐藏在Socket接口后面。

    套接字发展史及分类

    套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。

    套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。

    基于文件类型的套接字家族

    套接字家族名字:AF_UNIX

    unix一切皆文件,基于文件的套接字调用就是底层的文件系统来取数据,两个套接字进程运行在同一个机器上,可以通过访问一个文件系统间接完成通信

    基于网络类型的套接字家族

    套接字家族名字:AF_INET

    套接字工作流程

    服务端:初始化Socket---->然后端口绑定(bind)---->对端口进行监听(listen)---->调用accept接受

    客户端:初始化Socket---->连接服务器(connect)

    如果客户端和服务端连接成功,则可以进行连接。客户端发送信息,服务端处理请求,然后再把数据传给客户端。

    import socket
    
    # socket_family:可以为AF_UNIX或AF_INET
    # socket_type:可以是SOCK_STREAM或SOCK_DGRAM
    # protocol:一般不填,默认为0
    socket.socket(socket_family, socket_type, protocal=0)
    
    # 获取tcp/ip套接字
    tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 获取udp/ip套接字
    udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    

    服务端套接字函数

    方法 用途
    bind() 绑定(主机,端口号)到套接字
    listen() 开始TCP监听
    accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

    客户端套接字函数

    方法 用途
    connect() 主动初始化TCP服务器连接
    connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

    公共用途的套接字函数

    方法 用途
    recv() 接收TCP数据
    send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    recvfrom() 接收UDP数据
    sendto() 发送UDP数据
    getpeername() 连接到当前套接字的远端的地址
    getsockname() 当前套接字的地址
    getsockopt() 返回指定套接字的参数
    setsockopt() 设置指定套接字的参数
    close() 关闭套接字

    面向锁的套接字函数

    方法 用途
    setblocking() 设置套接字的阻塞与非阻塞模式
    settimeout() 设置阻塞套接字操作的超时时间
    gettimeout() 得到阻塞套接字操作的超时时间

    面向文件的套接字函数

    方法 用途
    fileno() 套接字的文件描述符
    makefile() 创建一个与该套接字相关的文件

    基于TCP协议的套接字编程

    服务端

    # 服务端.py
    
    import socket
    
    # 1、初始化服务端
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    # 2、绑定端口
    server.bind(('127.0.0.1', 8000))
    
    # 3、建立监听
    server.listen(5)
    
    # 4、等待客户端连接
    print('start...')
    conn, client_addr = server.accept()
    
    # 5、通信
    data = conn.recv(1024)  # 接受信息的最大字节数,默认为1024个字节
    print(data.decode('utf-8')) # 数据传输的是二进制
    
    conn.send(data.upper())
    
    # 6、断开连接
    conn.close()
    
    # 7、关闭服务器
    server.close()
    

    客户端

    # 客户端.py
    
    from socket import *
    
    # 1、初始化客户端
    client = socket(AF_INET, SOCK_STREAM)
    
    # 2、连接服务器
    client.connect(('127.0.0.1', 8000))
    
    # 3、通信
    msg = input('请输入你想传输的信息>>>')
    client.send(msg.encode('utf-8'))  # 以二进制形式发送数据
    
    data = client.recv(1024)
    print(data)
    
    # 4、关闭连接
    client.close()
    
  • 相关阅读:
    C#数组的Map、Filter、Reduce操作
    Safari中的input、textarea无法输入的问题
    小练手:用HTML5 Canvas绘制谢尔宾斯基三角形
    Web前端页面的浏览器兼容性测试心得(三)总结一些IE8兼容问题的解决方案
    大杀器Bodymovin和Lottie:把AE动画转换成HTML5/Android/iOS原生动画
    Web前端页面的浏览器兼容性测试心得(二)搭建原汁原味的IE8测试环境
    Web前端页面的浏览器兼容性测试心得(一)搭建测试用本地静态服务器
    CSS3、SVG、Canvas、WebGL动画精选整理
    利用JS代码快速获得知网论文作为参考文献的引用文本
    使用Node.js快速搭建简单的静态文件服务器
  • 原文地址:https://www.cnblogs.com/Hades123/p/11099346.html
Copyright © 2020-2023  润新知