Python 高级编程之 Socket 编程 -TCP 客户端和服务端
一、TCP 介绍
1. 概念
TCP 协议,即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP 通信需要经过创建连接、数据传送、终止连接三个步骤。
TCP 通信模型中,在通信开始之前,一定要先建立相关连接,才能发送数据。
2.TCP 特点:
面向连接:
通信双方必须先建立连接才能进行数据的传输。
可靠传输:
TCP 采用发送应答机制
超时重传:
启动定时器,在规定时间内未收到,则重新发送,直到收到。
错误校验
流量控制和阻塞管理
3.TCP 和 UDP 的比较
TCP 面向连接;UDP 是无连接的,即发送数据之前不需要建立连接.
TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付.
UDP 具有较好的实时性,工作效率比 TCP 高,适用于对高速传输和实时性有较高的通信或广播通信。
每一条 TCP 连接只能是点到点的;UDP 支持一对一,一对多,多对一和多对多的交互通信。
TCP 对系统资源要求较多,UDP 对系统资源要求较少。
TCP 严格区分客户端和服务端,UDP 不一定区分。
UDP 通信和 TCP 通信的过程如下:
UDP 通信:
TCP 通信:
形象地说:
UCP 类比于写信,别人不一定能收到;
TCP 类比于打电话,可以获知对方是否收到。
二、TCP 客户端构建
客户端:是需要被服务的一方;
服务器端:就是提供服务的一方。
TCP 客户端构建流程
创建 socket
连接服务器
收发数据 (最大接收 2014 个字节)
关闭套接字
NetAssist 进行配置如下
此时,NetAssist 作为服务端,Python 运行作为客户端。
import socket
def main():
#创建TCP套接字
tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#连接服务器
server_ip = input('Server IP:')
server_port = int(input('Server Port:'))
tcp_client.connect((server_ip,server_port))
#接收数据
recv_data = tcp_client.recv(1024)
print(recv_data)
#关闭套接字
tcp_client.close()
if __name__ == '__main__':
main()
数据进行解码:
import socket
def main():
#创建TCP套接字
tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#连接服务器
server_ip = input('Server IP:')
server_port = int(input('Server Port:'))
tcp_client.connect((server_ip,server_port))
#接收数据
recv_data = tcp_client.recv(1024).decode('gbk')
print(recv_data)
#关闭套接字
tcp_client.close()
if __name__ == '__main__':
main()
增加发送数据:
import socket
def main():
#创建TCP套接字
tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#连接服务器
server_ip = input('Server IP:')
server_port = int(input('Server Port:'))
tcp_client.connect((server_ip,server_port))
#发送数据
send_data = input('Data to send:')
tcp_client.send(send_data.encode('gbk'))
#接收数据
recv_data = tcp_client.recv(1024).decode('gbk')
print(recv_data)
#关闭套接字
tcp_client.close()
if __name__ == '__main__':
main()
三、TCP 服务端构建流程
TCP 服务端构建过程:
socket 创建套接字
bind 绑定 IP 和 port
listen 使套接字变为可以被动连接
accept 等待客户端的连接
recv/send 接收发送数据
关闭套接字
NetAssist 进行配置如下
此时,NetAssist 作为客户端,Python 运行作为服务器端;
在代码未运行时,NetAssist 点击连接按钮连接不了,因为此时服务器端还未开启,应该先运行服务器端、再连接。
import socket
def main():
#创建TCP监听套接字
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定信息
tcp_server.bind(('',7890))
#默认套接字主动变被动
tcp_server.listen(128)
#等待,创建服务套接字
new_client_socket,client_addr = tcp_server.accept()
print(client_addr)
if __name__ == '__main__':
main()
TCP 监听套接字负责等待新的客户端连接;
产生的服务套接字负责收发数据。
进一步完善代码:
import socket
def main():
#创建TCP监听套接字
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定信息
tcp_server.bind(('',7890))
#默认套接字主动变被动
tcp_server.listen(128)
#等待,创建服务套接字
new_client_socket,client_addr = tcp_server.accept()
print(client_addr)
#接收数据
recv_data = new_client_socket.recv(1024)
print(recv_data.decode('gbk'))
#发送数据
send_data = input('Data to send:').encode('gbk')
new_client_socket.send(send_data)
new_client_socket.close()
tcp_server.close()
if __name__ == '__main__':
main()
四、TCP 服务端为多个客户端服务
1. 代码分析
服务器端代码 tcp_server.accept() 部分会阻塞,阻塞直到有客户端连接;
new_client_socket.recv() 部分会阻塞,直到客户端有数据发送或者客户端断开连接解堵塞。
2. 多客户端实现
import socket
def main():
# 创建TCP监听套接字
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定信息
tcp_server.bind(('', 7890))
#为多个客户端服务
while True:
# 默认套接字主动变被动
tcp_server.listen(128)
# 等待,创建服务套接字
new_client_socket, client_addr = tcp_server.accept()
print(client_addr)
# 接收数据
recv_data = new_client_socket.recv(1024)
print(recv_data.decode('gbk'))
# 发送数据
send_str = 'Hello, ' +client_addr[0]
send_data = send_str.encode('gbk')
new_client_socket.send(send_data)
new_client_socket.close()
tcp_server.close()
if __name__ == '__main__':
main()
显然,此时可以连接多个客户端,但是每个客户端只能发送一次数据,再发送会产生异常,自动断开连接,要想多次服务,还需要进一步改进:
import socket
def main():
# 创建TCP监听套接字
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定信息
tcp_server.bind(('', 7890))
#为多个客户端服务
while True:
# 默认套接字主动变被动
tcp_server.listen(128)
# 等待,创建服务套接字
new_client_socket, client_addr = tcp_server.accept()
print(client_addr)
#为客户端提供多次服务
while True:
# 接收数据
recv_data = new_client_socket.recv(1024)
# 发送数据
if recv_data.decode('gbk'):
print(recv_data.decode('gbk'))
send_str = 'Hello, ' +client_addr[0]
send_data = send_str.encode('gbk')
new_client_socket.send(send_data)
#无数据,退出循环,关闭连接
else:
break
new_client_socket.close()
tcp_server.close()
if __name__ == '__main__':
main()
易知,可以实现为多个客户端提供服务,但是一次只能为一个客户端提供服务,不能同时为多个客户端服务。
本文内容由互联网用户自发贡献,版权归作者所有,本社区不拥有所有权,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:jaagool@sina.cn 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。