3. TCP 通信过程
3.1 编码解码
ipython3 工具安装 pip3 install ipython3
s = "hello dage"
# str类型 不能直接在网络中传输 如果需要传输需要转换为 Bytes 二进制 字节类型
# utf-8编码情况 一个汉字 3 个字节 ; GBK 编码情况下 一个汉字 2 字节
s1 = "hello大哥"
# str-编码-> bytes 二进制数据 = 字符串数据.encode(encoding='utf-8')
In [8]: b2 = s1.encode('utf-8')
In [9]: type(b2)
Out[9]: bytes
In [10]: b2
Out[10]: b'hello\xe5\xa4\xa7\xe5\x93\xa5'
In [13]: b3 = s1.encode('gbk')
In [14]: b3
Out[14]: b'hello\xb4\xf3\xb8\xe7'
# bytes-解码->str 字符串数据 = 二进制数据.decode(encoding='utf-8')
# 解码时的方案 一定要和此数据在编码时的方案保持一致 否则就会出错(异常或数据错误)
In [22]: b3.decode('gbk')
Out[22]: 'hello大哥'
3.2 客户端通信
import socket
if __name__ == '__main__':
# 1 创建TCP套接字 参数1是地址协议版本 IPv4 参数2是套接字类型 基于字节流Stream
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2 建立和服务器的连接 - 面向连接的协议 参数是服务器IP和服务器端口构成的元组
tcp_socket.connect(('172.16.51.151', 8080))
# 3 发送数据 参数就是需要发送的字节数据
tcp_socket.send("黑服你".encode())
# 4 接收数据 参数是需要接收数据的最大字节数 返回值就是接收到的数据<字节数据>
# 如果对方断开了连接 接收的数据将b'' 长度是0
ret = tcp_socket.recv(1024)
if len(ret) == 0:
print("对方断开连接了")
# print(ret)
else:
print("接收到了来自服务器的数据 %s" % ret.decode())
# 5 关闭连接 关闭套接字
tcp_socket.close()
3.3 服务器通信
# TCP 服务器和多个用户循环通信
import socket
# echo回射服务器 将接收到的数据原封不动反弹回客户端
if __name__ == '__main__':
# 1 创建一个服务器套接字(大堂经理) IPv4 基于字节流
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2 固定端口 IP=''表示本机所有IP
server_socket.bind(('', 8888))
# 3 监听 设置等待服务区(已经连通的但是尚未被服务的套接字) 经验值128
server_socket.listen(128)
# 4 循环接受一个新用户的连接请求 分配到和客户端关联的套接字 业务员
while True:
client_socket, client_address = server_socket.accept()
print("有客官来了 业务来了")
# (<socket.socket>业务员 , 客户端地址元组('172.16.51.151', 45970))
# print(ret)
# 5 由业务员负责给客户端服务 处理请求数据
while True:
data = client_socket.recv(4096)
print("客户端%s发送了%s" % (str(client_address),data.decode()))
# if len(data) == 0: # b'' None
if not data:
# 6 关闭和客户端关联的套接字 业务员
client_socket.close()
break
client_socket.send(data)
# 7 关闭服务区套接字 大堂经理
# server_socket.close()
TCP 相关知识点梳理
调试助手 windows版本 发送出来的数据是 GBK 编码 收 UTF-8编码
data = recv(4096)
send(data.deocde(‘GBK’).encode(‘UTF-8’))
b’’ b’ ’
3.4 Address already in use问题解决
-
权宜办法
换端口
-
原理: TCP 标准规定
为了保证最后一次 ACK 成功送达 连接完全断开
凡是主动断开连接的一方
套接字相关资源(端口) 必须保持 2MSL 时间 30s-2min
- 解决方案
地址重用 立即忽略 2MSL 时间
# 设置套接字选项 套接字选项 地址重用 1 表示设置
套接字对象.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
3.5 多任务版本TCP服务器
import socket
import threading
def client_request(client_socket, client_address):
"""处理客户端请求的函数"""
# 5 由业务员负责给客户端服务 处理请求数据
while True:
data = client_socket.recv(4096)
print("客户端%s发送了%s" % (str(client_address), data.decode()))
# if len(data) == 0: # b'' None
if not data:
# 6 关闭和客户端关联的套接字 业务员
client_socket.close()
break
client_socket.send(data)
# echo回射服务器 将接收到的数据原封不动反弹回客户端
if __name__ == '__main__':
# 1 创建一个服务器套接字(大堂经理) IPv4 基于字节流
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置套接字选项 作用: 立即重用端口 不需要等待30s-2min
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2 固定端口 IP=''表示本机所有IP
server_socket.bind(('', 8888))
# 3 监听 设置等待服务区(已经连通的但是尚未被服务的套接字) 经验值128
server_socket.listen(128)
# 4 循环接受一个新用户的连接请求 分配到和客户端关联的套接字 业务员
while True:
client_socket, client_address = server_socket.accept()
print("有客官来了 业务来了")
# (<socket.socket>业务员 , 客户端地址元组('172.16.51.151', 45970))
# print(ret)
# client_request(client_socket)
# 创建一个子线程 运行 处理客户端请求的函数
thd = threading.Thread(target=client_request, args=(client_socket,client_address))
thd.start()
# 7 关闭服务区套接字 大堂经理
# server_socket.close()