• python--socket/Socketerver并发/udp


    Socketerve并发

    基于tcp套接字,关键就是两个循环,一个链接循环,一个通讯循环

    Socketserver模块中分两个大类:server类(解决链接问题)和request类(解决通信问题)

    server类:

    request类:

     

    继承关系:

    以下述代码为例,分析socketserver源码:

    ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)
    ftpserver.serve_forever()

    查找属性的顺序:

    ThreadingTCPServer-->ThreadingMixIn-->TCPServer-->BaseServer

    1.实例化得到tfpserver,先找类ThreadingTCPServer的_init_,在TCPServer中找到,进而执行Server_bind,server_active

    2.找ftpsever下的server_forever,在BaseServer中找到,进而执行self._handle_request_noblock(),该方法同样是在BaseServer中

    3.执行self._handle_request_noblcok()进而执行request,client_address=self.get_request()(就是TCPServer中的self.socket.accept()),然后执行self.process_request(request,client_address)

    4.在ThreadingMiIn中找到process_request,开启多线程应对并发,进而执行process_request_thread,self.finish_request(request,client_address)

    5.上述四部分完成了链接循环,本部分开始进入处理通讯部分,在BaseServer中找到finish_request,触发我们自己定义的类的实例化,去找_init_方法,而我们自己定义的类没有该方法,则去它的父类也就是BaseRequestHandler中找 

    源代码分析总结:

    基于tcp的socketserver我们自己定义的类中的

    1.self.server即套接字对象

    2.self.request即一个链接

    3.self.client_adderss即客户端地址

    #服务端:
    import  socketserver
    class FTPserver(socketserver.BaseRequestHandler):#定义一个类 继承BaseRequestHandler #进行通讯
        def handle(self): 
            # print(self)
            # print(self.request) #拿到一个conn链接循环
            while True: #通信循环
                data=self.request.recv(1024)
                print(data.decode("utf-8"))
                self.request.send(data.upper())
    
    if __name__ == '__main__':
        obj=socketserver.ThreadingTCPServer(("127.0.0.1",8000),FTPserver) #自己的类名
        obj.serve_forever()#链接循环
    
    
    
    
    #客户端1
    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(("127.0.0.1",8000))
    
    while True:
        msg=input(">>").strip()
        if not msg:continue
        s.send(msg.encode("utf-8"))
        data=s.recv(1024)
    s.close()
    
    
    
    #客户端2
    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(("127.0.0.1",8000))
    
    while True:
        msg=input(">>").strip()
        if not msg:continue
        s.send(msg.encode("utf-8"))
        data=s.recv(1024)
    s.close()
    
    
    
    #客户端3
    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(("127.0.0.1",8000))
    
    while True:
        msg=input(">>").strip()
        if not msg:continue
        s.send(msg.encode("utf-8"))
        data=s.recv(1024)
    s.close()
    

    基于UDP的套接字

    udp:sendto发消息,recvfrom收消息

    udp是基于数据报

    sendinto

    sendinto(bytes_data,ip_port):发送数据报,bytes_data为空,还有ip_port,所有即便是发送空的bytes_data,数据报其实也不是空的,自己这端的缓冲区收到内容,操作系统就会控制udp协议发包

    recvfrom

    udp协议

    (1)如果如果收消息缓冲区里的数据为“空”,recvfrom也会阻塞

    (2)只不过udp协议的客户端sendinto一个空数据并不是真的空数据(包含:空数据+地址信息,得到的报仍然不会为空),所以客户端只要有一个sendinto(不管是否发送空数据,都不是真的空数据),服务端就可以recvfrom到数据。

    (3)udp无链接

    无链接,因而无需listen(backlog),更加没有什么连接池之说了

    无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失

    recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错

    只有sendinto发送数据没有recvfrom收数据,数据丢失

    udp套接字简单示例:

    #udp服务端
    import socket
    ip_port=("127.0.0.1",8000)
    buffer_size=1024
    udp_server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #数据报
    udp_server.bind(ip_port)
    while True: #通信循环
        data,addr=udp_server.recvfrom(buffer_size)
        print(data.decode("utf-8"),addr)
        msg=input(">>:")
    udp_server.sendto(msg.encode("utf-8"),addr)
        udp_server.sendto(data.upper(),addr)
    udp_serve.close()
    
    #udp客户端
    import socket
    ip_port=("127.0.0.1",8000)
    buffer_size=1024
    udp_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #数据报
    while True: #通信循环
        msg=input(">>:").strip()
        udp_client.sendto(msg.encode("utf-8"),ip_port) #每次发包都要指定端口
    
        data,addr= udp_client.recvfrom(buffer_size)
        print(data.decode("utf-8"))
    udp_client.close()
    
    #udp客户端1
    import socket
    ip_port=("127.0.0.1",8000)
    buffer_size=1024
    udp_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #数据报
    while True: #通信循环
        msg=input(">>:").strip()
        udp_client.sendto(msg.encode("utf-8"),ip_port) #每次发包都要指定端口
    
        data,addr= udp_client.recvfrom(buffer_size)
        print(data.decode("utf-8"))
    udp_client.close()
    
    
    #udp客户端2
    
    import socket
    ip_port=("127.0.0.1",8000)
    buffer_size=1024
    udp_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #数据报
    while True: #通信循环
        msg=input(">>:").strip()
        udp_client.sendto(msg.encode("utf-8"),ip_port) #每次发包都要指定端口
    
        data,addr= udp_client.recvfrom(buffer_size)
        print(data.decode("utf-8"))
    udp_client.close()
    
  • 相关阅读:
    idae修改默认maven全局设置以及maven的设置
    LINUX 基本察看命令
    tar解压bz2文件报错
    kafka和zookeeper集群部署
    elasticsearch集群部署和kibana插件部署
    tomcat JVM调优
    搭建zookeeper集群的坑
    判断链表是否有环,以及求出入环节点
    判断一个数是否是完全二叉树
    堆排序
  • 原文地址:https://www.cnblogs.com/niejinmei/p/6814417.html
Copyright © 2020-2023  润新知