1、TCP介绍
TCP协议,传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。
TCP通信模型中,在通信开始之前,一定要先建立相关连接,才能发生数据。
TCP特点
- 面向连接
– 通信双方必须先建立连接才能进行数据的传输 - 可靠传输
– TCP采用发送应答机制
– 超时重传
– 错误校验
– 流量控制和阻塞管理
2、TCP与UDP区别总结
1、TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接:(TCP需要连接;UDP不需要连接)
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付(TCP无差错、不丢失、不重复、按序到达;UDP安全性差、可靠性差)
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。(UDP实时性好,工作效率高,应用于高速传输或广播)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信(TCP连接时一对一;UDP支持多种模式通信)
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
UDP通信
TCP通信
TCP客户端编程
服务器端:就是提供服务的一方,而客户端,就是需要被服务的一方
TCP客户端构建流程
1.创建socket
2.链接服务器
3.接收数据(最大接收1024个字节)
4.关闭套接字
"""TCP客户端构建流程
1.创建socket
2.链接服务器
3.接收数据(最大接收1024个字节)
4.关闭套接字
"""
import socket
def main():
# 创建套接字
tcp_socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP是字节流stream UDP是数据报DGRAM
# 连接
server_ip = '192.168.0.111'
server_port = 7788
tcp_socket_client.connect((server_ip, server_port))
# 接收数据
recv_data = tcp_socket_client.recv(1024) # 限制接收的字节数大小为1024B
print(recv_data.decode('gbk')) # 中文解码
# 发送数据
send_data = input("发送的信息:")
tcp_socket_client.send(send_data.encode('gbk'))
# 关闭
tcp_socket_client.close()
if __name__ == '__main__':
main()
运行效果如下:
上面需要注意的是协议类型一定要选择TCP服务器,因为我们写的是客户端程序,需要服务端进行接收和应答。
TCP服务端编程
1 socket创建套接字
2 bind绑定IP和port
3 listen使套接字变为可以被动链接
4 accept等待客户端的链接
5 recv/send接收发送数据
"""
TCP服务端
1 socket创建套接字
2 bind绑定IP和port
3 listen使套接字变为可以被动链接
4 accept等待客户端的链接
5 recv/send接收发送数据
"""
import socket
def main():
# 创建套接字
tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定
tcp_socket_server.bind(("192.168.0.111", 7788))
# 主动变被动
tcp_socket_server.listen(128) # 这里的128可以暂时理解为连接最多客户端 但是不是,默认值
# 等待客户端
'''accept源码 可以看出返回的是个元祖类型数据 所以下面代码需要相对应采用元祖接受返回值
accept() -> (socket object, address info)
'''
"""
new_client_socket
这个参数的含义是:当我们建立tcp服务端和客户端第一次的连接以后,此时会新建一个新的套接字new_client_socket专门用于后期的收发数据工作,
而最早创建的tcp_socket_server这个套接字是被用于服务后面其他的客户端与服务端的第一次连接,所以需要新创建一个套接字
最早创建的tcp_socket_server这个套接字就用于监听其他的客户端和服务端的第一次连接
client_address
这个参数的含义是:新创建的连接的客户端的ip地址
这两个参数也是返回值,所以接收也需要用元祖接收,使用两个变量
"""
new_client_socket, client_address = tcp_socket_server.accept()
# print(client_address)
# 采用新的套接字收发数据
recv_data = new_client_socket.recv(1024)
print(recv_data)
# 发送数据
new_client_socket.send("哈哈".encode("gbk"))
# 关闭两个套接字 (重要)
new_client_socket.close()
tcp_socket_server.close()
if __name__ == '__main__':
main()
new_client_socket
这个参数的含义是:当我们建立tcp服务端和客户端第一次的连接以后,
此时会新建一个新的套接字new_client_socket专门用于后期的收发数据工作;
而最早创建的tcp_socket_server这个套接字就用于监听其他的客户端和服务端的第一次连接
client_address
这个参数的含义是:新创建的连接的客户端的ip地址
这两个参数也是返回值,所以接收也需要用元祖接收,使用两个变量
TCP服务端为多个客户端服务
TCP通信的流程介绍如下:
import socket
def main():
# 创建套接字
tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定
tcp_socket_server.bind(("192.168.0.111", 7788))
# 可以为多个客户端服务,此为服务端
while True:
# 主动变被动
tcp_socket_server.listen(128) # 这里的128可以暂时理解为连接最多客户端 但是不是,默认值
new_client_socket, client_address = tcp_socket_server.accept()
while True:
# 采用新的套接字收发数据 服务端运行至这里会先阻塞
recv_data = new_client_socket.recv(1024)
print(recv_data)
# 当客户端连接发来数据、客户端断开连接两种情况会解阻塞
if recv_data.decode("gbk"): # 当服务端收到客户端发送的信息,且不为空时执行下一步
# 发送数据
new_client_socket.send("哈哈".encode("gbk"))
else:
break # 跳出当前while循环# 服务端没有收到客户端的数据或者发来的数据是空字节的情况下进行
# 关闭两个套接字
new_client_socket.close() # 此时的新套接字放在这里是对应在上面代码中创建的套接字,此时关闭的就是上面的创建的新的套接字
tcp_socket_server.close()
if __name__ == '__main__':
main()
当客户端连接发来数据、客户端断开连接两种情况会解阻塞
此时需要注意的是:目前编写的TCP客户端和服务端只能一对一进行收发数据,同时连接的时候,只有一台客户端能够与服务端收发数据,另一台会处于阻塞的状态,当第一台客户端断开连接时候,第二台之前发送的数据才会传输到服务端,才会被打印出来。如果要两台客户端同时与服务端进行收发数据,需要用到多线程的知识。
文件下载器实例编程
TCP文件下载器编程_客户端
# -*- encoding: utf-8 -*-
"""
@File : TCP文件下载器编程_客户端.py
@Time : 2020/1/9 21:41
@Author : chen
"""
"""
TCP客户端
1、创建套接字
2、连接服务器
3、发送文件名称需要下载的请求
4、接受服务端发送来的数据
5、保存文件
6、关闭套接字
"""
import socket
def main():
tcp_socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # tcp套接字创建
# 连接服务器
# server_port = int(input("server port:")) # 注意这里的int,需要强转成int类型
tcp_socket_client.connect(("192.168.0.111", 7777))
# 发送文件名称需要下载的请求
file_name = input("请输入文件名称:") # 此时可以在这里加上文件名称和文件类型,即demo.txt
tcp_socket_client.send(file_name.encode("gbk"))
recv_data = tcp_socket_client.recv(1024*1024) # 1024B*1024B=1M 大小的文件限制
# 判断文件内容是否为空
if recv_data: # 若文件为空字节,就不会保存该文件
# 保存文件
with open('接收到的文件:' + file_name, 'wb') as f: # 注意使用wb 和with语句
f.write(recv_data)
else:
print("文件名称%s不存在或者文件为空!" % file_name)
# 关闭套接字
tcp_socket_client.close()
if __name__ == '__main__':
main()
TCP文件下载器编程_服务端
# -*- encoding: utf-8 -*-
"""
@File : TCP文件下载器编程_服务端.py
@Time : 2020/1/9 22:16
@Author : chen
"""
"""
TCP服务端
1、创建套接字
2、绑定客户端ip,端口
3、主动变被动 listen
4、等待客户端的连接 accept
5、发送/接收数据
"""
import socket
def send_file_to_client(new_client_socket): # 注意传参的套接字是新创建的,不能使用第一次创建的
# 接收客户端发送来的文件名称
file_name = new_client_socket.recv(1024).decode("gbk")
file_content = b"" # 为了防止空文件或文件不存在而报错,需要提前申明一个空文件 且必须是一个字节类型文件
try: # 异常处理是为了防止文件名称不存在,或者文件是空字节
with open(file_name, 'rb') as f:
file_content = f.read()
except:
print("该文件名称%s不存在或者文件为空!" % file_name)
# 没有异常情况就发送文件内容
new_client_socket.send(file_content) # 为了防止空文件或文件不存在而报错,需要提前申明一个空文件
def main():
# 创建套接字
tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定 ""ip为空代表本机任意ip都可以
tcp_socket_server.bind(("", 7777))
# 主动变被动
tcp_socket_server.listen(128) # 这里的128可以暂时理解为连接最多客户端 但是不是,默认值
new_client_socket, client_address = tcp_socket_server.accept()
# 给客户端返回文件内容 创建新的方法执行
send_file_to_client(new_client_socket)
# 关闭两个套接字
new_client_socket.close()
tcp_socket_server.close()
if __name__ == '__main__':
main()
代码执行顺序如图所示: