4.网络通信-TCP

网络通信-TCP

● TCP 面向连接、可靠的、基于字节流的传输控制协议

● TCP的特点

  • 面向连接
  • 可靠传输: 应答机制、超时重传、错误校验、流量管控

● TCP严格区分客户端、服务端

tcp网络程序-客户端

  • 实现步骤
    • 导入模块
    • 创建套接字
      socket.SOCK_STREAM
    • 建立连接
      tcp_client_socket.connect( ("服务端ip", 服务端端口) )
    • 发送数据
      tcp_client_socket.send(“内容”.encode())
    • 接收数据
      recv_data = tcp_client_socket.recv(1024)
      recv_data 是接收到的数据的二进制
    • 关闭连接
import socket

tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
address = ("localhost", 8888)
tcp_client_connect = tcp_client_socket.connect(address)
tcp_client_socket.send("hi".encode())

recv_data = tcp_client_socket.recv(1024)
print(recv_data.decode())

tcp_client_socket.close()

# [Remote IP 127.0.0.1 Port: 64760 ]
# hi
# 呵呵呵
666

tcp网络程序-服务端

  • 导入模块
  • 创建套接字
  • 绑定端口
  • 开启监听(把套接字由主动设置为被动模式)
    tcp_server_socket.listen(128) 最大允许128个连接
  • 等待客户端连接
    new_client_socket, client_ip_port = tcp_server_socket.accept()
    new_client_socket 新的套接字,只是服务当前的客户端
    client_ip_port 客户端ip和端口
  • 使用新的套接字接收客户端发送的信息
    new_client_socket.recv(1024)
  • 关闭新的套接字
    new_client_socket.close() 关闭和当前客户端的连接
  • 关闭服务器套接字
    tcp_server_socket.close() 服务器不再接收新的客户端,老客户端可以继续服务
import socket

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.bind(("", 8888))
tcp_server_socket.listen(128)  # windows有效,linux此数字无效

new_client_socket, client_ip_port = tcp_server_socket.accept()
new_client_socket.send("helloworld".encode())
print(f"当前客户端[%s]连接成功" % str(client_ip_port))

recv_date = new_client_socket.recv(1024)
recv_text = recv_date.decode()
print(f"接收到客户端[%s]的信息:%s" % (str(client_ip_port), recv_text))

new_client_socket.close()
tcp_server_socket.close()
当前客户端[('127.0.0.1', 56979)]连接成功
接收到客户端[('127.0.0.1', 56979)]的信息:hi

tcp网络程序-服务端增强

# 循环接收多条信息
import socket

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.bind(("", 8888))
tcp_server_socket.listen(128)  # windows有效,linux此数字无效

new_client_socket, client_ip_port = tcp_server_socket.accept()
new_client_socket.send("helloworld".encode())
print(f"当前客户端[%s]连接成功" % str(client_ip_port))

while True:
    # recv() 会让程序再次阻塞,收到信息后再接阻塞
    recv_data = new_client_socket.recv(1024)
    # 当接受到数据为 空 的时候,表示客户端已经断开连接了,服务端也要断开
    # if len(recv_data)!= 0:
    # b'xxxx'
    # 如果recv_data 非空即为真,否则为假
    if recv_data:
        recv_text = recv_data.decode()
        if recv_text != "exit":
            print("接收到[%s]的信息:%s" % (str(client_ip_port), recv_text))
        else:
            print("客户端已经断开连接!")
            break
    else:
        print("客户端已经断开连接!")
        break
new_client_socket.close()
tcp_server_socket.close()

# 当前客户端[('127.0.0.1', 54159)]连接成功
# 接收到[('127.0.0.1', 54159)]的信息:666
# 接收到[('127.0.0.1', 54159)]的信息:777
# 客户端已经断开连接!
# 当前客户端[('127.0.0.1', 58998)]连接成功
# 接收到[('127.0.0.1', 58998)]的信息:client2
# 客户端已经断开连接!
当前客户端[('127.0.0.1', 64526)]连接成功
接收到[('127.0.0.1', 64526)]的信息:666
接收到[('127.0.0.1', 64526)]的信息:777
客户端已经断开连接!
# 循环接受多个客户端连接
import socket

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.bind(("", 8888))
tcp_server_socket.listen(128)  # windows有效,linux此数字无效

