一、什么是TCP协议
TCP 协议则是建立在 IP 协议之上的。TCP 协议负责在两台计算机之间建立可靠连接,保证数据包按顺序达到。TCP 协议会通过 3 次握手建立可靠连接。然后需要对每个 IP 包进行编号,确保对方按顺序收到,如果包丢了,就自动重发。一个 TCP 报文来了以后,到底是交给那个程序,就需要端口号来区分。每个网络程序都向操作系统申请一个端口号。这样,两个进程在两台计算机之间建立网络连接就需要各自的 IP 地址和各自的端口号。一个进程也可以能同时与多个计算机建立连接,因此它会申请多个端口。
TCP 每次发送一个数据包,对方都要进行确认,保证数据准确到达对象(不需要应用程序来做这件事,操作系统会自动来做);
TCP 通信的过程中,可以想象成这个虚拟的通道正在占用,不允许其它人来发送结束;
TCP 只需要建立一次连接,之后只需要发送数据,而不需要填写对象的 IP 和 PORT;
二、创建TCP服务器
创建 TCP 服务器的伪代码如下:
import socket # 导入socket模块
ss = socket.socket() # 创建服务器套接字
ss.bind() # 套接字与地址绑定
ss.listen() # 监听连接
while True: # 监听连接
cs = ss.accept() # 接收客户端连接
while True: # 通信循环
cs.recv()/cs.send() # 对话(接收/发送)
cs.close() # 关闭客户端套接字
ss.close() # 关闭服务器套接字
所有套接字都是通过使用 socket.socket() 函数来创建的。因为服务器需要占用一个端口并等待客户端的请求,所以它们必须绑定到一个本地地址。特别地,TCP 服务器必须监听(传入)的连接。
调用 accept() 方法之后,就开启了一个简单的(单线程)服务器,它会等待客户端的连接。默认情况下,accept() 是阻塞的,这意味着指定将被暂停,直到一个连接到达。一旦服务器接收一个连接,就会返回(利用 accept())一个独立的客户端套接字,用来与即将到来的消息交换。当一个传入的请求到达时,服务器会创建一个新的通信接口来直接与客户端进行通信,再次空出主要的端口,以使其接收新的客户端连接。
一旦创建了临时套接字,通信就可以开始,通过使用这个新的套接字,客户端与服务器就可以开始参与发送和接收的对话中,直达连接终止。当一方关闭连接或者向对方发送一个空字符串时,通常就会关闭连接。
from socket import socket
from socket import AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
from time import ctime
HOST = "127.0.0.1"
PORT = 8080
ADDRESS = (HOST, PORT)
tcp_server = socket(AF_INET, SOCK_STREAM) # 创建服务器套接字
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 解决端口占用问题
tcp_server.bind(ADDRESS) # 套接字与地址绑定
tcp_server.listen(5) # 监听连接,最大挂起连接数为5
while True: # 监听连接
print("waiting for connection...")
connection, address = tcp_server.accept() # 接收客户端连接
print("...connection from: ", address)
while True: # 通信循环
try:
recv_data = connection.recv(1024) # 服务端接收消息,单次最大接收为1024个字节
# 在Linux系统中,一旦data收到空,意味着是一种异常的行为:客户端非法断开连接
if not recv_data: # 适用于Linux系统
break
print(f"收到客户端【{address}】发送的数据:{recv_data.decode('utf-8')}")
connection.send(f"【{ctime()}】 {recv_data.decode('utf-8')}".encode("utf-8")) # 服务端发送消息
except ConnectionResetError: # 适用于Windows系统
break
connection.close() # 关闭连接
tcp_server.close() # 关闭服务器套接字
在代码中,一个客户端连接关闭之后,服务器就会等到另一个客户端连接。
三、创建TCP客户端
创建 TCP 服务器的伪代码如下:
import socket # 导入socket模块
cs = socket.socket() # 创建客户端套接字
cs.connect() # 尝试连接服务器
while True: # 通信循环
cs.send()/cs.recv() # 对话(发送/接收)
cs.close() # 关闭客户端套接字
所有套接字都是利用 socket.socket() 创建的。然而,一旦客户端拥有了一个套接字,它就可以利用套接字的 connect() 方法直接创建一个到服务器的连接。当连接建立之后,它就可以直接参与到与服务器的一个对话中。最后,一旦客户端完成了它的事务,它就可以关闭套接字,终止此次连接。
from socket import socket
from socket import AF_INET, SOCK_STREAM
HOST = "127.0.0.1"
PORT = 8080
ADDRESS = (HOST, PORT)
tcp_client = socket(AF_INET, SOCK_STREAM) # 创建客户端套接字
tcp_client.connect(ADDRESS) # 尝试连接服务器
while True: # 通信循环
send_data = input("请输入要发送的数据: ").strip()
if not send_data: # 按空格、回车等键结束连接
break
tcp_client.send(send_data.encode("utf-8")) # 客户端发送数据,不允许发送空数据
recv_data = tcp_client.recv(1024) # 客户端接收数据,单次最大接收为1024个字节
if not recv_data:
break
print(f"收到服务端【{ADDRESS}】返回的数据:{recv_data.decode('utf-8')}")
tcp_client.close() # 关闭客户端套接字
四、执行TCP服务器和客户端
如果先运行客户端,那么将无法进行任何连接,因为没有服务器等待接受请求。服务器可以视为一个被动伙伴,因为必须首先建立自己,然后被动的等待连接。另一方面,客户端是一个主动的合作伙伴,因为它主动发起一个连接。换句话说,首先启动服务器(在任何客户端试图连接之前)。
在开发中,创建这种 “友好的” 退出方式的一种方法就是,将服务器的 while 循环放在一个 try-except 语句中的 except 子句中,并监控 EOFError 或 KeyboardInterrupt 异常,这样你就可以在 except 或 finally 子句中关闭服务器的套接字。
标签:02,socket,编程,TCP,服务器,接字,连接,客户端 From: https://www.cnblogs.com/kurome/p/17787987.html