TCP协议简介
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
计算机之间的通信需要使用Socket套接字,Python标准库中已内置Socket模块。
TCP客户端和服务器通信流程如下:
对于TCP服务端建立:
- 创建TCP套接字
- 关联本地端口与插座
- 绑定端口后客户端才能访问
- 设置套接字监听模式
- 设置为被动模式,不能主动发起请求,只能被动等待连接
- 接受来自客户端的新连接
- 三次握手建立连接
- 接收和发送数据
- 关闭客户机/服务器连接
- 返回到步骤4
对于TCP客户端建立:
- 创建TCP套接字
- 连接到TCP服务器
- 发送数据/接收数据
- 关闭连接
- 发起关闭连接通知
- 四次挥手后断开连接


搭建TCP客户端
接下来就来利用Python搭建一个简单的TCP客户端,实现局域网内通信。
流程如下:
- 创建客户端套接字对象
socket()
- 和服务器套接字建立连接
connect()
- 发送数据
send()
- 接受数据
recv()
- 关闭客户端套接字
close()
至于TCP协议和Socket的实现原理可自行查阅相关资料。
首先,导入Socket模块
import socket
创建套接字对象
- 使用
socket.socket(AddressFamily, Type)
返回一个socket对象- AddressFamily: 表示IP地址类型,分为IPv4和IPv6
- socket.AF_INET: IPv4
- Type: 表示传输协议类型
- SOCK_STREAM: TCP传输协议
- SOCK_DGRAM: UDP传输协议
- AddressFamily: 表示IP地址类型,分为IPv4和IPv6
- 默认参数即为
socket.AF_INET, socket.SOCK_STREAM
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
和服务端建立连接
connet()
方法仅接受一个元组参数,元组中包含IP和端口(服务端的IP和端口)
tcp_client_socket.connet(('192.168.26.70', 7777))
print('连接成功')
# 代码执行到此表示连接建立成功
准备发送的数据
- 服务端为Windows用gbk,Mac和Linux为utf-8
send_data = '你好服务端,我是客户端'.encode('utf-8')
发送数据
- 发送和接受数据都是以二进制字节流的形式进行
tcp_client_socket.send(send_data)
接收数据
recv(bytes)
方法接收服务端发来的数据,返回二进制数据- bytes参数指定接收的数据最大字节数为1024
recv_data = tcp_client_socket.recv(1024)
对数据进行解码,由于数据传输形式是二进制,需解码才能正常显示
recv_content = recv_data.decode('utf-8')
print(recv_content)
断开连接
tcp_client_socket.close()
以下是完整代码
'''
1. 创建客户端套接字对象 socket()
2. 和服务器套接字建立连接 connect()
3. 发送数据 send()
4. 接受数据 recv()
5. 关闭客户端套接字 close()
'''
import socket
# 导入socket模块
if __name__ == '__main__':
# 创建tcp客户端套接字
# 1. AF_INET: 表示IPv4
# 2. SOCK_STREAM: TCP传输协议
# 3. SOCK_DGRAM: UDP传输协议
# 默认参数 socket.AF_INET, socket.SOCK_STREAM
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 和服务端应用程序建立连接
tcp_client_socket.connect(('192.168.26.70', 7777))
print('连接成功')
# 代码执行到此,说明连接建立成功
# 准备发送的数据
# 服务端为Windows用gbk,Mac和Linux为utf-8
send_data = '你好服务端,我是客户端'.encode('utf-8')
# 发送数据
tcp_client_socket.send(send_data)
# 接受数据,指定接受的数据最大字节数是1024,最大4096
recv_data = tcp_client_socket.recv(1024)
# 返回的直接是服务端程序发送的二进制数据
# print(recv_data)
# 对数据进行解码
recv_content = recv_data.decode('utf-8')
print(recv_content)
# 断开连接
tcp_client_socket.close()
搭建TCP服务端
搭建完客户端,来搭建一个客户端尝试连接
- 创建服务端套接字对象 socket()
- 绑定端口号 bind()
- 设置监听 listen()
- 等待接受客户端的连接请求 accept()
- 接收数据 recv()
- 发送数据 send()
- 关闭套接字 close()
首先导入socket模块
import socket
创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
绑定ip和端口
- 如果在绑定IP时,没有给定IP,默认绑定的是本地IP地址
bind()
方法仅接受一个元组参数,元组中包含IP和端口(服务端的IP和端口)
tcp_server_socket.bind(('', 7777))
设置监听
- 使用
listen()
方法设置监听 - 设置监听后,服务端进入被动模式,只能被动连接,连接前会一直等待
tcp_server_socket.listen(128)
print('监听中···')
等待客户端连接
接受连接
- accpet()会返回两个数据,一个是客户端的Socket对象,另一个是客户端的地址信息
client_socket, ip_port = tcp_server_socket.accept()
print(f'客户端{ip_port[0]} 使用端口{ip_port[1]} 连接成功')
接受客户端的数据
- 如果接收的数据长度为0说明客户端发送了断开连接请求
data_recv = client_socket.recv(1024).decode('utf-8')
if len(data_recv):
print(f'客户端 {ip_port[0]} 发送的数据是 {data_recv}')
else:
print(f'客户端{ip_port[0]} 断开连接')
给客户端发送数据
data_send = '已收到'.encode('utf-8')
client_socket.send(data_send)
关闭客户端
client_socket.close()
关闭服务端
tcp_server_socket.close()
完整代码如下
'''
1. 创建服务端套接字对象 socket()
2. 绑定端口号 bind()
3. 设置监听 listen()
4. 等待接受客户端的连接请求 accept()
5. 接收数据 recv()
6. 发送数据 send()
7. 关闭套接字 close()
'''
import socket
if __name__ == '__main__':
# 创建tcp服务端套接字
# 如果马上重启服务器时会出现错误,因为地址和端口没有被释放
# OSError: [Errno 48] Address already in use
# 如果想马上释放,要设置socket选项
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置socket选项
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定IP和端口
# 如果在绑定ip时,没有给定ip,默认绑定的时本地ip地址
tcp_server_socket.bind(('', 7777))
# 设置监听
# 设置监听后服务端进入被动模式,只能被动接受连接
tcp_server_socket.listen(128)
print('监听中···')
# 等待客户端连接
# 如果客户连接上来后,该函数会返回两个数据,一个是客户端的Socket对象,另一个是客户端的地址信息
client_socket, ip_port = tcp_server_socket.accept()
print(f'客户端{ip_port[0]} 使用端口{ip_port[1]} 连接成功')
# 接受客户端的数据
data_recv = client_socket.recv(1024).decode('utf-8')
if len(data_recv):
print(f'客户端 {ip_port[0]} 发送的数据是 {data_recv}')
else:
print(f'客户端{ip_port[0]} 断开连接')
# 给客户端发送数据
data_send = '已收到'.encode('utf-8')
client_socket.send(data_send)
# 关闭客户端
client_socket.close()
# 关闭服务端
tcp_server_socket.close()
# 设置端口号复用,让程序退出端口号立即释放
Comments NOTHING