一、简单原理示例
简单说明
Python网络编程涉及到使用Python语言进行网络通信,这通常包括创建服务器和客户端应用程序,以及处理各种网络协议,如TCP/IP、UDP和HTTP等。Python内置了socket
模块,这个模块提供了底层的网络通信接口,使得开发者可以创建网络应用。
服务器端代码:
import socket
# 创建TCP/IP套接字
# socket.AF_INET:IPv4
# sc.SOCK_STREAM:TCP
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = socket.gethostname()
port = 12345
# 绑定端口
server_socket.bind((host, port))
# 设置最大连接数,超过后排队
server_socket.listen(5)
print(f'服务器已启动,监听端口 {port}...')
while True:
# 建立客户端连接
client_socket, client_address = server_socket.accept()
print(f'客户端 {client_address} 已连接')
# 接收来自客户端的数据
data = client_socket.recv(1024)
print(f'收到来自 {client_address} 的数据: {data.decode()}')
# 发送响应给客户端
response = '已收到消息,谢谢!\r\n'
client_socket.sendall(response.encode())
# 关闭连接
client_socket.close()
客户端代码
import socket
# 创建TCP/IP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 服务器地址和端口
server_host = 'localhost'
server_port = 12345
# 连接到服务器
client_socket.connect((server_host, server_port))
# 发送数据给服务器
message = '你好,服务器!\r\n'
client_socket.sendall(message.encode())
# 接收服务器的响应
data = client_socket.recv(1024)
print(f'收到来自服务器的响应: {data.decode()}')
# 关闭连接
client_socket.close()
二、多线程服务端(代码更新日期2024/3/15)
import socket as sc
import threading
# 服务器
def msg():
print("在线用户:")
for i in client_list:
print(i)
# 客户端
def dayin(stri):
for i in client_list:
stri+=(str(i)+'\n')
return stri
# 封装在线用户
def zai(new_client):
str = "当前在线用户:\n"
dnagqian = dayin(str)
str_data = dnagqian.encode("gbk")
new_client.send(str_data)
# 创建tcp服务端套接字
# socket.AF_INET:IPv4
# sc.SOCK_STREAM:TCP
client_list=[]
# 建立线程处理监听内容
def heard(new_client,ip_port):
while True:
# 接受数据
str = new_client.recv(1024)
if str:
print(f"客户端:{ip_port}")
recvmsg=str.decode("gbk")
print(f'消息为:{recvmsg}')
# 发送数据到客户端
str = "数据处理ing\n"
str_data = str.encode("gbk")
new_client.send(str_data)
if recvmsg=="看":
zai(new_client)
else:
print(f'用户{ip_port}下线了!')
client_list.remove(ip_port)
msg()
break;
new_client.close()
service=sc.socket(sc.AF_INET,sc.SOCK_STREAM)
service.setsockopt(sc.SOL_SOCKET,sc.SO_REUSEADDR,True)
# 绑定端口号
service.bind(("",8888))
# 设置监听
# 设置最大的等待接听数 设置最大连接数,超过后排队
service.listen(128)
# 等待接收客户端链接请求
while True:
new_client,ip_port=service.accept()
client_list.append(ip_port)
sub = threading.Thread(target=heard,args=(new_client,ip_port))
sub.setDaemon(True)
sub.start()
msg()
# 关闭套接字
service.close()
原理图解
流程图如下:
服务器端代码解读
- 创建套接字socket()
选定网络协议
# socket.AF_INET:IPv4 # sc.SOCK_STREAM:TCP
- 绑定服务器地址,端口号
service.bind(("",8888))
ip地址不填默认找到本机IP,绑定端口号
- 进行接听:
service.listen(128)
new_client,ip_port=service.accept()
在Python的
socket
编程中,service.accept()
是一个用于服务器端接受客户端连接的方法。这个方法会阻塞当前线程,直到有一个客户端连接到服务器。当有一个客户端连接时,它返回一个包含两个元素的元组:
- 一个新的socket对象,代表与客户端的连接(通常命名为
new_client
)。- 一个包含客户端IP地址和端口的元组(通常命名为
ip_port
)。
- 使用send(),recv()处理信息
注意在传输过程使用的是二进制流要通过按照一定规则编码
优化服务端
端口复用:
端口复用是一种技术,允许一个或多个程序同时监听同一个端口。在常规情况下,当一个服务器应用程序正在使用某个端口时,其他应用程序是无法使用该端口的,除非该端口被释放。然而,通过端口复用技术,多个应用程序可以同时监听同一个端口,从而提高系统的灵活性和效率。
端口复用的实现主要依赖于操作系统的支持。在Python的socket编程中,通过设置套接字选项来启用端口复用属性。具体来说,通过调用setsockopt
函数并设置SO_REUSEADDR
(在某些操作系统上可能是SO_REUSEPORT
)选项为真,可以启用端口复用功能。这样,即使一个应用程序已经占用了某个端口,其他应用程序也可以继续监听该端口。
端口复用的作用主要体现在服务器编程中。当服务器需要重启时,经常会遇到端口尚未完全关闭的情况。如果不设置端口复用,服务器可能无法完成绑定,因为端口还处于被别的套接口绑定的状态。通过启用端口复用,服务器可以在重启时立即重新绑定到相同的端口,从而避免了潜在的冲突和延迟。
需要注意的是,虽然端口复用可以提高系统的灵活性和效率,但也存在一些潜在的安全风险。例如,恶意程序可能利用端口复用技术来隐藏其活动或进行非法操作。因此,在使用端口复用技术时,需要谨慎考虑安全性问题,并采取适当的安全措施来保护系统的安全。
总之,端口复用是一种重要的技术,可以提高系统的灵活性和效率。通过正确设置套接字选项并谨慎考虑安全性问题,可以充分利用端口复用的优势,为应用程序提供更好的通信和交互体验。
建立线程
建立线程是为了让多个客户端并发执行交互,线程保证客户端服务独立
项目拓展:
可以实现网络多人聊天室的小作业,只要建立线程使用列表管理就可实现多人群聊与私聊功能。希望真心想学的同学自己拓展。加油少年!
多线程客户端:
import binascii as bb
import socket as sc
import threading
# 建立客户socket对象
# socket.AF_INET:IPv4
# sc.SOCK_STREAM:TCP
def creat():
client=sc.socket(sc.AF_INET,sc.SOCK_STREAM)
client.setsockopt(sc.SOL_SOCKET, sc.SO_REUSEADDR, True)
# 与服务器端套接字链接
client.connect(("192.168.17.1",8888))
while True:
# 发送数据到服务端
str=input("输入发送消息内容:")
str_data=str.encode("gbk")
client.send(str_data)
if str=="关闭":
break
# 接收服务端数据
# 接收最多字节数
str=client.recv(1024)
str_data=str.decode("gbk")
print(str_data)
# 关闭套接字
client.close()
if __name__ == '__main__':
sub = threading.Thread(target=creat)
sub.start()