python中UDP基于套接字编程,并发的TCP与UDP编程,TCP与UDP的差异。
菩提本无根,明镜亦非台
基于UDP协议的套接字通信:
1.UDP是无连接的,先启动那一端都不会报错。
UDP服务端:
ss = socket() #创建一个服务器的套接字
ss.bind() #绑定服务器套接字
inf_loop: #服务器无限循环
cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
ss.close() # 关闭服务器套接字
UDP客户端:
cs = socket() # 创建客户套接字
comm_loop: # 通讯循环
cs.sendto()/cs.recvfrom() # 对话(发送/接收)
cs.close() # 关闭客户套接字
2.UDP套接字通信,简单示例:
UDP服务端:
from socket import *
import time
server = socket(AF_INET, SOCK_DGRAM)
server.bind(('127.0.0.1', 8080))
while True:
data, client_addr = server.recvfrom(1024)
time.sleep(10)
server.sendto(data.upper(), client_addr)
UDP客户端:
from socket import *
client = socket(AF_INET, SOCK_DGRAM)
while True:
msg = input('>>: ').strip()
client.sendto(msg.encode("utf-8"), ('127.0.0.1', 8080))
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))
TCP协议 VS UDP协议
1.可靠性:
- tcp协议是可靠协议:
双方在通信过程中,对方必须回复一个ack确认信息,才会将自己这端的数据从内存中删除 - udp协议不可靠:
双方通信过程中,发送一条消息就会立即删除,不管对方是否接收到
2.有无链接:
- tcp有链接
- udp无链接
3.传输数据的效率:
- tcp需要互相通信 (效率低)
- udp不需要互相通信(效率高)
4.粘包问题
-
udp协议称之为数据报协议,每次发送都是一个完整的数据报,一个发送唯一对应一个接收所以udp协议没有粘包问题
-
对于tcp的粘包问题请参考上一篇博客。
5.深度理解连接https://www.cnblogs.com/linhaifeng/articles/6129246.html
基于socketserver模块实现并发
了解进程:
1.进程是什么?
- 顾名思义,进程即正在执行的一个过程。进程是对正在运行程序的一个抽象。
2.进程的概念是从哪来的?
- 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一。操作系统的其他所有内容都是围绕进程的概念展开的。
3.深度理解连接
4.并发连接
实现并发:
基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环
socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)
server类:
request类:
继承关系:
TCP服务端(支持并发):
# 支持并发版本
import socketserver
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self): # 处理通信
print(self.client_address)
while True:
try:
data = self.request.recv(1024) # self.request=>conn
if len(data) == 0: break
self.request.send(data.upper())
except Exception:
break
self.request.close()
if __name__ == '__main__':
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyRequestHandler, bind_and_activate=True)
s.serve_forever()
TCP客户端:
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input(">>: ").strip()
if len(msg) == 0:
continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
UDP服务端(支持并发):
import socketserver
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self): # 处理通信
data,server=self.request
server.sendto(data.upper(),self.client_address)
if __name__ == '__main__':
s = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyRequestHandler, bind_and_activate=True)
s.serve_forever()
UDP客户端:
from socket import *
client = socket(AF_INET, SOCK_DGRAM)
while True:
msg = input('>>: ').strip()
client.sendto(msg.encode("utf-8"), ('127.0.0.1', 8080))
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))
自行阅读:
以下述代码为例,分析socketserver源码:
ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)
ftpserver.serve_forever()
查找属性的顺序:ThreadingTCPServer->ThreadingMixIn->TCPServer->BaseServer
实例化得到ftpserver,先找类ThreadingTCPServer的__init__,在TCPServer中找到,进而执行server_bind,server_active
找ftpserver下的serve_forever,在BaseServer中找到,进而执行self._handle_request_noblock(),该方法同样是在BaseServer中
执行self._handle_request_noblock()进而执行request, client_address = self.get_request()(就是TCPServer中的self.socket.accept()),然后执行self.process_request(request, client_address)
在ThreadingMixIn中找到process_request,开启多线程应对并发,进而执行process_request_thread,执行self.finish_request(request, client_address)
上述四部分完成了链接循环,本部分开始进入处理通讯部分,在BaseServer中找到finish_request,触发我们自己定义的类的实例化,去找__init__方法,而我们自己定义的类没有该方法,则去它的父类也就是BaseRequestHandler中找....
源码分析总结:
基于tcp的socketserver我们自己定义的类中的
self.server即套接字对象
self.request即一个链接
self.client_address即客户端地址
基于udp的socketserver我们自己定义的类中的
self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
self.client_address即客户端地址
努力学习!