python全栈(一)网络通信与服务器之网络-tcp

前言

最近在学习网络与服务器,原因在于将要做一个“智能变电站管控系统”,虽然我的主打方向不在于服务端,也就是云端,但是觉得成为一个小小梦想的道路上,还是有必要科普学习一下,并且以实战产品为例来做,更贴合学以致用,不过,智能变电管控,目前自己方案已经拟定好,已经实现了一部分功能,只是用tk做的界面,现在想将其实现到web端深度学习的服务,等这个完工,我也将开源出来给大家,一来,可以分享给大家借鉴一下,二来,也希望得到大家的建议,来完善它。这个项目本是企业级别的,为啥开源,主要是自己推荐过,不过没得到上级领导的反馈,但又想这些东西变得有价值,也就分享给有需要一起的人吧!
现在写的网络通信部分博客,我将会不定期修改和完善博文,暂且写的比较粗略。于此同时,我将不定期更新瑞芯微RK3399Pro的开发过程的记录,望有志者一起加入交流学习。
----小小AI算法工程师一枚

一、概念简介

TCP通信模型

1.概念
tcp通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中的“打电话”。
TCP协议,即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。
TCP通信模型中,在通信开始之前,一定要先建立相关连接,才能发送数据。

TCP通信如下图:
在这里插入图片描述
在这里插入图片描述
2.TCP特点:
面向连接:
通信双方必须先建立连接才能进行数据的传输。
可靠传输:
TCP采用发送应答机制
超时重传:
启动定时器,在规定时间内未收到,则重新发送,直到收到。
错误校验
流量控制和阻塞管理
3.TCP和UDP的比较
TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接.
TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付.
UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
TCP对系统资源要求较多,UDP对系统资源要求较少。
TCP严格区分客户端和服务端,UDP不一定区分。

形象地说
UCP类比于写信,别人不一定能收到;
TCP类比于打电话,可以获知对方是否收到。

二、TCP客户端构建

客户端:是需要被服务的一方;
服务器端:就是提供服务的一方。
TCP客户端构建流程
1. 创建socket:socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2. 连接服务器:tcp_socket.connect((server_ip, server_port)):tcp3次握手的开始
3. 收发数据(最大接收2014个字节) :tcp_socket.send(send_data.encode(“utf-8”)),注意收发的数据需要编解码。
4. 关闭套接字: tcp_socket.close():tcp4次挥手的开始

01-tcp-客户端(client)

import socket

def main():
    # 1. 创建tcp的套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 链接服务器
    # tcp_socket.connect(("192.168.33.11", 7890))
    server_ip = input("请输入要链接的服务器的ip:")
    server_port = int(input("请输入要链接的服务器的port:"))
    server_addr = (server_ip, server_port)
    tcp_socket.connect(server_addr)  # connect是tcp三次握手的开始

    # 3. 发送数据/接收数据
    send_data = input("请输入要发送的数据:")
    tcp_socket.send(send_data.encode("utf-8"))  # 数据进行编码

    # 4. 关闭套接字
    tcp_socket.close()  # 客户端调用close()是4次挥手的开始

if __name__ == "__main__":
    main()

三、TCP服务端构建

TCP服务端构建过程:

  1. socket创建套接字:创建TCP监听套接字
  2. bind绑定IP和port(本机,即服务器ip和port)
  3. listen使套接字变为可以被动连接
  4. accept等待客户端的连接,创建服务套接字
  5. recv/send接收发送数据(注意发送和接受的数据编解码,windows为’gbk’, linux为“utf-8”)
  6. 关闭套接字(监听套机字和服务套接字)

02-tcp-服务器(server)

import socket

def main():
    # 1. 买个手机(创建套接字 socket)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 插入手机卡(绑定本地信息 bind)
    tcp_server_socket.bind(("", 7890))

    # 3. 将手机设置为正常的 响铃模式(让默认的套接字由主动变为被动 listen)
    tcp_server_socket.listen(128)

    print("-----1----")
    # 4. 等待别人的电话到来(等待客户端的链接 accept)
    new_client_socket, client_addr = tcp_server_socket.accept()  # tcp3次握手的成功
    print("-----2----")

    print(client_addr)

    # 接收客户端发送过来的请求
    recv_data = new_client_socket.recv(1024)
    print(recv_data)

    # 回送一部分数据给客户端
    new_client_socket.send("hahahghai-----ok-----".encode("utf-8"))

    # 关闭套接字
    new_client_socket.close()  # 服务套接字  tcp4次挥手的结束
    tcp_server_socket.close()  # 监听套接字

if __name__ == "__main__":
    main()

03-循环为多个客户端服务器
代码分析说明
服务器端代码tcp_server.accept()部分会阻塞,阻塞直到有客户端连接;
new_client_socket.recv()部分会阻塞,直到客户端有数据发送或者客户端断开连接解堵塞。

