TCP的概念:英文(Transmission Control Protocal)简称传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
为什么要有TCP呢?
网络之间的数据传输需要通过ip地址找到对应主机,再通过端口号找到对应端口,再通过端口把数据传输给对应的软件,但是大家要知道,数据是不可以随便传输的,任何事物都需要遵守相应的规则才能顺利到达目标,这时就需要一个通信双方共同遵守的传输协议——TCP
TCP协议的通信步骤有三个:
1.首先需要通信双方建立连接
2.然后彼此可以进行数据传输
3.数据传输结束后就关闭连接
对于通信步骤解释一下:之前有说过TCP是面向连接的,这就相当于生活之中的打电话,比如你想和某人通电话,是不是需要先拨号进行建立连接,等对方同意建立连接,也就是对方接了你的电话,这时你说的话对方才能够收到,然后等到通话结束需要挂断电话进行关闭连接。这个过程就完美体现了TCP的通信步骤。
TCP的特点:
1.面向连接的:
也就是通信双方必须先要进行建立连接,才能进行数据传输,等到数据传输结束后双方再断开连接,以释放系统资源。
2.可靠的数据传输:
TCP采用传输应答机制
超时重传
错误校验
流量控制和阻塞管理
好了TCP已经介绍完成,TCP是一种传输协议,是通信双方需要共同遵守的一个准则,有了准则通信又是如何进行的呢?
此时就需要socket(套接字)来完成具体的通信过程。
socket(套接字)概念:是进程之间传输数据的一个工具,多进程之间想要进行网络通信都需要基于socket。可以说我们使用的QQ、微信等软件进行通信时都使用到了socket。
这里先给大家简单介绍一下socket套接字的概念,具体的实际应用在以下的具体操作中给大家展示。
接下来我们通过python建立一个TCP客户端和TCP服务器来完成数据通信:
TCP客户端建立的步骤:
1.创建客户端套接字
2.和服务器套接字建立连接
3.给服务器端发送数据
4.接收服务器端的数据
5.关闭客户端套接字
说明:
1.TCP 网络应用程序开发分为客户端程序开发和服务端程序开发。
2.主动发起建立连接请求的是客户端程序
3.等待接受连接请求的是服务端程序
导入 socket 模块
import socket
创建客户端socket 对象
socket.socket(AddressFamily, Type)
参数说明:
AddressFamily 表示IP地址类型, 分为IPv4和IPv6
Type 表示传输协议类型
方法说明:
connect((host, port)) 表示和服务端套接字建立连接, host是服务器ip地址,port是应用程序的端口号
send(data) 表示发送数据,data是二进制数据
recv(buffersize) 表示接收数据, buffersize是每次接收数据的长度
掌握了socket套接字在tcp客户端中的使用方法,那么接下来我们
在Python中创建一个tcp客户端:
import socket
if __name__ == '__main__':
# 1 创建客户端套接字对象tcp_client_1
# 参数介绍:AF_INET 代表IPV4类型, SOCK_STREAM代表tcp传输协议类型 ,注:AF_INET6代表IPV6
tcp_client_1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2 通过客户端套接字的connect方法与服务器套接字建立连接
# 参数介绍:前面的ip地址代表服务器的ip地址,后面的61234代表服务端的端口号 。
tcp_client_1.connect(("192.168.100.100",61234))
# 将编号好的数据存到变量send_data中,注:encode(encoding='utf-8)是将数据转换成utf-8的格式发送给服务器
send_data = "你好,服务器,我是客户端1号".encode(encoding='utf-8')
# 3 通过客户端套接字的send方法将数据发送给服务器
tcp_client_1.send(send_data)
# 4 通过客户端套接字的recv方法来接受服务器返回的数据存到变量recv_data中,1024是可接收的最大字节数。
recv_data = tcp_client_1.recv(1024)
# 将接收到的服务器数据recv_data通过decode方法解码为utf-8
print(recv_data.decode(encoding = 'utf-8'))
# 5 最后关闭客户端套接字连接
tcp_client_1.close()
通信结果的话等TCP服务端也搭建完成后,一起来进行测试。
TCP服务器建立的步骤:
1.创建服务器端套接字
2.绑定端口
3.设置监听
4.等待接收客户端的连接请求
5.接收客户端发来的数据
6.给客户端发送数据
7.关闭服务端的套接字
导入 socket 模块
import socket
创建服务端 socket 对象
socket.socket(AddressFamily, Type)
参数说明:
AddressFamily 表示IP地址类型, 分为IPv4和IPv6
Type 表示传输协议类型
方法说明:
bind((host, port)) 表示绑定端口号, host 是 ip 地址,port 是端口号,ip 地址一般不指定,表示本机的任何一个ip地址都可以。
listen (backlog) 表示设置监听,backlog参数表示最大等待建立连接的个数。
accept() 表示等待接受客户端的连接请求
send(data) 表示发送数据,data 是二进制数据
recv(buffersize) 表示接收数据, buffersize 是每次接收数据的长度
掌握了socket套接字在tcp服务器中的使用方法,那么接下来我们
在Python中创建一个tcp服务器:
#导入socket模块
import socket
if __name__ == '__main__':
# 创建tcp服务端套接字
# 参数同客户端配置一致,这里不再重复
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放,否则的话在30秒-2分钟之内这个端口是不会被释放的,这是TCP的为了保证传输可靠性的机制。
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 给客户端绑定端口号,客户端需要知道服务器的端口号才能进行建立连接。IP地址不用设置,默认就为本机的IP地址。
tcp_server.bind(("", 61234))
# 设置监听
# 128:最大等待建立连接的个数, 提示: 目前是单任务的服务端,同一时刻只能服务与一个客户端,后续使用多任务能够让服务端同时服务与多个客户端
# 不需要让客户端进行等待建立连接
# listen后的这个套接字只负责接收客户端连接请求,不能收发消息,收发消息使用返回的这个新套接字tcp_client来完成
tcp_server.listen(128)
# 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
# 1. 专门和客户端通信的套接字: tcp_client
# 2. 客户端的ip地址和端口号: tcp_client_address
tcp_client, tcp_client_address= tcp_server.accept()
# 代码执行到此说明连接建立成功
print("客户端的ip地址和端口号:", tcp_client_address)
# 接收客户端发送的数据, 这次接收数据的最大字节数是1024
recv_data = tcp_client.recv(1024)
# 对服务器发来的数据进行解码保存到变量recv_content中
recv_content = recv_data.decode(encoding = "utf-8")
print("接收客户端的数据为:", recv_content)
# 准备要发送给服务器的数据
send_data = "好的,消息已收到".encode(encoding = "utf-8")
# 发送数据给客户端
tcp_client.send(send_data)
# 关闭服务与客户端的套接字, 终止和客户端通信的服务
tcp_client.close()
# 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务 但是正常来说服务器的套接字是不需要关闭的,因为服务器需要一直运行。
# tcp_server.close()
好了,单任务版的TCP客户端和服务器都已经搭建完成,接下来先让TCP服务器端跑起来,然后再通过TCP客户端连接发送数据。
好了,单任务版的TCP客户端和服务器已经搭建成功,但是因为是单进程的,所以客户端的用户只能发送一条消息,他们之间就断开了连接,无法持续通信,并且其他用户此时想连接上来也是不允许的,因为服务器目前只能接收一个客户端,所以如果我们想让同时多个用户对服务器进行多次通信需要怎么做呢?
利用我上篇文章提到的多进程和多线程来实现
搭建持续性多用户版的服务器:
因为大部分代码都已经详细注释过,以下代码中与上重复部分简要注释:
# 导入套接字模块
import socket
# 导入线程模块
import threading
# 定义个函数,使其专门重复处理客户的请求数据(也就是重复接受一个用户的消息并且重复回答,直到用户选择下线)
def dispose_client_request(tcp_client_1,tcp_client_address):
# 5 循环接收和发送数据
while True:
recv_data = tcp_client_1.recv(4096)
# 6 有消息就回复数据,消息长度为0就是说明客户端下线了
if recv_data:
print("客户端是:", tcp_client_address)
print("客户端发来的消息是:", recv_data.decode())
send_data = "消息已收到,正在处理中...".encode()
tcp_client_1.send(send_data)
else:
print("%s 客户端下线了..." % tcp_client_address[1])
tcp_client_1.close()
break
if __name__ == '__main__':
# 1 创建服务端套接字对象
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 设置端口复用,使程序退出后端口马上释放
tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
# 2 绑定端口
tcp_server.bind(("",61234))
# 3 设置监听
tcp_server.listen(128)
# 4 循环等待客户端连接请求(也就是最多可以同时有128个用户连接到服务器进行通信)
while True:
tcp_client_1 , tcp_client_address = tcp_server.accept()
# 创建多线程对象
thd = threading.Thread(target = dispose_client_request, args = (tcp_client_1,tcp_client_address))
# 设置守护主线程 即如果主线程结束了 那子线程中也都销毁了 防止主线程无法退出
# thd.setDaemon(True)
# 启动子线程对象
thd.start()
# 7 关闭服务器套接字 (其实可以不用关闭,因为服务器一直都需要运行)
# tcp_server.close()
总结:可以看到TCP服务器同时连接了两个用户,并且与每个用户进行了多次通信,直到客户端下线。