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类:

img

request类:

img

继承关系:

img

img

img

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即客户端地址

posted @ 2020-08-18 19:06  Orange-ONE  阅读(120)  评论(0编辑  收藏  举报