new_client_socket, client_addr = tcp_server_socket.accept()
accept() 默认为堵塞;返回值为,服务套接字以及被客户端(被服务)的(ip,port)。

import socket

def main():
    # 1. 买个手机(创建套接字 socket)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 插入手机卡(绑定本地信息 bind)
    tcp_server_socket.bind(("", 7890))

    # 3. 将手机设置为正常的响铃模式(让默认的套接字由主动变为被动 listen)
    tcp_server_socket.listen(128)

    while True:
        print("等待一个新的客户端的到来...")
        # 4. 等待别人的电话到来(等待客户端的链接accept)
        new_client_socket, client_addr = tcp_server_socket.accept()

        print("一个新的客户端已经到来%s" % str(client_addr))

        # 接收客户端发送过来的请求
        recv_data = new_client_socket.recv(1024)
        print("客户端福送过来的请求是:%s" % recv_data.decode("utf-8"))

        # 回送一部分数据给客户端
        new_client_socket.send("hahahghai-----ok-----".encode("utf-8"))

        # 关闭套接字
        # 关闭accept返回的套接字 意味着 不会在为这个客户端服务
        new_client_socket.close()
        print("已经服务器完毕。。。。")

    # 如果将监听套接字 关闭了,那么会导致 不能再次等待新客户端的到来,即xxxx.accept就会失败
    tcp_server_socket.close()

if __name__ == "__main__":
    main()

04-循环为多个客户端服务并且多次服务一个客户端

import socket

def main():
    # 1. 买个手机(创建套接字 socket)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 插入手机卡(绑定本地信息 bind)
    tcp_server_socket.bind(("", 7890))

    # 3. 将手机设置为正常的响铃模式(让默认的套接字由主动变为被动 listen)
    tcp_server_socket.listen(128)

    # 循环目的:调用多次accept,从而为多个客户端服务
    while True:
        print("等待一个新的客户端的到来...")
        # 4. 等待别人的电话到来(等待客户端的链接 accept)
        new_client_socket, client_addr = tcp_server_socket.accept()

        print("一个新的客户端已经到来%s" % str(client_addr))
        
        # 循环目的: 为同一个客户端服务多次
        while True:
            # 接收客户端发送过来的请求
            recv_data = new_client_socket.recv(1024)
            print("客户端福送过来的请求是:%s" % recv_data.decode("utf-8"))

            # 如果recv解堵塞,那么有2种方式:
            # 1. 客户端发送过来数据
            # 2. 客户端调用close(数据为空)导致而了,这里recv解堵塞
            if recv_data:
                # 回送一部分数据给客户端
                new_client_socket.send("hahahghai-----ok-----".encode("utf-8"))
            else:
                break

        # 关闭套接字
        # 关闭accept返回的套接字 意味着不会在为这个客户端服务
        new_client_socket.close()
        print("已经为这个客户端服务完毕。。。。")

    # 如果将监听套接字 关闭了,那么会导致 不能再次等待新客户端的到来,即xxxx.accept就会失败
    tcp_server_socket.close()

if __name__ == "__main__":
    main()

05-案例:文件下载-client
TCP客户端实现思路

  1. 创建套接字
  2. 连接服务器 – tcp3次握手的开始
  3. 获取下载文件名称
  4. 发送下载文件下载请求
  5. 接收服务端发送过来的数据
  6. 保存文件
  7. 关闭套接字 – tcp4次挥手的开始

tcp_client.py

import socket

def main():
    # 1. 创建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 获取服务器的ip port
    dest_ip = input("请输入下载服务器的ip:")
    dest_port = int(input("请输入下载服务器的port:"))

    # 3. 链接服务器
    tcp_socket.connect((dest_ip, dest_port))

    # 4. 获取下载的文件名字
    download_file_name = input("请输入要下载的文件名字:")

    # 5. 将文件名字发送到服务器
    tcp_socket.send(download_file_name.encode("utf-8"))

    # 6. 接收文件中的数据
    recv_data = tcp_socket.recv(1024)  # 1024--->1K  1024*1024--->1k*1024=1M 1024*1024*124--->1G

    if recv_data:
        # 7. 保存接收到的数据到一个文件中
        with open("[新]" + download_file_name, "wb") as f:
            f.write(recv_data)

    # 8. 关闭套接字
    tcp_socket.close()

if __name__ == "__main__":
    main()

06-案例:文件下载-server

TCP服务器端实现思路:

  1. 创建套接字(监听套接字)
  2. 绑定信息:bind(本机,即服务器)
  3. 主动变被动:listen()
  4. 等待客户端连接:accept()–> tcp3次握手的成功
  5. 收发数据(注意编解码)
  6. 关闭套接字(监听套机字和服务套接字)

