一、网络传输方式-TCP
1.TCP概念:
TCP简称传输控制协议,是一种面向连接,可靠的,基于字节流的传输通信协议。
TCP通信需要经过建立连接,数据传送,终止连接三个步骤。
2.TCP特点:
2.1 面向连接:
通信双方必须先建立连接才能进行数据传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
2.2 可靠传输:
<1>TCP采用应答机制:
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
<2>超时重传:
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
<3>错误校验:
由发送端计算,然后由接收端验证,其目的是为了检测数据在发送端到接收端之间是否有改动,如果接收方检测到校验和有差错,则直接丢弃这个数据包。
<4>流量控制和阻塞管理:
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
3.TCP的优缺点:
优点:
可靠,稳定
适合传输大量的数据
缺点:
数据传输速度慢
占用系统资源
4.TCP和UDP的区别:
<1>TCP面向连接; UDP是不面向连接;
<2>TCP提供可靠的数据传输,也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP不保证可靠的数据传输,容易出现丢包情况;
<3>TCP需要连接传输速度慢,UDP不需要连接传输速度快
<4>TCP不支持发广播;UDP支持发广播
<5>TCP对系统资源要求较多,UDP对系统资源要求较少。
<6>TCP适合发送大量数据,UDP适合发送少量数据
<7>TCP有流量控制,UDP没有流量控制
5.TCP使用场景:
对网络质量有要求的时候。比如:HTTP,HTTPS,FTP等文件传输协议;POP,SMTP等邮件传输协议。
6. UDP网络程序流程:
udp网络程序发送数据不需要建立连接
7.TCP网络程序流程:
tcp网络程序需要先通过3次握手建立连接
二、TCP客户端:
#!/usr/bin/python3
#-*-coding:utf-8-*-
import socket
# 设置服务器的ip和端口
ip_host = ("192.168.14.49", 7788)
if __name__ == '__main__':
# 创建套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
tcp_client_socket.connect(ip_host)
# 打印连接状态
print("连接成功...")
# 发送数据
conent = input(">")
tcp_client_socket.send(conent.encode())
# 接收数据
recv_data = tcp_client_socket.recv(1024)
print("接收到的数据:", recv_data.decode())
# 关闭连接
tcp_client_socket.close()
三、TCP服务端:
#!/usr/bin/python3
#-*-coding:utf-8-*-
# 导包
import socket
if __name__ == '__main__':
# 创建tcpServer的套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口
tcp_server_socket.bind(('',7788))
# 设置端口复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 设置监听
tcp_server_socket.listen(128)
# 获取客户端的连接
service_tcp_client, client_host = tcp_server_socket.accept()
# 打印客户端ip和地址
print("客户端:", client_host)
# 接手客户端的数据
recv = service_tcp_client.recv(1024)
print("客户端发送的数据:",recv.decode())
# 服务端回复客户端
service_tcp_client.send("服务端已收到你的数据...".encode())
# 关闭客户端的套接字
service_tcp_client.close()
# 关闭服务短的套接字
tcp_server_socket.close()
注意:
TCP连接断开后.端口不会立即释放,需要等待1~2分钟
# 设置程序退出后端口后立即释放,即端口复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
四、TCP注意点:
1.tcp服务器一般情况下都需要绑定端口号,否则客户端找不到这个服务器
2.tcp客户端一般不绑定端口号,使用随机生成的端口号即可
※3.tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的
4.当tcp客户端和服务端建立好连接才可以收发数据,udp是不需要建立连接,直接就可以发送数据
5.当一个tcp客户端和服务端连接成功后,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
6.listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的
7.关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。
8.关闭accept返回的套接字意味着这个客户端已经服务完毕
※9.当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
五、TCP实现FTP文件下载器
1.服务端:
# -*-coding:utf-8-*-
from socket import *
import os
if __name__ == '__main__':
# 创建套接字
tcp_server = socket(AF_INET, SOCK_STREAM)
# 设置端口
tcp_server.bind(('', 7788))
# 设置端口复用
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)
# 设置监听
tcp_server.listen(128)
while True:
# 获取客户端连接
tcp_client, client_host = tcp_server.accept()
print("当前来连接的客户端是:")
print(client_host)
# 获取文件名
file_name = tcp_client.recv(1024).decode('utf-8')
print(file_name)
# 传输文件
# print(os.path.exists(file_name))
if os.path.exists(file_name):
with open(file_name, 'rb') as f:
print(f)
while True:
f_data = f.read(1024)
print(f_data)
if f_data:
tcp_client.send(f_data)
else:
print("文件传输完成...")
break
else:
tcp_client.send("文件不存在...".encode("utf-8"))
# 关闭客户端流
tcp_client.close()
# 关闭服务端流
# tcp_server.close()
2.客户端:
#!/usr/bin/python3
#-*-coding:utf-8-*-
from socket import *
server_host = ("192.168.14.74", 7788)
if __name__ == '__main__':
# 创建套接字
tcp_client = socket(AF_INET, SOCK_STREAM)
# 连接服务器
tcp_client.connect(server_host)
file_name = input("输入要下载的文件名:")
tcp_client.send(file_name.encode("utf-8"))
with open(file_name, "wb") as f:
while True:
# 接收文件
recv_data = tcp_client.recv(1024)
print(len(recv_data))
if recv_data:
f.write(recv_data)
f.flush()
else:
print("下载完成...")
break
# 关流
tcp_client.close()
标志位:
SYN: 表示连接请求 ACK: 表示确认 FIN: 表示关闭连接 seq:表示报文序号 ack: 表示确认序号
小结:
1.第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
2.第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack (number )=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
3.第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
六、TCP的三次握手:
建立连接时产生三次握手
七、TCP的四次挥手:
在断开连接时发生四次挥手
小结:
1.第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送。
2.第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1。
3.第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送。
4.第四次挥手:Client收到FIN后,接着发送一个ACK给Server,确认序号为收到序号+1。