2-创建TCP文件下载器

TCP简介

UDP的全称是User Datagram Protocol(用户数据报协议),只提供面向无连接的通信服务,而且也没有流量控制、重发纠错等机制,因此只能适用于包总量比较少的服务,例如DNS、SNMP,或者多媒体即时通信、LAN内通信和广播通信等对于信息完整性要求不高的场景。

TCP的全称是传输控制协议,不仅是面向连接的,防止流量的浪费,而且实现了数据传输的各种控制,例如丢包时的重发控制、次序混乱时的顺序控制。

问题:TCP如何保证可靠传输?

  1. 发送应答机制
  2. 超时重传机制
  3. 错误校验
  4. 流量控制和阻塞控制

UDP和TCP的区别

UDP通信模型,类似于写信,写信之前并不确定对方是否存在,因此是不可靠的
在这里插入图片描述

TCP模型类似于打电话,相当于建立了一条虚拟电路,通过应答机制保证对方接受到的信息是完整的,因此是可靠传输。

而且TCP模型中会严格区分服务器端和客户端,而UDP模型则并不会严格区分client和server,都是采用recvfrom和sendto。

TCP客户端

  1. 创建套接字
  2. 连接服务器
  3. 发送/接收数据
  4. 关闭套接字
import socket

def main():
    # 创建套接字
    tcp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 连接服务器
    server_ip = input("输入要连接的服务器的IP: ")
    server_port = int(input("输入要连接的服务器的port"))
    server_addr = (server_ip,server_port)
    tcp_client_socket.connect(server_addr)
    # 发送/接收数据
    send_data = input("输入要发送的数据")
    tcp_client_socket.send(send_data)
    # 关闭套接字
    tcp_client.close()

if __name__=="__main__":
    main()

TCP服务器端

  1. 创建监听套接字
  2. 监听套接字绑定ip和port(思考为什么)
  3. listen,将监听套接字变成被动接收
  4. accept,监听套接字等待客户端的连接
  5. 如果有客户端连接,创建服务套接字
  6. recv/send,服务套接字接收发送数据(思考是否还需要recv_addr)
  7. 服务完毕,服务套接字被释放

创建并将监听套接字设置为等待客户连接的过程很像买手机:

  1. 买手机——创建监听套接字
  2. 插手机卡——listen使监听套接字可以被动接受
  3. 设置手机状态可以接听——accept使监听套接字等待客户端连接

总之,监听套接字,负责等待新的客户端连接;服务套接字,可以为这个客户服务,这个过程也很像通信公司的呼叫中心。

按照这个思路,我们设计TCP服务器如下:

import socket
# 创建TCP服务器
def Tcp_server():
    # 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 绑定IP和port
    tcp_server_socket.bind("127.0.0.1",7788)
    # 让默认套接字由主动变被动
    tcp_server_socket.listen(128)
    # 等待客户端的连接_拥塞
    new_client_socket,client_addr = tcp_server_socket.accept()
    # 接收客户端发送来的数据,注意TCP的recv不需要addr,因为已经建立了连接
    recv_data = new_client_socket.recv(1024)
    print("接收来自%s的消息:%s"%(client_addr,recv_data))
    new_client_socket.send('okk'.encode('utf-8'))
    # 关闭服务套接字
    new_client_socket.close()

if __name__=="__main__":
    Tcp_server()

注意:tcp_server_socket,listen(128)表示监听套接字最多能够同时接收128个客户端的响应。

当然,TCP支持server端接收不定长的消息,因为recv解堵塞有两种情况,要么接收到数据,要么客户端关闭,此时接收到的信息为None,我们据此修改上面的代码:

# 支持不定长的消息的TCP server

import socket
# 创建TCP服务器
def Tcp_server():
    # 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 绑定IP和port
    tcp_server_socket.bind("127.0.0.1",7788)
    # 让默认套接字由主动变被动,最多接收128字节的数据
    tcp_server_socket.listen(128)
    while True:
        # 等待客户端的连接_拥塞
        new_client_socket,client_addr = tcp_server_socket.accept()
        while True:
            # 接收客户端发送来的数据
            recv_data = new_client_socket.recv(1024)
            # recv 解堵塞有两种情况:接收到消息/客户端关闭
            if recv_data is None:
                # recv_data is None, 表示客户端已关闭
                new_client_socket.close()
                break
            else:
                print("接收来自%s的消息:%s"%(client_addr,recv_data))
                new_client_socket.send('okk'.encode('utf-8'))

        # 关闭服务套接字
        new_client_socket.close()
        print("客户已经服务完毕")

    tcp_server_socket.close() # 理想情况下,服务器不会关闭

if __name__=="__main__":
    Tcp_server()

案例:基于TCP的文件下载器

我们基于上面讨论的内容,实现一个TCP文件下载器的client端和server端

client端

import socket

FILE_NAME = './download.txt'

def main():
    # 创建socket
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 连接服务器
    server_ip = input("输入服务器IP:")
    server_port = input("输入服务器PORT:")
    server = (server_ip,server_port)
    tcp_socket.connect(server)
    # 接收数据
    recv_data = tcp_socket.recv(1024)  # 1024-->1k, 1024*1024-->1M
    # 如果接收到数据在创建文件,否则不创建
    # 注意:接收到的数据一般是二进制的,直接写入二进制即可
    if recv_data:
        with open(FILE_NAME,'wb') as f:
            f.wirte(recv_data)
    else:
        print("没有接收到文件")
    # 关闭套接字
    tcp_socket.close()

if __name__=="__main__":
    main()

server端

import socket
# 服务器

DOWNLOAD_PATH = './download/'
def send_file_2_client(new_client_socket,client_addr):
    file_name = new_client_socket.recv(1024).decode("utf-8")
    print("%s要下载%s"%(client_addr,file_name))
    file_content = None
    # 打开文件
    try:
        f = open(DOWNLOAD_PATH+file_name) 
        file_content = f.read()
        f.close()
    except Exception as ret:
        print("没有要下载的文件")
    new_client_socket.send(file_content.decode("utf-8"))


def main():
    """服务器接收来自客户端的请求,并发送文件"""
    # 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 绑定本地信息
    tcp_server_socket.bind(("",7788))
    # 主动变被动
    tcp_server_socket.listen(128)
    # 等待
    client_socket,client_addr = tcp_server_socket.accept()
    # 接收要下载的文件名,并返回
    send_file_2_client(client_socket,client_addr)
    # 关闭套接字
    client_socket.close()

if __name__=="__main__":
    main()

TCP补充

  1. TCP服务器端一般情况都需要绑定IP和端口,而客户端一般不需要,因而一般来说是客户端主动连接服务器;
  2. tcp服务器端的监听套接字的listen是必须的,参数为监听套接字能够同时响应的客户端请求的最大数目;
  3. 客户端连接服务器时,需要用connect连接,不需要地址,这一点与UDP不同;
  4. 当客户端的套接字调用close后,服务器端的套接字也会解堵塞,并返回None,服务器端可据此判断客户端是否已经下线。

猜你喜欢

转载自blog.csdn.net/weixin_43721070/article/details/121754751