TCP网络编程
IP地址
作用: 标识计算机网络中一个主机设备/系统(标识计算机网络通信过程中的收发双方地址)
在同一个计算机网络 IP地址不能重复
端口
- 概念: 本质就是一个数字, 标识计算机系统系统中一个应用程序(服务)
- 端口号: 数字 0~65535(2字节 = 16 bit = 2**16)
- 如果一个程序没有设定端口号, 系统会随机为用户程序分配一个动态端口
- 在linux 中查看占用某端口的程序用 sudo lsof -i:端口号 来查询
- 在网络通信过程中一般将 IP 和端口合用
socket 套接字
套接字 在中国台湾被称为 —> 网络插座
sock 底层 封装了 网络通信的功能, 用户使用这个功能完成网络交互就可以了
TCP
Transmission Control Protocol 传输控制协议. 就是一种套接字
特点:
1.面向连接: 通信之前需要先建立连接, 通信, 关闭连接. 类似打电话
2.可靠 保证 发送包安全到达
- 应答机制 接受方收到了数据恢复应答 ACK
- 超时传送 发送方启动定时器, 超过一定时间限制就会重发
- 错误效验 数据+结果 -----> 收到数据对数据进行演算
- 拥塞控制 发送方会根据实际情况动态调整发送速度,降低丢包率
- 有序编号 每个包编号
客户端通信流程
需要在linux中安装网络调试助手
import socket
if __name__ == "__main__":
# 1 创建一个套接字 实例对象=模块.类() IPV4 基于字节流
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 建立和服务器的连接 面向连接 .connect(服务器地址(IP, 端口))
server_address = ("172.17.0.1", 8080)
tcp_socket.connect(server_address)
# 3. 和服务器通信 发送数据 .send(需要发送的字节<二进制> 数据)
data = input("请输入需要给小姐姐发送的数据:")
tcp_socke.send(data.encode())
# 4.接受数据 返回值 = .recv(本次接受的最大字节)
# 返回值正常情况下是收到的字节; 如果对方断开连接 收到的就是空数据 b""
recv_data = tcp_socket.recv(1024)
if recv_data:
print("收到了数据%s" % recv_data.decode("utf-8"))
else:
print("对方已经下线")
# 5. 关闭套接字
tcp_socket.close()
服务器通信过程
需要在linux中安装网络调试助手
import socket
import threading
def func(new_client_socket, client_address):
"""循环的接受用户的信息 收到 - 恢复 判断下线结束当前代码的执行"""
while True:
# 5 客服使用分机和用户通信
# 5.1 分机接受来自用户的数据
recv_data = new_client_socket.recv(1024)
print("收到了来自客户端 %s 的数据: " % (str(client_address), recv_data.decode())
# 5.2 判断用户是否已经下线 如果下线就关闭套接字 退出循环; 否则继续
if not recv_data:
print("用户已经下线")
new_client_socket.close()
break
# 5.3 使用分机回复消息
new_client_socket.send(recv.data)
if __name__ == "__main__":
# 1. 创建一个服务器套接字 专门接受用户的连接请求并转发给分机
server_socket = socket.socket()
server_socket_setsockopt(sock.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定端口
local_address = ("", 8888) # "" 表示本机所有IP
server_socket.bind(local_address)
# 3. 监听 开始营业 # 128 表示服务器可以让多个客户等待被服务 不等于服务器可以和多少个用户通信
server_socket.listen(128)
while True:
# 4. 接受用户的请求 , 转接请求到分机
new_client_socket, client_address = server_socket.accept()
# (<socket.socket套接字对象 分机个用户通信>, 客户端地址元祖("192.168.154.64", 33062))
# print(ret)
print("接收到来自%s的用户连接请求"% str(client_address))
# 每当有一个新用户连接到服务器, 为他创建一个专门子线程运行 func 函数 处理消息
thd = threading.Thread(target=func, args=(new_client_socket, client_address))
thd.start()
在调试上述代码有可能会出现 OSError: [Errno 98] Address already in use 这个问题, 这个是历史遗留问题,换个端口就好啦
地址重用
产生原因: TCP标准为了保证最后一次ACK能够可靠送达规定,
凡是主动断开连接的一方必须保持套接字资源(端口)一段时间 30s-2min === 2 MSL 时间
导致服务器重启在绑定原有端口就会出现地址被使用的现象
解决: 忽略 2MSL 时间 使用地址重用
解决方案:
-
set socket option 套接字层面 重用地址 reuse address 1True 表示设置 0False 取消设置
-
服务器套接字对象.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 1. 创建一个服务器套接字 专门接受用户的链接请求并转发给分机
server_socket = socket.socket()
# 让服务器可以立即重启 并且绑定原有端口等资源 解决 address in use
server_socket.setsockopt(sock.SOL_SOCKET, socket.SO_REUSEADDR, 1)