while True:
    new_client_socket, client_ip_port = tcp_server_socket.accept()
    new_client_socket.send("helloworld".encode())
    print(f"新客户端[%s]连接成功" % str(client_ip_port))

    while True:
        # recv() 会让程序再次阻塞,收到信息后再接阻塞
        recv_data = new_client_socket.recv(1024)
        # 当接受到数据为 空 的时候,表示客户端已经断开连接了,服务端也要断开
        # if len(recv_data)!= 0:
        # b'xxxx'
        # 如果recv_data 非空即为真,否则为假
        if recv_data:
            recv_text = recv_data.decode()
            if recv_text != "exit":
                print("接收到[%s]的信息:%s" % (str(client_ip_port), recv_text))
            else:
                print("客户端已经断开连接!")
                break
        else:
            print("客户端已经断开连接!")
            break
    new_client_socket.close()
# tcp_server_socket.close()

# clent2的信息会阻塞到clent1客户端断开连接后才会接收

# 新客户端[('127.0.0.1', 60422)]连接成功
# 接收到[('127.0.0.1', 60422)]的信息:clent1
# 客户端已经断开连接!
# 新客户端[('127.0.0.1', 53496)]连接成功
# 接收到[('127.0.0.1', 53496)]的信息:clent2

案例-文件下载器

"""
目标:
D:/git-python/bak/hello.txt
下载到
D:/git-python/进阶/py/hello.txt
1、导入模块
2、创建套接字
3、建立连接
4、接收用户输入的文件名
5、发送文件名到服务端
6、创建文件,并且准备保存
7、接收服务端发送的数据,保存到本地(循环)
8、关闭套接字
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、建立连接
tcp_client_socket.connect(("localhost",8888))
# 4、接收用户输入的文件名
file_name = input("请输入要下载的文件名:\n")
# 5、发送文件名到服务端
tcp_client_socket.send(file_name.encode())
# 6、创建文件,并且准备保存
with open("D:/git-python/进阶/py/"+file_name, "wb") as file:
    # 7、接收服务端发送的数据,保存到本地(循环)
    while True:
        file_data = tcp_client_socket.recv(1024)
        if file_data:
            file.write(file_data)
        else:
            print(f"{file_name}文件下载成功")
            break
# 8、关闭套接字
tcp_client_socket.close()

# 请输入要下载的文件名:
# hello.txt
# hello.txt文件下载成功
"""
1、导入模块
2、创建套接字
3、绑定端口
4、设置监听,设置套接字由主动为被动
5、接受客户端连接
6、接收客户端发送的文件名
7、根据文件名读取文件内容
8、把读取的内容发送给客户端(循环)
9、关闭和当前客户端的连接
10、关闭服务器
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置套接字地址可以重用 setsockopt(当前套接字, 属性名, 属性值)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 3、绑定端口
tcp_server_socket.bind(("", 8888))
# 4、设置监听,设置套接字由主动为被动
tcp_server_socket.listen(128)

# 接收多客户端连接与多文件传输
while True:
    # 5、接受客户端连接
    new_client_socket, client_ip_port = tcp_server_socket.accept()
    print(f"新客户端[%s]连接成功" % str(client_ip_port))
    # 6、接收客户端发送的文件名
    file_name = new_client_socket.recv(1024).decode()
    #print(file_name)
    try:
        # 7、根据文件名读取文件内容
        with open("D:/git-python/bak/"+file_name, "rb") as file:
            # 8、把读取的内容发送给客户端(循环)
            while True:
                file_data = file.read(1024)
                # 判断是否读取到了文件末尾
                if file_data:
                    new_client_socket.send(file_data)
                else:
                    break
    except Exception as e:
        print(f"文件{file_name}下载失败!")
    else:
        print(f"{file_name}文件传输成功")
    # 9、关闭连接
    new_client_socket.close()
# tcp_server_socket.close()

# 新客户端[('127.0.0.1', 61574)]连接成功
# hello.txt文件传输成功
# 新客户端[('127.0.0.1', 59449)]连接成功
# orders.txt文件传输成功
# 新客户端[('127.0.0.1', 59452)]连接成功
# 文件dfdfd.txt下载失败!
新客户端[('127.0.0.1', 55539)]连接成功
文件dfdfdfd.txt下载失败!