tcp 图解:
服务端 tcp_server.py
import socket
if __name__ == '__main__':
# 创建服务端套接字,这个套接字服务于整个程序
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定ip和端口
server_socket.bind(("", 5000))
# 设置监听,将主动套接字改为被动套接字,被动套接字只能接受客户端的请求,只能收消息,不能发
消息
# 128 最大的连接数,单线程的情况只允许连接一个客户端
server_socket.listen(128)
#1.SOL_SOCKET表示当前套接字
#2.SO_REUSEADDR 表示端口释放选项
#2.True 表示打开断开释放,一个套接字连接结束后,会释放端口,以免端口被占用
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
# 没有客户端连接的时候,程序会在这里阻塞,一直等待客户端的连接请求(3次握手)
# 客户端连接时会返回一个新的套接字new_socket,这个新的套接字就负责与这个客户端通信
# ip_port表示请求连接的客户端
new_socket, ip_port = server_socket.accept()
# 代码执行到这里,就说明客户端已经连接,会解阻塞
# 接受客户端发的消息,返回二进制数据,需要解码,不会返回ip地址和端口
recv_data = new_socket.recv(1024)
print(recv_data.decode("utf-8"))
new_socket.send("我是tom".encode("utf-8"))
#关闭新套接字 说明此时服务端与这个客户端通信结束,再次连接会再次生成新的套接字
new_socket.close()
# 关闭全局套接字,服务端停止程序
server_socket.close()
服务端 tcp_client.py
import socket
if __name__ == '__main__':
# 创建套接字,AF_INET代表使用的是IPV4,SOCK_DGRAM代表是tcp传输
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
data = input("请输入你要发送的数据")
# 连接服务端
s.connect(("192.168.2.106",5000))
s.send(data.encode("utf-8"))
# 接受数据会阻塞,1024表示接受的最大字节数1024,recv_data是返回的二进制内容,不会返回端口
recv_data= s.recv(1024)
print("接受的内容content:%s" % recv_data.decode("utf-8"))
s.send("我是tom".encode("utf-8"))
s.close()
TCP特点
1. 面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这一个连接进行。
完成数据交换后,双方必须断开此连接,以释放系统资源。
这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
2. 可靠传输
1)TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
2)超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
3)错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
4) 流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
TCP与UDP的不同点
- 面向连接(确认有创建三方交握,连接已创建才作传输。)
- 有序数据传输
- 重发丢失的数据包
- 舍弃重复的数据包
- 无差错的数据传输
- 阻塞/流量控制
特别注意:当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线