tcp_server.py

import socket

def send_file_2_client(new_client_socket, client_addr):
    # 1. 接收客户端 需要下载的文件名
    # 接收客户端发送过来的 要下载的文件名
    file_name = new_client_socket.recv(1024).decode("utf-8")
    print("客户端(%s)需要下载文件是:%s" % (str(client_addr), file_name))

    file_content = None
    # 2. 打开这个文件,读取数据
    try:
        f = open(file_name, "rb")
        file_content = f.read()
        f.close()
    except Exception as ret:
        print("没有要下载的文件(%s)" % file_name)


    # 3. 发送文件的数据给客户端
    if file_content:
        # new_client_socket.send("hahahghai-----ok-----".encode("utf-8"))
        new_client_socket.send(file_content)

def main():
    # 1. 买个手机(创建套接字 socket)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 插入手机卡(绑定本地信息 bind)
    tcp_server_socket.bind(("", 7890))

    # 3. 将手机设置为正常的 响铃模式(让默认的套接字由主动变为被动 listen)
    tcp_server_socket.listen(128)

    while True:
        # 4. 等待别人的电话到来(等待客户端的链接 accept)
        new_client_socket, client_addr = tcp_server_socket.accept()

        # 5. 调用发送文件函数,完成为客户端服务
        send_file_2_client(new_client_socket, client_addr)

        # 6. 关闭套接字
        new_client_socket.close()
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

总结

  1. accept()(默认为堵塞)解堵塞:(意味着tcp三次握手的成功)
    服务器端代码tcp_server.accept()部分会阻塞,阻塞直到有客户端连接,同时创建服务套接字;
    new_client_socket, client_addr = tcp_server_socket.accept()
    accept() 默认为堵塞;返回值为,服务套接字以及被客户端(被服务)的(ip,port)。
    关闭accept返回的套接字意味着不会在为这个客户端服务.
    如果将监听套接字 关闭了,那么会导致不能再次等待新客户端的到来,即xxxx.accept就会失败.

  2. 如果recv(默认为堵塞)解堵塞,那么有2种方式:
    a. 客户端发送过来数据
    b. 客户端调用close断开连接(数据为空)导致而了,这里recv解堵塞。(意味着tcp4次挥手的开始)

四、tcp三次握手、四次挥手

tcp三次握手:双方准备资源
在这里插入图片描述
tcp四次挥手:双方释放资源
在这里插入图片描述
tcp三次握手、四次挥手”示意图如下:
全双工–> 收发
在这里插入图片描述
tcp三次握手:connect堵塞 三次握手之后 解堵塞,
客服端知道服务器准备好了,就是数值“+1”,同理服务端端知道客户器。
syn :请求, ack :应答
一般客户端先关,后关服务端。
四次挥手:close()。
谁先调用close()谁就会等待2~3min。
客户端不绑定端口,端口随机,服务器绑定端口。

五、tcp/ip协议

  • TCP/IP协议(族)
    早期的计算机网络,都是由各厂商自己规定一套协议,IBM、Apple和Microsoft都有各自的网络协议,互不兼容
    为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为了实现互联网这个目标,互联网协议族(Internet Protocol Suite)就是通用协议标准。
    因为互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议(族)
    ip一种是电脑上的地址,另外一种是协议。
    在这里插入图片描述
    各层功能的说明
    物理层:在设备之间传输比特流,规定了电平、速度和电缆针脚。
    数据链路层:主要将从物理层接收的数据进行MAC地址(网卡地址)的封装与解封,这一层的数据称为帧,在数据链路层工作的设备是交换机。
    网络层:提供逻辑地址,供路由器确定路径。
    网络层:将相邻层的数据进行IP地址的解封和封装,在这一层工作的设备是路由器,这一层的数据被称为数据包、报文。
    传输层:定义了一些传输数据的协议和端口,eg:TCP(传输控制协议,传输效率底,可靠性强),UDP(用户数据报协议,用于传输可靠性要求不高的数据),将下层数据进行分段传输,到达目的地址后再进行重组。这一层的数据叫做段。提供面向连接或非面向连接的数据传递以及进行重传前的差错检测。
    会话层:通过传输层建立数据传输的通路,主要在系统之间发起会话或者接收会话请求,(设备之间需要互相认识,可以是IP地址,MAC地址或者主机名)
    表示层:对接收的数据进行加解密,加解压缩。
    应用层:终端应用,与用户交互,提供接口和服务。
      
    ping命令路线:应用进程-ICMP-网络接口
    原始套机字:应用进程-IP
    在这里插入图片描述
    说明:
    • 网际层也称为:网络层
    • 网络接口层也称为:链路层

链路层:mac地址-网卡的序列号。
另外一套标准:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值