zoukankan      html  css  js  c++  java
  • 02: SocketServer服务

    网络编程其他篇

    目录:

    1.1 SocketServer四种基本流及 异步处理理论部分     返回顶部

      1、SocketServer作用

          1.  socket无法支持多并发,SocketServer 可以实现多并发

          2.  SocketServer使编写一个Socket服务器通信变得更加简单

          3.  SocketServer其实就是对socket的再封装

      2、SocketServer提供了4个基本的服务类

          1. TCPServer针对TCP套接字流
          2. UDPServer针对UDP数据报套接字
          3. UnixStreamServer和UnixDatagramServer针对UNIX域套接字,不常用(Unix本机间通信)
          4. 它们的继承关系如下:

                               

       3、异步处理

          1.  上面四个服务类都是同步处理请求的,一个请求没处理完不能处理下一个请求

          2.  要想支持异步模型,可以利用多继承让server类继承ForkingMixInThreadingMixIn mix-in classes。

          3.  ForkingMixIn利用多进程(分叉)实现异步。

          4.  ThreadingMixIn利用多线程实现异步。

      4.、请求处理类

          1.  要实现一项服务,还必须派生一个handler class请求处理类,并重写父类的handle()方法

          2.  handle方法就是用来专门是处理请求的,该模块是通过服务类和请求处理类组合来处理请求的。

          3.  SocketServer模块提供的请求处理类有BaseRequestHandler,以及它的派生类StreamRequestHandler和DatagramRequestHandler

          4.  从名字看出可以一个处理流式套接字,一个处理数据报套接字。

      5、实现异步,支持多连接

          1.  前面介绍服务类时提到过,四个基本的服务类默认是同步模型的。

          2.  要想支持异步可以利用多继承从ForkingMixInThreadingMixInmix-in classes和一个基本的服务类继承来定义一个支持异步的服务类

          3.  比如:class Server(ThreadingMixIn, TCPServer): pass

          4.  ForkingMixIn 要考虑进程间的通信。ThreadingMixIn要考虑线程访问同一变量时的同步和互斥

    1.2 创建socketserver实现: 多客户端并发     返回顶部

      1、创建一个socketserver 至少分以下几步

          1.  创建一个request handler class(请求处理类),继承自BaseRequestHandler class并重写它的handle()方法,该方法将处理客户端的请求。

          2.  实例化一个server class对象,并将服务的地址和之前创建的request handler class传递给它。

               server class对象有这些:TCPServer;UDPServer;UnixStreamServer;UnixDatagramServer

          3.  调用server class对象的handle_request() 或 serve_forever()方法来开始处理请求

          4.  handle_request和server_forever区别是server_forever只是反复handle_request而已

    import socketserver
    class MyTcpServer(socketserver.BaseRequestHandler):
        def handle(self):
            while True:
                self.request.recv(1024)
                self.request.send('I am server'.encode('utf-8'))
    server = socketserver.ThreadingTCPServer(('127.0.0.1',8888),MyTcpServer)
    server.serve_forever()
    简化版的server端代码如下
    import socketserver
    #每个客户端过来都会实例化一个MyTCPHandler实例,并调用handle方法来处理与客户端的通信
    class MyTCPHandler(socketserver.BaseRequestHandler):
    #1 第一步:创建一个request handler class(请求处理类),继承自BaseRequestHandler class
        def handle(self):    #2 第二步:并重写它的handle()方法,该方法将处理客户端的请求
            while True:
                try:
                    self.data = self.request.recv(1024).strip()   #conn.recv(1024) = self.request.recv()
                    print("{} wrote:".format(self.client_address[0]))
                    print(self.data)
                    self.request.send(self.data.upper())      #conn.send()=self.request.sendall()
                except ConnectionResetError as e:
                    print("error",e)
                    break
    if __name__ == "__main__":
        HOST, PORT = "localhost", 9999
        server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) #socketserver.TCPServer(单)
        server.serve_forever()  #4 第四步:调用server class对象的serve_forever()方法来开始处理请求
    #3 第三步:实例化一个server class对象,并将服务的地址和之前创建的request handler class传递给它
    #注: 实例化一个server class对象工作原理
    #a. (HOST, PORT):将监听地址当做一个参数传递给socketserver.TCPServer()
    #b. 每连接一个客户端就用MyTCPHandler实例化一个对象,将实例对象传送给socketserver.TCPServer
    #c. 然后服务器端使用MyTCPHandler类中的handle方法与客户端通信
    socketserver实现多客户端并发: server端
    import socket
    client = socket.socket()                      #声明socket类型,同时生成socket连接对象
    client.connect(('localhost',9999))            #指定要连接的服务器地址和端口号
    while True:
        msg = input(">>:").strip()               #输入要发送给服务器的消息
        if len(msg) == 0:continue                #如果客户端输入空格,让客户端继续输入
        client.send(msg.encode("utf-8"))         #将输入的消息发送到服务器端
        data = client.recv(1024)                 #接收服务器端发送过来的数据,每次1024字节
        print('client_recv:',data)               #将从服务器端接收的数据在客户端打印出来
    client.close()
    socketserver实现多客户端并发: client端

    1.3 SocketServer实现多并发FTP 部分功能     返回顶部

      1、使用SocketServer完成FTP的多用户上传这个步骤

    import socketserver,os,json                #服务器端代码
    class MyTCPHandler(socketserver.BaseRequestHandler):
        def put(self,*args):
            '''接收客户端文件'''
            cmd_dic = args[0]
            filename = cmd_dic["filename"]
            filesize = cmd_dic["size"]
            if os.path.isfile(filename):
                f = open(filename + '.new','wb')
            else:
                f = open(filename,'wb')
                print("file not exist",filename)
            self.request.send(b"200 ok")
            receive_size = 0
            while receive_size < filesize:
                data = self.request.recv(1024)
                f.write(data)
                receive_size += len(data)
            else:
                print("file [%s] hsa uploaded ..."%filename)
    
        def handle(self):
            while True:
                try:
                    self.data = self.request.recv(1024).strip()
                    print("{} wrote:".format(self.client_address[0]))
                    print(self.data)
                    cmd_dic = json.loads(self.data.decode())
                    action = cmd_dic["action"]
                    if hasattr(self,action):
                        func = getattr(self,action)
                        func(cmd_dic)
                except ConnectionResetError as e:
                    print("error",e)
                    break
    if __name__ == "__main__":
        HOST, PORT = "0.0.0.0", 9999
        server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
        server.serve_forever()
    多用户上传: server端代码
    import socket,os,json
    class FtpClient(object):
        def __init__(self):
            self.client = socket.socket()
        def help(self):
            msg = '''ls; pwd; cd ..  ;get filename  ; put filename'''
            print(msg)
        def connect(self,ip,port):
            self.client.connect((ip,port))
        def interactive(self):
            #self.authenticate()
            while True:
                cmd = input(">>:").strip()
                if len(cmd) == 0:continue
                cmd_str = cmd.split()[0]
                if hasattr(self,'cmd_%s'%cmd_str):
                    func = getattr(self,'cmd_%s'%cmd_str)
                    func(cmd)
                else:
                    self.help()
        def cmd_put(self,*args):
            cmd_split = args[0].split()
            if len(cmd_split) > 1:
                filename = cmd_split[1]
                if os.path.isfile(filename):
                    filesize = os.stat(filename).st_size
                    msg_dic = {
                        "action": "put",
                        "filename":filename,
                        'size':filesize,
                        'overridden':True
                    }
                    self.client.send( json.dumps(msg_dic).encode('utf-8') )
                    server_response = self.client.recv(1024) #防止连包, 等服务器确认
                    f = open(filename,'rb')
                    for line in f:
                        self.client.send(line)
                    else:
                        print("file upload success.....")
                        f.close()
                else:
                    print(filename,"is not exist")
        def cmd_get(self):
            pass
    ftp = FtpClient()
    ftp.connect("1.1.1.3",9999)
    ftp.interactive()
    多用户上传: client端代码
    1、将server端代码复制到Linux下,路径:/bbb/server.py
    2. 将client端代码直接放到PyCharm中运行
    3. 在PyCharm中执行put tt.py将本目录下的tt.py文件上传到linux的/bbb文件夹中
        >>:put tt.py
        file upload success.....
    4. 执行命令完成后就可以看到有这个文件:/bbb/tt.py
    用户上传运行演示

      2、文件传输时显示进度条

    import sys,time
    def view_bar(num, total):
        rate = num / total
        rate_num = int(rate * 100)
        r = '
    [%s%s]%d%%' % ("="*num, " "*(100-num), rate_num, )
        sys.stdout.write(r)
        sys.stdout.flush()
    
    for i in range(0, 101):
        time.sleep(0.1)
        view_bar(i, 100)
        
    # 1)这里使用的是sys.stdout.write(r),输出结果是进度条增加,但是不会换行
    # 2)2.x中的print无法指定end符号为其他值,默认会输出一个"
    ",也就是用一次必定换到下一行
    # 3)到了3.x中print成为了一个真正意义上的函数,后来就可以任意指定end符号的值,你可以输出一次后末尾添加上任意你想要的值,而不是强制换行。
    # 4)stdout没有end这个符号这一说,输出不会换行,因此如果你想同一样输出多次,在需要输出的字符串对象里面加上"
    ",就可以回到行首了。
    打印进度条代码

    作者:学无止境
    出处:https://www.cnblogs.com/xiaonq
    生活不只是眼前的苟且,还有诗和远方。

  • 相关阅读:
    【题解】 bzoj1191: [HNOI2006]超级英雄Hero (二分图)
    谈谈Android项目框架的前世今生
    十四个好用的开源漏洞检测工具
    关于Java导出100万行数据到Excel的优化方案
    逗号分割符--字段中含逗号等情况的解析方法Java实现
    Java日期处理
    服务器&浏览器伪装的故事
    浏览器User-agent简史(user-agent)
    修改tomcat服务器banner的方法
    life_motto
  • 原文地址:https://www.cnblogs.com/xiaonq/p/7904277.html
Copyright © 2011-2022 走看看