Python TCP 编程
1. TCP 介绍
2. TCP Socket 代码示例
1. TCP 介绍
详见《TCP:与 UDP 区别、三次握手、四次挥手、Socket 编程》。
TCP 与 UDP 在通信模型上的区别
UDP 通信模型
UDP 通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中的“写信”。
TCP 通信模型
TCP 通信模型中,在通信开始之前,一定要先建立相关的连接,才能发送数据,类似于生活中的"打电话"。
2. TCP Socket 代码示例
TCP 服务器
在生活中,如果想让别人能更够打通咱们的电话获取相应服务的话,需要做以下几件事情:
- 买个手机
- 插上手机卡
- 设计手机为正常接听状态(即能够响铃)
- 静静的等着别人拨打
如同上面的电话机过程一样,在程序中,如果想要完成一个 TCP 服务器的功能,需要的流程如下:
- socket 创建一个套接字
- bind 绑定 ip 和 port
- listen 使套接字变为可以被动连接
- accept 等待客户端的链接
- recv/send 接收发送数据
示例:
1 import socket 2 3 # 创建socket 4 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 6 # 绑定本地信息 7 address = ("", 7788) 8 tcpSocket.bind(address) 9 10 # 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的连接 11 tcpSocket.listen(5) 12 13 while 1: 14 # 当有新的客户端来连接服务器时,就会产生一个新的套接字来专门为这个客户端服务 15 print("等待客户端连接...") 16 # newSocket就是用来为这个新来的客户端服务的 17 # tcpSocket就可以省下来专门等待其他新客户端的连接 18 newSocket, clientAddr = tcpSocket.accept() 19 # 打印客户端的IP与端口 20 print("Connected by: ", clientAddr) 21 22 # 开始数据传输 23 try: 24 limit_time = 10 25 # 设置若10秒内没有收到数据则抛出异常 26 newSocket.settimeout(limit_time) 27 # 接收对方发送过来的数据,最大接收1024个字节 28 recvData = newSocket.recv(1024) 29 print("接收到数据为:", recvData.decode()) 30 # 发送一些数据给客户端 31 # 1)send:返回值是发送的字节数量, 这个数量值可能小于要发送的字节数。如果有错误,则会抛出异常。 32 # 2)sendall:尝试发送所有数据, 成功则返回None, 失败则抛出异常。 33 newSocket.send("Thank you!".encode()) 34 # 关闭为这个客户端服务的套接字。只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接 35 newSocket.close() 36 except socket.timeout: 37 print('%s秒未接收到客户端数据,断开连接' % limit_time) 38 newSocket.close() # 关闭连接 39 break 40 except Exception: 41 print("与客户端连接发生异常,断开连接") 42 newSocket.close() # 关闭连接 43 break 44 45 # 关闭监听套接字。只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接 46 tcpSocket.close()
socket.listen([backlog])
该方法用于限制建立 socket 连接申请的个数。如果 backlog 指定了(最少是 0,如果比 0 小,系统默认改成 0),则将连接申请的数量限制为 backlog 个,如果没有指定,将指派一个默认的合理值。
事实上 accept 方法一次只能接收一个 client 的连接申请,而 client 则是多个的,这样 socket 会设计一个队列来存储 client 的连接申请则是理所当然的,于是 accept 便从这个队列里提取首位成员处理即可。而 backlog 参数的含义便是这个队列的最大值,也就是同时受理连接申请的最大值。如果队列满了便需要 client 重新 connect。
- Windows、Mac 此连接参数有效;
- Linux 此连接参数无效,默认最大。这就是很多 HTTP 服务器需用 linux 系统的原因。
运行效果
TCP 客户端
TCP 客户端要比服务器端简单很多。如果说服务器端是需要自己买手机、查手机卡、设置铃声、等待别人打电话流程的话,那么客户端就只需要找一个电话亭,拿起电话拨打即可,流程要少很多。
示例:
1 import socket 2 3 # 创建socket 4 tcpClientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 6 # 连接服务器端 7 serAddress = ("192.168.3.4", 7788) 8 tcpClientSocket.connect(serAddress) 9 10 # 提示用户输入数据 11 sendData = input("请输入要发送给服务器的数据:") 12 13 tcpClientSocket.send(sendData.encode()) 14 15 # 接收对方发送过来的数据,最大接收1024个字节 16 recvData = tcpClientSocket.recv(1024) 17 print("接收到数据为:", recvData.decode()) 18 19 # 关闭监听套接字。只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接 20 tcpClientSocket.close()
运行效果
应用:1V1 聊天
服务器端
1 import socket 2 3 # 创建socket 4 tcpServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 6 # 绑定本地信息 7 address = ("", 7788) 8 tcpServerSocket.bind(address) 9 10 # 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的连接了 11 tcpServerSocket.listen(5) 12 13 while True: 14 15 # 如果有新的客户端来连接服务器,那么就产生一个新的套接字专门为这个客户端服务器 16 # newSocket用来为这个客户端服务 17 # tcpSerSocket就可以省下来专门等待其他新客户端的连接 18 newSocket, clientAddr = tcpServerSocket.accept() 19 20 while True: 21 22 # 接收对方发送过来的数据,最大接收大小为2014字节 23 recvData = newSocket.recv(1024).decode() 24 25 # 如果对方发送“88”,则意味着客户端关闭连接 26 if recvData == "88": 27 print("Receive:", recvData) 28 print("----关闭聊天----") 29 break 30 else: 31 print("Receive:", recvData) 32 sendData = input("Send:") 33 newSocket.send(sendData.encode()) 34 35 # 关闭这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了 36 # 如果还需要服务,只能再次重新连接 37 newSocket.close() 38 39 # 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接 40 tcpSerSocket.close()
客户端
1 import socket 2 3 # 创建socket 4 tcpClientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 6 # 连接服务器端 7 serAddress = ("localhost", 7788) 8 tcpClientSocket.connect(serAddress) 9 10 while True: 11 12 # 提示用户输入数据 13 sendData = input("Send:") 14 15 tcpClientSocket.send(sendData.encode()) 16 if sendData == "88": 17 break 18 else: 19 # 接收对方发送过来的数据,最大接收1024个字节 20 recvData = tcpClientSocket.recv(1024) 21 print("Receive:", recvData.decode()) 22 23 # 关闭监听套接字。只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接 24 tcpClientSocket.close()
运行效果