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 = '\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符号为其他值,默认会输出一个"\n",也就是用一次必定换到下一行
# 3)到了3.x中print成为了一个真正意义上的函数,后来就可以任意指定end符号的值,你可以输出一次后末尾添加上任意你想要的值,而不是强制换行。
# 4)stdout没有end这个符号这一说,输出不会换行,因此如果你想同一样输出多次,在需要输出的字符串对象里面加上"\r",就可以回到行首了。
打印进度条代码

 

posted @ 2017-11-27 15:16  不做大哥好多年  阅读(235)  评论(0编辑  收藏  举报