当前版本号[20231109]。
版本 | 修改说明 |
---|---|
20231109 | 初版 |
之前我们学习了IP地址和端口号,通过IP地址能够找到对应的设备,然后再通过端口号找到对应的端口,再通过端口把数据传输给应用程序,这里要注意,数据不能随便发送,在发送之前要选择网络传输方式(传输协议),保证程序之间按照指定的传输规则进行数据的通信。
TCP的英文全拼(Transmission Control Protocol)简称传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
通信双方必须先建立好连接才能进行数据的传输,并且双方都会为此连接分配必要资源用来记录连接的状态和信息。当数据传输完成后,双方必须断开此连接,以释放系统资源。
TCP采用发送应答机制
通过TCP这种方式发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传送成功
超时重传
发送端发送一个报文之后就会启动定时器,如果指定时间内没有得到应答就会重新发送这个报文段
错误校验
TCP用一个校验和函数来校验数据是否有错误,在发送和接收时都要计算校验和
流量控制和阻塞管理
流量控制用来避免发送端发送过快而使得接收方来不及接收
TCP是一个稳定、可靠的传输协议,常用于对数据进行准确无误的传输,比如:文件下载,浏览器上网。
网络传输是以二进制数据进行传输的
提示:
在网络传输数据的时候,数据需要先编码转化为二进制(bytes)数据类型
提示:
encoed()和decode()函数可以接受参数,encoding是指在编解码过程中使用的编码方案。
bytes.decode(encoding=“utf-8”)
str.encode(encoding=”utf-8”)
TCP网络应用程序开发分为:
如何区分两个程序?
客户端程序是指运行在用户设备上的程序
服务端程序是指运行在服务器设备上的程序,专门为客户端提供数据服务。
1.创建客户端套接字对象(买电话)
2.和服务端套接字建立连接(打电话)
3.发送数据(说话)
4.接收数据(接听)
5.关闭客户端套接字(挂电话)
导入socket模块
rfykgaierw
import socket
创建客户端socket对象使用socket类
socket.socket(AddressFamily, Type)
import socket
# 创建udp的套接字
# AF_INET: ipv4地址类型
# SOCK_STREAM: TCP传输协议类型
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ...这里是使用套接字的功能(省略).
# 不用的时候,关闭套接字
s.close()
import socket
# 创建tcp socket
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 目的信息
server_ip = input(“请输入服务器ip:”)
server_port = int(input(“请输入服务器port:”))
# 连接服务器套接字
tcp_client_socket.connect((server_ip, server_port))
# 提示用户输入数据
send_data = input(“请输入要发送的数据:”)
tcp_client_socket.send(send_data.encode(”utf-8“)) # 注意:如果是windows的网络调试助手
使用gbk编码
# 关闭套接字
tcp_client_socket.close()
import socket
# 创建tcp socket
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_ip = input("请输入服务器ip:")
server_port = int(input("请输入服务器port:"))
# 连接服务器套接字
tcp_client_socket.connect((server_ip, server_port))
send_data = input("请输入要发送的数据:")
tcp_client_socket.send(send_data.encode(”utf-8"))
# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcp_client_socket.recv(1024)
print('接收到的数据为:', recvData.decode(’utf-8'))
# 关闭套接字
tcp_client_socket.close()
具有了客户端和服务端,一个网络应用才可以真正的使用
1.创建服务端端套接字对象
2.绑定IP地址和端口号
3.设置监听
4.等待接受客户端的连接请求
5.接收数据
6.发送数据
7.关闭套接字
导入socket模块
import socket
创建服务端socket对象使用socket类
socket.socket(AddressFamily, Type)
import socket
# 创建socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(("", 7788)) #绑定IP地址和端口号
# 设置监听
tcp_server_socket.listen(128) # 128:表示最大等待连接数
# 如果有新的客户端来连接服务端,那么就产生一个新的套接字专门为这个客户端服务
client_socket, clientAddr = tcp_server_socket.accept()
recv_data = client_socket.recv(1024) # 接收1024个字节
print("接收到的数据为:", recv_data.decode())
client_socket.close() # 关闭为这个服务与客户端的套接字
tcp_server_socket.close() # 关闭为这个服务端套接字
import socket
# 创建socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(("", 7788)) #绑定IP地址和端口号
tcp_server_socket.listen(128) # 设置监听, 128:表示最大等待连接数
# 如果有新的客户端来连接服务端,那么就产生一个新的套接字专门为这个客户端服务
client_socket, clientAddr = tcp_server_socket.accept()
recv_data = client_socket.recv(1024) # 接收1024个字节
print("接收到的数据为:", recv_data.decode())
client_socket.send("thank you !".encode()) # 发送数据到客户端
client_socket.close()# 关闭为这个服务与客户端的套接字
tcp_server_socket.close()# 关闭为这个服务端套接字
导入socket模块
创建TCP套接字‘socket’
参数1: ‘AF_INET’, 表示IPv4地址类型
参数2: ‘SOCK_STREAM’, 表示TCP传输协议类型
参数1: 元组, 比如:(’’, 端口号),元组里面的一个元素是ip地址,一般不需要设置,第二个元素是启动程序后使用的端口号。
参数1: 最大等待连接数
等待接受客户端的连接请求‘accept’
发送数据‘send’
参数1: 要发送的二进制数据, 注意: 字符串需要使用encode()方法进行编码
参数1: 表示每次接收数据的大小,单位是字节,注意: 解码成字符串使用decode()方法
当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间。
send是不是直接把数据发给服务端?
不是,要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡
recv是不是直接从客户端接收数据?
不是,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据。
说明:
发送数据是发送到发送缓冲区,接收数据是从接收缓冲区
不管是recv还是send都不是直接接收到对方的数据和发送数据到对方,发送数据会写入到发送缓冲区,接收数据是从接收缓冲区来读取,发送数据和接收数据最终是由操作系统控制网卡来完成。
目前我们开发的TCP服务端程序只能服务于一个客户端,那该如何实现一个服务端服务多个客户端?下面我们分两半进行分析。
实现步骤分析:
示例代码如下:
import socket
# 创建socket
if __name__ == '__main__':
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(("", 8888)) #绑定IP地址和端口号
tcp_server_socket.listen(128)# 设置监听, 128:表示最大等待连接数
while True:
# 如果有新的客户端来连接服务端,那么就产生一个新的套接字专门为这个客户端服务
client_socket, clientAddr = tcp_server_socket.accept()
recv_data = client_socket.recv(1024) # 接收1024个字节
print("接收到的数据为:", recv_data.decode())
client_socket.send("thank you !".encode()) # 发送数据到客户端
client_socket.close()# 关闭为这个服务与客户端的套接字
tcp_server_socket.close()# 关闭为这个服务端套接字
示例结果如下:
但缺点就是:使用while true 可以服务多个客户端,但是要等第一个客户端运行结束后,才能到下一个。
目前我们开发的TCP服务端程序不能同时服务于多个客户端
使用多任务可以实现一个服务端同时服务多个客户端,本案例中我们使用线程
实现步骤分析:
2. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
示例代码如下:
import socket
import threading
def handle_client(conn_socket):
#接收数据
recv_data = conn_socket.recv(1024) # 接收1024个字节
print("接收到的数据为:", recv_data.decode())
conn_socket.send("thank you !".encode()) # 发送数据到客户端
conn_socket.close() # 关闭为这个服务与客户端的套接字
# 创建socket
if __name__ == '__main__':
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(("", 8888)) #绑定IP地址和端口号
tcp_server_socket.listen(128)# 设置监听, 128:表示最大等待连接数
while True:
# 如果有新的客户端来连接服务端,那么就产生一个新的套接字专门为这个客户端服务
conn_socket, ip_port = tcp_server_socket.accept()
print("客户端地址:", ip_port)
#使用多线程去接收多个客户端的请求
sub_thread = threading.Thread(target=handle_client, args=(conn_socket, ))
sub_thread.start()
tcp_server_socket.close()# 关闭为这个服务端套接字
示例结果如下:
while True
service_client_socket, ip_port = tcp_server_socket.accept()
while True:
service_client_socket, ip_port = tcp_server_socket.accept()
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
sub_thread.start()
tcp_server_socket.close()# 关闭为这个服务端套接字
示例结果如下:
[外链图片转存中...(img-zgmLIhZC-1699542688026)]
#### 5.5.1 知识要点
1. 编写一个TCP服务端程序,循环等待接受客户端的连接请求
```python
while True
service_client_socket, ip_port = tcp_server_socket.accept()
while True:
service_client_socket, ip_port = tcp_server_socket.accept()
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
sub_thread.start()