码迷,mamicode.com
首页 > 编程语言 > 详细

Python--socket和threading编程

时间:2016-07-31 20:30:53      阅读:307      评论:0      收藏:0      [点我收藏+]

标签:

网络编程基础(一)

    •   TCP/IP协议
      •   OSI/RM协议
      •   特点:独立于计算机硬件和操作系统,统一分配网络地址,提供可靠服务,隔离了物理网络的硬件差异
      •   协议分层(百度):网络接口层:IEE802.3(以太网协议集),IEEE802.4(令牌环网);网络层(IP);传输层(tcp/udp);应用层(FTP/HTTP/SMTP/DNS)
    •   IP地址和端口
  • 网络编程基础(二)
    •   UDP协议
    •   TCP协议
    •   套接字Socket
      •   TCP连接的端点称作套接字
      •   表示方法:IP地址:端口号,一个socket就是:(ip地址:端口号)
      •   一个TCP连接就是两个套接字,也就是{(IP地址:端口号),(IP地址:端口号)}
      •   每一条TCP连接被两个套接字确定,同一个ip地址可以有不同的TCP连接,同一个端口号可以出现在不同的TCP连接中
    •   TCP和UDP的不同点
      •   TCP先建立连接,再通信,最后释放连接,udp不用连接
      •   TCP保证数据可靠交付;TCP不保证可靠交付,用户自行处理可靠性
      •   TCP连接开销大,UDP小;TCP使用实时性低,数据可靠性高的场合,UDP适合实用性高,数据可靠性低的场合
    •   TCP和UDP的相同点
      •   都位于TCP/IP协议的第四层
      •   为应用层提供服务,都要通过网际层来一线数据的传输ICMP协议
    •   TCP协议
      •   HTTP,FTP,TELNET,POP,SMTP
    •   UDP协议
      •   TFTP,DNS,SNMP,VOIP,QQ
  • 服务器端socket的建立
    •   C/S模式简介:客户/服务器模式,客户端为主动向服务器发出服务请求的一方。服务器一般在系统启动时自动调用运行,等待客户机的请求
      与C/S模式相对的是B/S(浏览器/服务器模式),客户端使用同意的浏览器,而不用装门部署,服务器和浏览器使用HTTP协议进行通信
    •   套接字网络编程
      •   TCP通信
      • 技术分享
      •   UDP通信
      • 技术分享
  • Python中的socket
    •   socket对象是支持网络通信的,socket对象支持使用TCP/UDP协议进行网络通信(只能选择其中一个协议)
    •   socket编程所需要的对象函数和常量
    •   创建套接字:socket.socket(family,type)   family表示套接字使用什么版本协议    Type=SOCK_STREAM(使用TCP)   type=sock_DGRAM(UDP协议)
    •   服务器端套接字的方法
      •   bind(address)绑定,address为(ip:port):将套接字绑定到本地主机的ip或者端口上;
      •   listen(backlog):开始Tcp转入连接。backlog拒绝连接前允许操作系统挂起的连接数,1-5
      •   accept():接收TCP连接,并返回连接上的套接字对象和客户端地址构成的元组。返回的连接上的套接字对象可用于接收和发送信息
    •   客户端socket对象的方法
      •   connect(address),address=(hostname,port)构成的元组,建立与服务器间的连接
    •   TCP协议的socket收发数据的方法
      •   recv(【buffersize】):接收数据,参数为接收最大数据量,返回接收的数据
      •   send(bytes)通过socket发送数据
      •   sendall(bytes)通过socket发送数据(返回前将数据都发送出去)
    •   UDP协议的socket收发数据方法
      •   recvfrom(与上面类似)
      •   sendto(bytes,address)发送的字节和指定发送的目的地址
    •   关闭socket;close()
    •   其他相关函数
      •   gethostname()返回本地主机名
      •   gethostbyname_ex(hostname)#返回元组(hostname,aliaslist,ipaddrrlist)
    •   用socket建立TCP服务器端方法
      •   基本步骤
        •   创建套接字并绑定地址,开始监听连接,接收连接并收发数据,关闭套接字
        • 技术分享
          #coding=gbk
          
          import socket
          
          HOST = ‘‘
          PORT = 321
          
          s=socket.socket()
          s.bind((HOST,PORT))
          
          s.listen(5)
          
          client,addr=s.accept()#接收客户端的连接,返回一个客户端,
          
          print(client address:,addr)
          
          while True:
              data = client.recv(1024)#接收数据
              if not data:#为空,断开连接
                  break
              else:
                  print(receive data :,data.decode(utf-8))#数据不为空,输出
              client.send(data)#将数据发挥客户端
          client.close()
          s.close()
          View Code
    •   用socket建立UDP服务器端方法
      •   基本步骤
        •   创建套接字并绑定地址,开始监听连接,收发数据,关闭套接字
        • 技术分享
          #coding=gbk
          
          import socket
          
          HOST = ‘‘
          PORT = 3214
          
          #socket.socket(family,type)   family表示套接字使用什么版本协议    Type=SOCK_STREAM(使用TCP)   type=sock_DGRAM(UDP协议)
          s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#使用iPv4协议
          s.bind((HOST,PORT))
          
          data = True
          
          
          while data:
              data,addr = s.recvfrom(1024)
              if data==bbye:#为空,断开连接
                  break
              else:
                  print(receive data :,data.decode(utf-8))#数据不为空,输出
              s.send(data,addr)#将数据发挥客户端
          s.close()
          View Code
  • 客户端socket建立
    •   socket的异常
      •   error:套接字或者地址有关的错误
      •   herror:error的子类,表示发生与地址有关的错误
      •   gaierror:getaddressinfo()或者gethostinfo()中的与地址有关的错误
      •   timeout:套接字超时错误
      •   处理异常
        •   try,catch进行
    •   用TCP实现客户端
      •   基本步骤
        •   创建套接字,用套接字连接服务器;收发数据;关闭套接字
          技术分享
          #coding=gbk
          import socket
          
          
          HOST = 127.0.0.1
          PORT = 3215
          s=socket.socket()
          
          try:
              s.connect((HOST,PORT))
              data="nihao!"
              while data:
                  s.sendall(data.encode(utf-8))#编码发送出去的信息
                  data=s.recv(1024)#接收数据
                  print(reveive is :\n,data.decode(utf-8))#解码打印收到的数据
                  data=input(please input string:\n)
          except socket.errno as err:
              print(err)
          finally:
              s.close()
              
                  
          View Code
    •   用UDP实现客户端
      •   基本步骤
        •   创建套接字,收发数据,关闭套接字
        • 技术分享
          #coding=gbk
          import socket
          
          
          HOST = 127.0.0.1
          PORT = 3215
          s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#使用iPv4协议)
          
          data=nihao
          while data:
                  s.sendto(data.encode(utf-8),(HOST,PORT))#编码发送出去的信息
                  if data==bye:
                      break
                  data,addr=s.recvfrom(1024)
                  print(receive is :\n,data.decode(utf-8))
              
                  data=input(please input string:\n)
          
          s.close()
              
                  
          View Code
  • 备份服务器的服务器端的实现
    •   尝试一个C/S模式下网络远程备份系统,C/S模式下开发服务器端和客户端,穿送的是文件所以使用TCP协议进行
    •   备份服务器的功能分析
      •   可以自定义服务的IP的地址和端口号
      •   指定保存备份文件的目录
      •   关闭服务器
      •   以多线程的方式同时为多个客户端提供备份服务
      • 技术分享
        #coding=gbk
        from tkinter import *
        from tkinter.ttk import *
        import socket
        
        import struct
        
        
        def start(host,port):
            pass
        
        def MyFrame(Frame):
            #初始化构造方法
            def __init__(self,root):
                super().__init__(root)
                self.root=root
                self.grid()#布局方式网格布局
                self.local_ip=127.0.0.1#服务器端默认的IP地址
                self.serv_ports=[10888,20888,30888]#三个服务器的端口
                self.init_components()#初始化界面方法
            
            #界面函数的实现
            def init_components(self):#初始化用户界面的方法
                proj_name=Label(self,text=远程备份服务器)
                proj_name.grid(columnspan=2)#网格式布局,占用两列
                
                serve_ip_label=Label(self,text=服务地址)
                serve_ip_label.grid(row=1)#列是默认为0列
                
                #下拉列表,显示服务器的地址拱用户选择
                self.serv_ip=Combobox(self,values=self.get_ipaddr())
                #设置默认的服务器的ip地址
                self.serv_ip.set(self.local_ip)
                self.serv_ip.grid(row=1,column=1)
                
                #服务端口的LABEL
                serv_port_label=Label(self,text=服务端口)
                serv_port_label.grid(row=2) 
                #下拉列表,显示服务器的服务端口拱用户选择
                self.serv_port=Combobox(self,values=self.serv_ports)
                #设置默认的服务端口
                self.serv_port.set(self.serv_ports[1])
                #网格布局  放置指定位置
                self.serv_port.grid(row=2,column=1)
                
                #启动按钮
                self.start_serv_btn=Button(self,text=启动服务,command=self.start_serv)
                self.start_serv_btn.grid(row=3)
                
                #退出服务的按钮
                self.start_exit_btn=Button(self,text=退出服务,command=self.root.destroy)
                self.start_exit_btn.grid(row=3,column=1)
                
            def get_ipaddr(self):
                #获取本机的ip地址
                #获取名字
                host_name=socket.gethostname()
                #获取信息
                info=socket.gethostbyname_ex(host_name)
                info=info[2]
                info.append(self.local_ip)
            
            #定义启动服务器的方法    
            def start_serv(self):
                print(self.serv_ip.get(),self.serv_port.get())
                start(self.serv_ip.get(),self.serv_port.get())
                
        if __name__==__main__:
            root=Tk()
            root.title(备份服务器)
            root.resizable(FALSE, FALSE)#允许用户更改窗体大小
            a=MyFrame(root)
            a.mainloop()
        View Code
    •    最简备份服务器端建立
      •   同一时间只能连接一个客户并且为其备份
      •   备份客户端的一个目录及其子目录的所有文件
      •   与客户端交互
        •   客户端发送即将要发送文件信息的大小
        •   服务器端接收客户端通知的文件信息的大小
        •   客户端发送文件信息(包括文件大小。文件名)
        •   服务器端依照文件信息的大小接收文件信息
        •   客户端逐个发送文件数据,每发送完一个文件数据,接收该文件的备份结果
        •   服务器端接收文件数据并保存备份至文件系统,每接收完一个文件就返回备份结果
        • 技术分享
          #coding=gbk
          from tkinter import *
          from tkinter.ttk import *
          import socket
          import os
          import struct
          import pickle
          #建立一个默认的备份目录
          BAK_PATH=re:\bak
          
          #根据指定长度来接受文件信息
          def recv_unit_data(clnt,infos_len):
              data=b‘‘ #原来文件为空
              #如果要接受的文件在0-1024之间就直接接收该文件,如果大于1024需要循环分段接收,每次只是接收1024个字节,剩下的在全部接收即可
              if 0<infos_len<=1024:#
                  data+=clnt.recv(infos_len)#接收文件
              else:#长度太长
                  while  True: 
                      if infos_len >1024:
                         data+=clnt.recv(1024) 
                         infos_len-=1024
                      else:
                          data+=clnt.recv(infos_len)
                          break
              return data
          
          def get_files_info(clnt):
              fmt_str=Q#用于向服务器端传送文件信息的大小
              headsize=struct.calcsize(fmt_str)#计算长度
              data=clnt.recv(headsize)
              infos_len=struct.unpack(fmt_str, data)[0]
              data =recv_unit_data(clnt, infos_len)
              return pickle.loads(data)#得到文件信息的列表
             
          def mk_math(filepath): #建立文件路径
              paths=filepath.split(os.path.sep)[:-1]#将文件路径进行分割
              p=BAK_PATH
              for path in paths:#遍历用户端传来的路径
                  p=os.path.join(p,path)#将保存的路径添加到默认的路径上
                  if not os.path.exists(p):#如果路径不存在就建立路径
                      os.mkdir(p)
                      
          #接收客户端传来文件,并且根据文件信息来进行保存备份
          def recv_file(clnt,infos_len,filepath):            
              mk_math(filepath)#遍历文件   通过路径
              filepath = os.path.join(BAK_PATH,filepath)#服务器上的路径的文件名
              f = open(filepath,wb+)#新建一个文件
              #接收文件
              try:
                  if 0 < infos_len <=1024:
                      data = clnt.recv(infos_len)
                      f.write(data)
                  else:
                      while True:
                          if infos_len >1024:
                              data=clnt.recv(1024)
                              f.write(data)
                              infos_len-=1024
                          else:
                              data = clnt.recv(infos_len)
                              f.write(data)
                              break
              except:
                  print(error)
              else:
                  return True
              finally:
                  f.close()
            
            #向客户端发送失败成功消息      
          def send_echo(clnt,res):
              if res:
                  clnt.sendall(bsuccess)
              else:
                  clnt.sendall(bfailure)
                  
          #启动服务器的方法
          def start(host,port):
              if not os.path.exists(BAK_PATH):
                  os.mkdir(BAK_PATH)
              st=socket.socket() #tcp协议
              st.bind(host,port) #绑定套接字
              st.listen(1) #侦听网络,一个客户端连接
              client,addr=st.accept() #接收连接,建立连接
              
              files_lst=get_files_info(client)#获取客户端要传送的文件列表(包括文件大小和文件路径:元组)
              for size,filepath in files_lst:#遍历得到文件大小和路径
                  res = recv_file(client,size,filepath)#接收所有的文件    返回备份的结果:true或者false
                  send_echo(client,res)#保存成功标志,发送给客户端
                  
              client.close()#关闭客户端
              st.close()
          
          def MyFrame(Frame):
              #初始化构造方法
              def __init__(self,root):
                  super().__init__(root)
                  self.root=root
                  self.grid()#布局方式网格布局
                  self.local_ip=127.0.0.1#服务器端默认的IP地址
                  self.serv_ports=[10888,20888,30888]#三个服务器的端口
                  self.init_components()#初始化界面方法
              
              #界面函数的实现
              def init_components(self):#初始化用户界面的方法
                  proj_name=Label(self,text=远程备份服务器)
                  proj_name.grid(columnspan=2)#网格式布局,占用两列
                  
                  serve_ip_label=Label(self,text=服务地址)
                  serve_ip_label.grid(row=1)#列是默认为0列
                  
                  #下拉列表,显示服务器的地址拱用户选择
                  self.serv_ip=Combobox(self,values=self.get_ipaddr())
                  #设置默认的服务器的ip地址
                  self.serv_ip.set(self.local_ip)
                  self.serv_ip.grid(row=1,column=1)
                  
                  #服务端口的LABEL
                  serv_port_label=Label(self,text=服务端口)
                  serv_port_label.grid(row=2) 
                  #下拉列表,显示服务器的服务端口拱用户选择
                  self.serv_port=Combobox(self,values=self.serv_ports)
                  #设置默认的服务端口
                  self.serv_port.set(self.serv_ports[1])
                  #网格布局  放置指定位置
                  self.serv_port.grid(row=2,column=1)
                  
                  #启动按钮
                  self.start_serv_btn=Button(self,text=启动服务,command=self.start_serv)
                  self.start_serv_btn.grid(row=3)
                  
                  #退出服务的按钮
                  self.start_exit_btn=Button(self,text=退出服务,command=self.root.destroy)
                  self.start_exit_btn.grid(row=3,column=1)
                  
              def get_ipaddr(self):
                  #获取本机的ip地址
                  #获取名字
                  host_name=socket.gethostname()
                  #获取信息
                  info=socket.gethostbyname_ex(host_name)
                  info=info[2]
                  info.append(self.local_ip)
              
              #定义启动服务器的方法    
              def start_serv(self):
                  print(self.serv_ip.get(),self.serv_port.get())
                  start(self.serv_ip.get(),int(self.serv_port.get()))
                  
          if __name__==__main__:
              root=Tk()
              root.title(备份服务器)
              root.resizable(FALSE, FALSE)#允许用户更改窗体大小
              a=MyFrame(root)
              a.mainloop()
                  
          View Code
  • 备份服务器的基本客户端实现
    •   功能
      •   设置连接服务器的IP地址和端口号
      •   输入备份目录,备份其中的所有文件
      •   显示服务器端发来的备份结果
      •   选择备份时启用压缩备份
  •   客户端与服务器最终版
  • 技术分享
    #coding=gbk
    from tkinter import *
    from tkinter.ttk import *
    import socket
    import os
    import struct
    import pickle
    #建立一个默认的备份目录
    BAK_PATH=re:\bak
    
    #根据指定长度来接受文件信息
    def recv_unit_data(clnt,infos_len):
        data=b‘‘ #原来文件为空
        #如果要接受的文件在0-1024之间就直接接收该文件,如果大于1024需要循环分段接收,每次只是接收1024个字节,剩下的在全部接收即可
        if 0<infos_len<=1024:#
            data+=clnt.recv(infos_len)#接收文件
        else:#长度太长
            while  True: 
                if infos_len >1024:
                   data+=clnt.recv(1024) 
                   infos_len-=1024
                else:
                    data+=clnt.recv(infos_len)
                    break
        return data
    
    def get_files_info(clnt):
        fmt_str=Q#用于向服务器端传送文件信息的大小
        headsize=struct.calcsize(fmt_str)#计算长度
        data=clnt.recv(headsize)
        infos_len=struct.unpack(fmt_str, data)[0]
        data =recv_unit_data(clnt, infos_len)
        return pickle.loads(data)#得到文件信息的列表
       
    def mk_math(filepath): #建立文件路径
        paths=filepath.split(os.path.sep)[:-1]#将文件路径进行分割
        p=BAK_PATH
        for path in paths:#遍历用户端传来的路径
            p=os.path.join(p,path)#将保存的路径添加到默认的路径上
            if not os.path.exists(p):#如果路径不存在就建立路径
                os.mkdir(p)
                
    #接收客户端传来文件,并且根据文件信息来进行保存备份
    def recv_file(clnt,infos_len,filepath):            
        mk_math(filepath)#遍历文件   通过路径
        filepath = os.path.join(BAK_PATH,filepath)#服务器上的路径的文件名
        f = open(filepath,wb+)#新建一个文件
        #接收文件
        try:
            if 0 < infos_len <=1024:
                data = clnt.recv(infos_len)
                f.write(data)
            else:
                while True:
                    if infos_len >1024:
                        data=clnt.recv(1024)
                        f.write(data)
                        infos_len-=1024
                    else:
                        data = clnt.recv(infos_len)
                        f.write(data)
                        break
        except:
            print(error)
        else:
            return True
        finally:
            f.close()
      
      #向客户端发送失败成功消息      
    def send_echo(clnt,res):
        if res:
            clnt.sendall(bsuccess)
        else:
            clnt.sendall(bfailure)
            
    #启动服务器的方法
    def start(host,port):
        if not os.path.exists(BAK_PATH):
            os.mkdir(BAK_PATH)
        st=socket.socket() #tcp协议
        st.bind((host,port)) #绑定套接字
        st.listen(1) #侦听网络,一个客户端连接
        client,addr=st.accept() #接收连接,建立连接
        
        files_lst=get_files_info(client)#获取客户端要传送的文件列表(包括文件大小和文件路径:元组)
        for size,filepath in files_lst:#遍历得到文件大小和路径
            res = recv_file(client,size,filepath)#接收所有的文件    返回备份的结果:true或者false
            send_echo(client,res)#保存成功标志,发送给客户端
            
        client.close()#关闭客户端
        st.close()
    
    class MyFrame(Frame):
        #初始化构造方法
        def __init__(self,root):
            super().__init__(root)
            self.root=root
            self.grid()#布局方式网格布局
            self.local_ip=127.0.0.1#服务器端默认的IP地址
            self.serv_ports=[10888,20888,30888]#三个服务器的端口
            self.init_components()#初始化界面方法
        
        #界面函数的实现
        def init_components(self):#初始化用户界面的方法
            proj_name=Label(self,text=远程备份服务器)
            proj_name.grid(columnspan=2)#网格式布局,占用两列
            
            serve_ip_label=Label(self,text=服务地址)
            serve_ip_label.grid(row=1)#列是默认为0列
            
            #下拉列表,显示服务器的地址拱用户选择
            self.serv_ip=Combobox(self,values=self.get_ipaddr())
            #设置默认的服务器的ip地址
            self.serv_ip.set(self.local_ip)
            self.serv_ip.grid(row=1,column=1)
            
            #服务端口的LABEL
            serv_port_label=Label(self,text=服务端口)
            serv_port_label.grid(row=2) 
            #下拉列表,显示服务器的服务端口拱用户选择
            self.serv_port=Combobox(self,values=self.serv_ports)
            #设置默认的服务端口
            self.serv_port.set(self.serv_ports[1])
            #网格布局  放置指定位置
            self.serv_port.grid(row=2,column=1)
            
            #启动按钮
            self.start_serv_btn=Button(self,text=启动服务,command=self.start_serv)
            self.start_serv_btn.grid(row=3)
            
            #退出服务的按钮
            self.start_exit_btn=Button(self,text=退出服务,command=self.root.destroy)
            self.start_exit_btn.grid(row=3,column=1)
            
        def get_ipaddr(self):
            #获取本机的ip地址
            #获取名字
            host_name=socket.gethostname()
            #获取信息
            info=socket.gethostbyname_ex(host_name)
            info=info[2]
            info.append(self.local_ip)
        
        #定义启动服务器的方法    
        def start_serv(self):
            print(self.serv_ip.get(),self.serv_port.get())
            start(self.serv_ip.get(),int(self.serv_port.get()))
            
    if __name__==__main__:
        root=Tk()
        root.title(备份服务器)
        root.resizable(FALSE, FALSE)#允许用户更改窗体大小
        a=MyFrame(root)
        a.mainloop()
    
    
    
    
    #coding=gbk
    from tkinter import *
    from tkinter.ttk import *
    import socket
    import os
    import pickle
    import struct
    
    #获取给出路径的文件信息
    def get_file_info(path):
        if not path or not os.path.exists(path):
            return NONE
        files=os.walk(path)#获取文件
        infos=[]
        file_paths=[]
        for p,d,fs in files:#文件都在fs列表中
            for f in fs:
                file_name=os.path.join(p,f)#获取文件的文件名
                file_size=os.stat(file_name).st_size#获取文件的大小
                file_paths.append(file_name)#加入到file_path
                file_name=file_name[len(path)+1:]
                infos.append((file_size,file_name))#将文件信息加入到文件信息中
        return infos,file_paths
    
    #向服务器端发送文件信息
    def send_files_infos(my_sock,infos):
        fmt_str=Q
        infos_bytes=pickle.dumps(infos)#对文件信息进行二进制编码
        infos_bytes_len=len(infos_bytes)#获取长度
        infos_len_pack=struct.pack(fmt_str,infos_bytes_len)#对长度利用struct进行二进制编码
        my_sock.sendall(infos_len_pack)#将整个发送放到服务器端
        my_sock.sendall(infos_bytes)#发送文件信息
    
    def send_files(my_sock,file_path):#本机文件,本机文件路径
        f = open(file_path,rb)
        try:
            while True:
                data=f.read(1024)
                if data:
                    my_sock.sendall(data)#发送
                else:
                    break
        finally:
                    f.close()
    
    def get_bak_info(my_sock,size=7):
        info = my_sock.recv(size)
        print(info.decode(utf-8))
    
    def start(host,port,src):
        if not os.path.exists(src):
            print(备份的目标不存在!)
            return 
        s = socket.socket()#TCP协议
        s.connect((host,port))
        path = src#获取用户的备份路径
        file_infos,file_paths=get_file_info(path)#获取要备份的文件信息和路径
        send_files_infos(s,file_infos)#发送文件信息
        for fp in file_paths:#发送所有信息至S
            send_files(s,fp)
            print(fp)#把发送出去的文件的信息打印
            get_bak_info(s)#获取备份的结果
        s.close()
        
        
    class MyFrame(Frame):
        #初始化构造方法
        def __init__(self,root):
            super().__init__(root)
            self.root=root
            self.grid()#布局方式网格布局
            self.remote_ip=127.0.0.1#服务器端的IP地址默认值
            self.remote_ports=10888#默认的端口
            self.remote_ip_var=StringVar()#输入框
            self.remote_ports_var=IntVar()
            self.bak_src_var=StringVar()
            
            self.init_components()#初始化界面方法
        
        #界面函数的实现
        def init_components(self):#初始化用户界面的方法
            proj_name=Label(self,text=远程备份客户端)
            proj_name.grid(columnspan=2)#网格式布局,占用两列
            
            serve_ip_label=Label(self,text=服务地址:)
            serve_ip_label.grid(row=1)#列是默认为0列
            
            #下拉列表,显示服务器的地址拱用户选择
            self.serv_ip=Entry(self,textvariable=self.remote_ip_var)
            #设置默认的服务器的ip地址e
            self.remote_ports_var.set(self.remote_ip)
            self.serv_ip.grid(row=1,column=1)
            
            #服务端口的LABEL
            serv_port_label=Label(self,text=服务端口:)
            serv_port_label.grid(row=2) 
            #下拉列表,显示服务器的服务端口拱用户选择
            self.serv_port=Entry(self,textvariable=self.remote_ports_var)
            #设置默认的服务端口
            self.remote_ports_var.set(self.remote_ports)
            #网格布局  放置指定位置
            self.serv_port.grid(row=2,column=1)
            
            #用户备份的数据
            src_label=Label(self,text=备份的目标:)
            src_label.grid(row=3)
            
            #输入框
            self.bak_src=Entry(self,textvariable=self.bak_src_var)
            self.bak_src.grid(row=3,column=1
                              )
            #
            self.start_serv_btn=Button(self,text=开始备份,command=self.start_send)
            self.start_serv_btn.grid(row=4)
            
            #
            self.start_exit_btn=Button(self,text=退出程序,command=self.root.destroy)
            self.start_exit_btn.grid(row=4,column=1)
    
        
        #定义启动服务器的方法    
        def start_send(self):
            print(self.remote_ip_var.get(),self.remote_ports_var.get())
            print(start...)
            start(self.remote_ip_var.get(),int(self.remote_ports_var.get()),self.bak_src_var.get())#想服务器发送东西
            
    if __name__==__main__:
        root=Tk()
        root.title(远程备份客户机)
        root.resizable(FALSE, FALSE)#允许用户更改窗体大小
        a = MyFrame(root)
        a.mainloop()
    View Code
  • 通过多线程实现备份服务器端
    •   单线程服务端问题
      •   启动服务,界面停止响应
      •   一个客户端正在备份,其他客户端不能连接
    •   建立多线程服务器
      •   解决点击启动服务,页面停止响应的问题,实现多个客户端进行交互
      •   退出服务器:将服务线程配置为后台线程(可能使文件丢失);应用线程同步的手段退出服务(这个方法好)
      •   可压缩备份服务
        •   客户端发送已压缩文件
        •   与客户端交互基本流程
  • 通过多线程实现备份客户端
  • 最终版
  • 技术分享
    #coding=gbk
    from tkinter import *
    from tkinter.ttk import *
    import socket
    import threading
    import os
    import struct
    import pickle
    import threading
    
    #使用户图形界面和服务器退出循环变量
    SERV_RUN_FLAG=TRUE
    flag_lock = threading.Lock()
    #建立一个默认的备份目录
    BAK_PATH=re:\bak
    
    #根据指定长度来接受文件信息
    def recv_unit_data(clnt,infos_len):
        data=b‘‘ #原来文件为空
        #如果要接受的文件在0-1024之间就直接接收该文件,如果大于1024需要循环分段接收,每次只是接收1024个字节,剩下的在全部接收即可
        if 0<infos_len<=1024:#
            data+=clnt.recv(infos_len)#接收文件
        else:#长度太长
            while  True: 
                if infos_len >1024:
                   data+=clnt.recv(1024) 
                   infos_len-=1024
                else:
                    data+=clnt.recv(infos_len)
                    break
        return data
    
    def get_files_info(clnt):
        fmt_str=Q?#用于向服务器端传送文件信息的大小,文件信息的压缩选项
        headsize=struct.calcsize(fmt_str)#计算长度
        data=clnt.recv(headsize)
        infos_len,compress=struct.unpack(fmt_str, data)
        data =recv_unit_data(clnt, infos_len)
        return pickle.loads(data),compress#得到文件信息的列表
       
    def mk_math(filepath): #建立文件路径
        paths=filepath.split(os.path.sep)[:-1]#将文件路径进行分割
        p=BAK_PATH
        for path in paths:#遍历用户端传来的路径
            p=os.path.join(p,path)#将保存的路径添加到默认的路径上
            if not os.path.exists(p):#如果路径不存在就建立路径
                os.mkdir(p)
                
    def get_compress_size(clnt):            
        fmt_str = Q#长整型
        size=struct.calcsize(fmt_str)
        data = clnt.recv(size)
        size,=struct.unpack(fmt_str,data)#得到压缩后文件的大小
        return size
        
                
    #接收客户端传来文件,并且根据文件信息来进行保存备份
    def recv_file(clnt,infos_len,filepath,compress):            
        mk_math(filepath)#遍历文件   通过路径
        filepath = os.path.join(BAK_PATH,filepath)#服务器上的路径的文件名
        #根据压缩选项判断
        if compress :
            infos_len = get_compress_size(clnt)#压缩后文件的长度
            filepath = ‘‘.join(os.path.splitext(filepath)[0],.tar.gz)
        f = open(filepath,wb+)#新建一个文件
        #接收文件
        try:
            if 0 < infos_len <=1024:
                data = clnt.recv(infos_len)
                f.write(data)
            else:
                while True:
                    if infos_len >1024:
                        data=clnt.recv(1024)
                        f.write(data)
                        infos_len-=1024
                    else:
                        data = clnt.recv(infos_len)
                        f.write(data)
                        break
        except:
            print(error)
        else:
            return True
        finally:
            f.close()
      
      #向客户端发送失败成功消息      
    def send_echo(clnt,res):
        if res:
            clnt.sendall(bsuccess)
        else:
            clnt.sendall(bfailure)
    
    
    def client_operate(client):
            #compress 可压缩选项
            files_lst,compress=get_files_info(client)#获取客户端要传送的文件列表(包括文件大小和文件路径:元组)
            for size,filepath in files_lst:#遍历得到文件大小和路径
                res = recv_file(client,size,filepath,compress)#接收所有的文件    返回备份的结果:true或者false
                send_echo(client,res)#保存成功标志,发送给客户端
            
            client.close()#关闭客户端
           
    #启动服务器的方法
    def start(host,port):
        if not os.path.exists(BAK_PATH):
            os.mkdir(BAK_PATH)
        st=socket.socket() #tcp协议
        st.settimeout(1)        #为了退出的时候能够有时间获得共享资源的锁,保证服务器端正常的退出;防止while中可以退出
        st.bind((host,port)) #绑定套接字
        st.listen(1) #侦听网络,一个客户端连接
        #获得serv_run_falg的访问权
        flag_lock.acquire()
        while SERV_RUN_FLAG:#多次服务     多线程
            flag_lock.release()#释放访问权
            client=None
            try:
                
                client,addr=st.accept() #接收连接,建立连接
            #线程化的启动client_operater函数       防止当前进程正在执行的时候其他线程也要进程这个服务,所以我们就每次有客户端想要进行连接的时候,我们就创建一个线程去为每一个要求服务的东西提供服务
            #多个服务器为多个客户端进行服务
            except socket.timeout:#超时
                pass
            if client: 
                t =threading.Thread(target=client_operate,args=(client,))
                t.start()
            flag_lock.acquire()#为了下次进入循环的时候仍然要锁定共享变量
        st.close()
    
    class MyFrame(Frame):
        #初始化构造方法
        def __init__(self,root):
            super().__init__(root)
            self.root=root
            self.grid()#布局方式网格布局
            self.local_ip=127.0.0.1#服务器端默认的IP地址
            self.serv_ports=[10888,20888,30888]#三个服务器的端口
            self.init_components()#初始化界面方法
        
        #界面函数的实现
        def init_components(self):#初始化用户界面的方法
            proj_name=Label(self,text=远程备份服务器)
            proj_name.grid(columnspan=2)#网格式布局,占用两列
            
            serve_ip_label=Label(self,text=服务地址)
            serve_ip_label.grid(row=1)#列是默认为0列
            
            #下拉列表,显示服务器的地址拱用户选择
            self.serv_ip=Combobox(self,values=self.get_ipaddr())
            #设置默认的服务器的ip地址
            self.serv_ip.set(self.local_ip)
            self.serv_ip.grid(row=1,column=1)
            
            #服务端口的LABEL
            serv_port_label=Label(self,text=服务端口)
            serv_port_label.grid(row=2) 
            #下拉列表,显示服务器的服务端口拱用户选择
            self.serv_port=Combobox(self,values=self.serv_ports)
            #设置默认的服务端口
            self.serv_port.set(self.serv_ports[1])
            #网格布局  放置指定位置
            self.serv_port.grid(row=2,column=1)
            
            #启动按钮
            self.start_serv_btn=Button(self,text=启动服务,command=self.start_serv)
            self.start_serv_btn.grid(row=3)
            
            #退出服务的按钮
            self.start_exit_btn=Button(self,text=退出服务,command=self.root.destroy)
            self.start_exit_btn.grid(row=3,column=1)
            
        def get_ipaddr(self):
            #获取本机的ip地址
            #获取名字
            host_name=socket.gethostname()
            #获取信息
            info=socket.gethostbyname_ex(host_name)
            info=info[2]
            info.append(self.local_ip)
        
        #定义启动服务器的方法    
        def start_serv(self):
            #线程化运行
    #         print(self.serv_ip.get(),self.serv_port.get())
    #         start(self.serv_ip.get(),int(self.serv_port.get()))
            host = self.serv_ip.get()#获取服务器地址
            port = int(self.serv_port.get())#获取服务端口,转化为整型
            serv_th= threading.Thread(target=start,args=(host,port))#建立线程化服务器
            serv_th.start()
            #当点击启动服务之后,我们关闭这个按钮不让服务再次启动
            self.start_serv_btn.state([disabled,])
            
    #建立一个自己的跟窗口的类,为了退出
    class MyTk(Tk):
        def destroy(self):
            global SERV_RUN_FLAG
            while True:
                if flag_lock.acquire():#获取全局共享变量
                    SERV_RUN_FLAG=False
                    flag_lock.release()
                    break
                super().destroy()
                
    if __name__==__main__:
        root=MyTk()
        root.title(备份服务器)
        root.resizable(FALSE, FALSE)#允许用户更改窗体大小
        a=MyFrame(root)
        a.mainloop()
    
    
    
    
    #coding=gbk
    from tkinter import *
    from tkinter.ttk import *
    import socket
    import os
    import pickle
    import struct
    import time
    import threading
    import tarfile,tempfile
    #获取给出路径的文件信息
    def get_file_info(path):
        if not path or not os.path.exists(path):
            return NONE
        files=os.walk(path)#获取文件
        infos=[]
        file_paths=[]
        for p,d,fs in files:#文件都在fs列表中
            for f in fs:
                file_name=os.path.join(p,f)#获取文件的文件名
                file_size=os.stat(file_name).st_size#获取文件的大小
                file_paths.append(file_name)#加入到file_path
                file_name=file_name[len(path)+1:]
                infos.append((file_size,file_name))#将文件信息加入到文件信息中
        return infos,file_paths
    
    #向服务器端发送文件信息
    def send_files_infos(my_sock,infos,compress):
        fmt_str=Q?
        infos_bytes=pickle.dumps(infos)#对文件信息进行二进制编码
        infos_bytes_len=len(infos_bytes)#获取长度
        infos_len_pack=struct.pack(fmt_str,infos_bytes_len,compress)#对长度利用struct进行二进制编码
        my_sock.sendall(infos_len_pack)#将整个发送放到服务器端
        my_sock.sendall(infos_bytes)#发送文件信息
    
    def send_files(my_sock,file_path,compress):#本机文件,本机文件路径
        if not compress:
            f = open(file_path,rb)
        else:
            f = tempfile.NamedTemporaryFile()
            tar=tarfile.open(mode=w|gz,fileobj=f)
            tar.add(file_path)
            tar.close()
            f.seek(0)
            filesize = os.stat(f.name).st_size
            filesize_bytes=struct.pack(Q, filesize)
            my_sock.sendall(filesize_bytes)
        try:
            while True:
                data=f.read(1024)
                if data:
                    my_sock.sendall(data)#发送
                else:
                    break
        finally:
                    f.close()
    
    def get_bak_info(my_sock,size=7):
        info = my_sock.recv(size)
        print(info.decode(utf-8))
    
    def start(host,port,src,compress):
        if not os.path.exists(src):
            print(备份的目标不存在!)
            return 
        s = socket.socket()#TCP协议
        s.connect((host,port))
        path = src#获取用户的备份路径
        file_infos,file_paths=get_file_info(path)#获取要备份的文件信息和路径
        send_files_infos(s,file_infos,compress)#发送文件信息
        for fp in file_paths:#发送所有信息至S
            send_files(s,fp,compress)
            print(fp)#把发送出去的文件的信息打印
            get_bak_info(s)#获取备份的结果
        s.close()
        
        
    class MyFrame(Frame):
        #初始化构造方法
        def __init__(self,root):
            super().__init__(root)
            self.root=root
            self.grid()#布局方式网格布局
            self.remote_ip=127.0.0.1#服务器端的IP地址默认值
            self.remote_ports=10888#默认的端口
            self.remote_ip_var=StringVar()#输入框
            self.remote_ports_var=IntVar()
            self.bak_src_var=StringVar()
            self.compress_var=BooleanVar()
            self.init_components()#初始化界面方法
        
        #界面函数的实现
        def init_components(self):#初始化用户界面的方法
            proj_name=Label(self,text=远程备份客户端)
            proj_name.grid(columnspan=2)#网格式布局,占用两列
            
            serve_ip_label=Label(self,text=服务地址:)
            serve_ip_label.grid(row=1)#列是默认为0列
            
            #下拉列表,显示服务器的地址拱用户选择
            self.serv_ip=Entry(self,textvariable=self.remote_ip_var)
            #设置默认的服务器的ip地址e
            self.remote_ports_var.set(self.remote_ip)
            self.serv_ip.grid(row=1,column=1)
            
            #服务端口的LABEL
            serv_port_label=Label(self,text=服务端口:)
            serv_port_label.grid(row=2) 
            #下拉列表,显示服务器的服务端口拱用户选择
            self.serv_port=Entry(self,textvariable=self.remote_ports_var)
            #设置默认的服务端口
            self.remote_ports_var.set(self.remote_ports)
            #网格布局  放置指定位置
            self.serv_port.grid(row=2,column=1)
            
            #用户备份的数据
            src_label=Label(self,text=备份的目标:)
            src_label.grid(row=3)
            
            #输入框
            self.bak_src=Entry(self,textvariable=self.bak_src_var)
            self.bak_src.grid(row=3,column=1)
            
            tar_label=Label(self,text=备份压缩:)
            tar_label.grid(row =4 )
            
            self.compress_on=Checkbutton(self,text=开始压缩,variable=self.compress_var,onvalue=1,offvalue=0)
            self.compress_on.grid(row=4,column=1)
            #
            self.start_serv_btn=Button(self,text=开始备份,command=self.start_send)
            self.start_serv_btn.grid(row=5)
            
            #
            self.start_exit_btn=Button(self,text=退出程序,command=self.root.destroy)
            self.start_exit_btn.grid(row=5,column=1)
    
        
        #定义启动服务器的方法    
        def start_send(self):
    #         print(self.remote_ip_var.get(),self.remote_ports_var.get())
    #         print(‘start...‘)
            host = self.remote_ip_var.get()
            port = self.remote_ports_var.get()
            compress=self.compress_var.get()
            src=self.bak_src_var.get()
            self.bak_src_var.set(‘‘)
            t = threading.Thread(target=start,args=(host,int(port),src,compress))
            t.start()
    #         start(self.remote_ip_var.get(),int(self.remote_ports_var.get()),self.bak_src_var.get())#想服务器发送东西
            
    if __name__==__main__:
        root=Tk()
        root.title(远程备份客户机)
        root.resizable(FALSE, FALSE)#允许用户更改窗体大小
        a = MyFrame(root)
        a.mainloop()
    View Code
  • socketserver框架的使用
    •   编写网络服务应用器的框架,划分了一个基本服务器框架,划分了处理请求(服务器类和请求处理器类)
    •   服务器构建
      •   建立客户端处理类;初始化服务器类传入相关参数;启动服务器
    •   基本对象
      •   BasesSrver(通过继承定制服务器类)
        •   方法介绍:serve_forever启动服务器,
          •   handle_request()处理请求:顺序:先调用get_request()获取客户端请求连接,verify()对客户端请求连接进行认证,process_request():实现与客户端进行交互
          •   finish_request()创建
        •   关闭服务器shutdown();shutdown必须在serve_forever()不同线程中调用才能关闭服务器
      •   TCPServer
        •   继承baseserver的服务器类,可以直接初始化TCP服务器,初始化参数:(host,post)服务器服务地址;handler类:处理客户端数据
      •   UDPServer:TCPServer的子类,可以直接初始化
      •   BaseRequestHandle
        •   setup方法:准备请求处理器
        •   handle方法:完成请求具体操作(一般只用这个)
        •   finish方法:清理setup期间的相关资源
      •   StreamRequestHandler(上面那个类的子类)使用TCP协议

        •   定制请求处理器时可以只覆盖handle()
        •   实例属性request代表和客户端连接的socket可以用它实现TCP数据的接收
        • 技术分享
          #coding=gbk
          
          import  socketserver
          import threading
          
          #关闭服务器
          def sd():
              if serv:
                  serv.shutdown()#关闭服务器
                  #shutdown必须在serve_forever()不同线程中调用才能关闭服务器
          
          class MyHdl(socketserver.StreamRequestHandler):#tcp协议的服务器
              def handle(self):#覆盖handle方法
                  #和客户端进行交互
                  while True:
                      data = self.request.recv(1024)#接收数据
                      print(收到数据:,data.decode(utf-8))#解码
                      if data==bbye:
                          break
                      self.request.sendall(data)#将收到的数据传回给客户端
                  print(本次服务结束)    
                  threading.Thread(target=sd).start()
                  
          if __name__==__main__:
                  HOST=‘‘
                  PORT=3214
                  #实例化TCPserver
                  serv=socketserver.TCPServer((HOST,PORT),MyHdl)#服务的地址,自定义的类
                  #启动服务器
                  serv.serve_forever()
              
          
          
          #coding=gbk
          import socket
          
          HOST = 127.0.0.1
          PORT = 3214
          
          s=socket.socket()#tcp协议
          s.connect((HOST,PORT))
          data=你好
          while data:
              #发送数据到服务器端
              s.sendall(data.encode(utf_8))
              if data==bye:
                  break
              #从服务器端接收数据
              data = s.recv(1024)
              print(收到数据;,data.decode(utf-8))
              data=input(输入要发送的信息:)
          s.close()
          View Code
        •   客户端发过来的数据也可以rfile属性来处理,rfile是一个类file对象,有缓冲,可以按行分次读取,其方法主要有:read(n),readline()
        •   发往客户端的数据通过wfile属性来处理,wfile不软冲数据,对客户端发送的数据需要一次性写入,写入时用write(data)
        • 技术分享
          #coding=gbk
          
          import socketserver
          
          class MyHdl(socketserver.StreamRequestHandler):
              def handle(self):
                  while True:
                      #从客户端读取一行数据
                      data = self.rfile.readline()
                      if  not data:
                          break
                      print(收到:,data.decode(utf-8.strip(\n)))#strip去除末尾的换行符
                      #把收到数据发回给客户端   
                      self.wfile.write(data)
                      
          if __name__==__main__:
              HOST = ‘‘
              PORT = 3214
              s = socketserver.TCPServer((HOST,PORT),MyHdl)
              s.serve_forever()
          
          
          
          #coding=gbk
          import socket
          
          HOST = 127.0.0.1
          PORT = 3214
          
          s=socket.socket()#tcp协议
          s.connect((HOST,PORT))
          data=你好
          while data:
              #为了让服务器按行接收
              data +=\n
              s.sendall(data.encode(utf_8))
              data = s.recv(1024)
              print(收到数据;,data.decode(utf-8).strip(\n))#strip  去除换行符
              data=input(输入要发送的信息:)
          s.close()
          View Code
      •   DatagramRequestHandeler  使用udp协议
        •   略
        • 技术分享
          #coding=gbk
          
          import socketserver
          
          class MyHdl(socketserver.DatagramRequestHandler):
              def handle(self):
                      #UDP无连接的,从客户端获取字符和套接字
                      data,socket = self.request
                      print(收到:,data.decode(utf-8.strip(\n)))#strip去除末尾的换行符
                      #把收到数据发回给客户端   
                      socket.sendto(data,self.client_address)
                      
          if __name__==__main__:
              HOST = ‘‘
              PORT = 3214
              s = socketserver.UDPServer((HOST,PORT),MyHdl)
              s.serve_forever()
          
          
          #coding=gbk
          import socket
          
          HOST = 127.0.0.1
          PORT = 3214
          
          s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#UDP协议
          data=你好
          s.sendto(data.encode(utf-8),(HOST,PORT))
          while data != bye:
              data =b‘‘
              while len(data)==0:
                  data,addr=s.recvfrom(1024)
              print(收到数据;,data.decode(utf-8))
              data=input(输入要发送的信息:)
              if data == ‘‘:
                  data = bye
              s.sendto(data.encode(utf-8),(HOST,PORT))
          
              
          s.close()
          View Code
  • 使用socketserver重新编写备份服务器端
    • 技术分享
      #coding=gbk
      from tkinter import *
      from tkinter.ttk import *
      import socket
      import threading
      import os
      import struct
      import pickle
      import threading
      import socketserver
      
      #建立一个默认的备份目录
      BAK_PATH=re:\bak
      
      #根据指定长度来接受文件信息
      def recv_unit_data(clnt,infos_len):
          data=b‘‘ #原来文件为空
          #如果要接受的文件在0-1024之间就直接接收该文件,如果大于1024需要循环分段接收,每次只是接收1024个字节,剩下的在全部接收即可
          if 0<infos_len<=1024:#
              data+=clnt.recv(infos_len)#接收文件
          else:#长度太长
              while  True: 
                  if infos_len >1024:
                     data+=clnt.recv(1024) 
                     infos_len-=1024
                  else:
                      data+=clnt.recv(infos_len)
                      break
          return data
      
      def get_files_info(clnt):
          fmt_str=Q?#用于向服务器端传送文件信息的大小,文件信息的压缩选项
          headsize=struct.calcsize(fmt_str)#计算长度
          data=clnt.recv(headsize)
          infos_len,compress=struct.unpack(fmt_str, data)
          data =recv_unit_data(clnt, infos_len)
          return pickle.loads(data),compress#得到文件信息的列表
         
      def mk_math(filepath): #建立文件路径
          paths=filepath.split(os.path.sep)[:-1]#将文件路径进行分割
          p=BAK_PATH
          for path in paths:#遍历用户端传来的路径
              p=os.path.join(p,path)#将保存的路径添加到默认的路径上
              if not os.path.exists(p):#如果路径不存在就建立路径
                  os.mkdir(p)
                  
      def get_compress_size(clnt):            
          fmt_str = Q#长整型
          size=struct.calcsize(fmt_str)
          data = clnt.recv(size)
          size,=struct.unpack(fmt_str,data)#得到压缩后文件的大小
          return size
          
                  
      #接收客户端传来文件,并且根据文件信息来进行保存备份
      def recv_file(clnt,infos_len,filepath,compress):            
          mk_math(filepath)#遍历文件   通过路径
          filepath = os.path.join(BAK_PATH,filepath)#服务器上的路径的文件名
          #根据压缩选项判断
          if compress :
              infos_len = get_compress_size(clnt)#压缩后文件的长度
              filepath = ‘‘.join(os.path.splitext(filepath)[0],.tar.gz)
          f = open(filepath,wb+)#新建一个文件
          #接收文件
          try:
              if 0 < infos_len <=1024:
                  data = clnt.recv(infos_len)
                  f.write(data)
              else:
                  while True:
                      if infos_len >1024:
                          data=clnt.recv(1024)
                          f.write(data)
                          infos_len-=1024
                      else:
                          data = clnt.recv(infos_len)
                          f.write(data)
                          break
          except:
              print(error)
          else:
              return True
          finally:
              f.close()
        
        #向客户端发送失败成功消息      
      def send_echo(clnt,res):
          if res:
              clnt.sendall(bsuccess)
          else:
              clnt.sendall(bfailure)
      
      
      def client_operate(client):
              #compress 可压缩选项
              files_lst,compress=get_files_info(client)#获取客户端要传送的文件列表(包括文件大小和文件路径:元组)
              for size,filepath in files_lst:#遍历得到文件大小和路径
                  res = recv_file(client,size,filepath,compress)#接收所有的文件    返回备份的结果:true或者false
                  send_echo(client,res)#保存成功标志,发送给客户端
              
              client.close()#关闭客户端
             
      #建立服务器
      class BakHdl(socketserver.StreamRequestHandler):
          def handle(self):
              client_operate(self.request)
              
      #建立服务器和启动服务器
      def start(host,port):
          #初始化
          server = socketserver.ThreadingTCPServer((host,port),BakHdl)
          #线程的方法启动服务器
          s = threading.Thread(target=server.serve_forever)
          s.start()
          return server
      
      
      class MyFrame(Frame):
          #初始化构造方法
          def __init__(self,root):
              super().__init__(root)
              self.root=root
              self.server = None
              self.grid()#布局方式网格布局
              self.local_ip=127.0.0.1#服务器端默认的IP地址
              self.serv_ports=[10888,20888,30888]#三个服务器的端口
              self.init_components()#初始化界面方法
          
          #界面函数的实现
          def init_components(self):#初始化用户界面的方法
              proj_name=Label(self,text=远程备份服务器)
              proj_name.grid(columnspan=2)#网格式布局,占用两列
              
              serve_ip_label=Label(self,text=服务地址)
              serve_ip_label.grid(row=1)#列是默认为0列
              
              #下拉列表,显示服务器的地址拱用户选择
              self.serv_ip=Combobox(self,values=self.get_ipaddr())
              #设置默认的服务器的ip地址
              self.serv_ip.set(self.local_ip)
              self.serv_ip.grid(row=1,column=1)
              
              #服务端口的LABEL
              serv_port_label=Label(self,text=服务端口)
              serv_port_label.grid(row=2) 
              #下拉列表,显示服务器的服务端口拱用户选择
              self.serv_port=Combobox(self,values=self.serv_ports)
              #设置默认的服务端口
              self.serv_port.set(self.serv_ports[1])
              #网格布局  放置指定位置
              self.serv_port.grid(row=2,column=1)
              
              #启动按钮
              self.start_serv_btn=Button(self,text=启动服务,command=self.start_serv)
              self.start_serv_btn.grid(row=3)
              
              #退出服务的按钮
              self.start_exit_btn=Button(self,text=退出服务,command=self.exit)#退出服务关闭图形界面
              self.start_exit_btn.grid(row=3,column=1)
              
          def exit(self):
              if self.server:
                  threading.Thread(target=self.server.shutdown).start()#启动关闭服务器的线程
              self.root.destroy()
              
          def get_ipaddr(self):
              #获取本机的ip地址
              #获取名字
              host_name=socket.gethostname()
              #获取信息
              info=socket.gethostbyname_ex(host_name)
              info=info[2]
              info.append(self.local_ip)
          
          #定义启动服务器的方法    
          def start_serv(self):
              if not os.path.exists(BAK_PATH):
                  os.mkdir(BAK_PATH)
      #         print(self.serv_ip.get(),self.serv_port.get())
      #         start(self.serv_ip.get(),int(self.serv_port.get()))
              host = self.serv_ip.get()#获取服务器地址
              port = int(self.serv_port.get())#获取服务端口,转化为整型
              self.server = start(host,port)
      #         serv_th= threading.Thread(target=start,args=(host,port))#建立线程化服务器
      #         serv_th.start()
      #         #当点击启动服务之后,我们关闭这个按钮不让服务再次启动
              self.start_serv_btn.state([disabled,])
              
      if __name__==__main__:
          root=Tk()
          root.title(备份服务器)
          root.resizable(FALSE, FALSE)#允许用户更改窗体大小
          a=MyFrame(root)
          a.mainloop()
      View Code
  • 用socket实现FTP服务器和FTP客户端
    •   FTP协议
      •   提供可靠的文件传输,属于TCP/IP协议,位于应用层,采用典型的C/S模式工作,可用匿名或者指定用户登录,下层采用有连接可靠的TCP协议
    •   工作原理及过程
      •   FTP客户端             FTP服务器端
            登录服务器   <----->登录验证
         传输文件操作 <-----> 接收或者发送文件
          退出登录结束  <----->结束FTP服务
          文件操作
    •   工作模式
      •   主动模式(PORT):数据连接有服务器端发起,客户端建立接收服务器
      •   被动模式(PASV):和上面相反
    •   ftp最小实现:
      •   FTP命令:USER,QUIT,PORT,TYPE,MODE,STRU,RETR,STOP,NOOP
      •   命令返回码:2XX命令成功;4XX客户端错误;5XX服务错误
      •   传输方式:流模式文件传输,文件传输
    •   功能分析
      •   控制模块
        •   接收客户端命令,操作后返回结果;用户可以用空用户名或者匿名用户名登录;用port/pasv是服务器工作于不同模式
      •   数据传输模块:与客户端进行文件传输及其他数据交换
  • FTP最终代码
    •   
      技术分享
      #coding=gbk
      import socket
      import socketserver
      import threading
      import time
      import os
      
      #requesthandler类
      class FTPHdl(socketserver.StreamRequestHandler):
          def __init__(self,request=None,client_address=None,server=None):
              self.coms_keys = (QUIT,USER,NOOP,TYPE,PASV,PORT,RETP,STOR)
              #建立命令所对应的方法
              self.coms={}
              #
              self.init_coms()
              self.server
              #服务器命令模块的端口
              self.cmd_port=21
              #数据模块端口
              self.data_port=20
              #保存ip地址和端口号
              self.pasv_data_ip=None
              self.pasv_data_port=None
              
              self.args=None
              self.loged=False
              #模式
              self.pasv_mode=None
              super().__init__(request, client_address, server)
          
          #字典方法  命令
          def init_coms(self):
              for k in self.coms_keys:
                  self.coms[k]=getattr(self,exe_ + k.lower())#获取当前类的方法    exe为前缀,lower为命令的小写
          #用于对客户单进行处理
          def handle(self):
              while True:
                  #接收用户端命令,读取一行
                  cmds = self.rfile.readline()
                  if not cmds:
                      continue
                  cmds = cmds.decode(utf-8)
                  #建立命令动词    命令行的分析
                  cmd = self.deal_args(cmds)
                  if cmd in self.coms_keys:#命令动词是否在ftp所有的命令中
                      self.coms.get(cmd)()
                  else:
                      #返回错误代码
                      self.send(500,Invaild command.)
                      #如果命令为退出
                  if cmd == QUIT:
                      break
                  #分析命令信息
          def deal_args(self,cmds):
              #如果空格在命令行中   必须分开   前面是命令动词  后面是命令参数
              if   in cmds:
                  cmd,args=cmds.split( )
                  args = args.strip(\n).strip()#对参数进行处理
              else:
                  cmd=cmds.strip(\n)#删除换行符
                  args=‘‘
              if args:
                  self.args=args
              return cmd.upper()#返回命令动词    大写
          def exe_quit(self):
              self.send(221,bye)
          def exe_user(self):
              user=self.args
              if user in (‘‘,anonymous):
                  user = annoymous
                  self.loged=True
                  self.send(230,identified!)
              else:
                  self.send(530,Only use annoymous)
                  
          def exe_pasv(self):
              if not self.loged:
                  self.send(332,Please login.)
                  return 
              if self.pasv_mode:
                  info = entering passive mode (%s) % self.make_pasv_info()
                  self.send(227,info)
                  return
              try:
                  self.enter_pasv()
                  info = entering passive mode (%s) %self.make_pasv_info()
                  self.pasv_mode=True
                  self.send(227,info)
              except Exception as e:
                  print(e)
                  self.send(500,failure change to passive mode.)
          def enter_pasv(self):
              if self.server.data_server is None:
                  self.pasv_data_ip,self.pasv_data_port=self.server.create_data_server()
          def exe_port(self):
              self.send(500,Do not offer port mode.)
          def exe_noop(self):
              self.send(200,ok)
          def exe_type(self):
              self.send(200,ok)
          def exe_retr(self):
              if not os.path.exists(self.args):
                  self.send(550,File is not exists.)
                  return 
              client_addr=self.request.getpeername()[0]
              self.add_opr_file(client_addr,(RETR,self.args))
              self.send(150,ok.)
          def exe_stor(self):
              client_addr=self.request.getpeername()[0]
              self.add_opr_file(client_addr,(STOP,self.args))
          def add_opr_file(self,client_addr,item):
              if client_addr in DataHdl.client_opr:
                  DataHdl.client_opr[client_addr].append(item)
              else:
                  DataHdl.client_opr[client_addr]=[item,]
                  
          def sebd(self,code,info):
              infos=%d %s\n % (code,info)
              self.request.sendall(infos.encode(utf_8))
              
      class MyThrTCPServ(socketserver.ThreadingTCPServer):
          def __init__(self,addr,Hdl):
              self.data_server=None
              super().__init__(addr,Hdl)
          def shutdown(self):
              if self.data_server:
                  threading.Thread(target=self.data_server.shutdown).start()
              super().shutdown()
          def create_data_server(self):
              self.data_server=socketserver.ThreadingTCPServer((127.0.0.1,0),DataHdl)
              pasv_data_ip,pasv_data_port=self.data_server.server_address
              threading.Thread(target=self.data_server.serve_forever).start()
              return pasv_data_ip,pasv_data_port
          
      class DataHdl(socketserver.StreamRequestHandler):
          client_opr={}
          def handle(self):
              peerip=self.request.getpeername()[0]
              opr=self.get_opr_args(peerip)
              if opr:
                  if opr[0]==RETR:
                      self.retr_file(opr[1])
                  elif opr[0]==STOR:
                      self.stor_file(opr[1])
              self.request.close()
              
          def get_opr_args(self,peerip):
              if peerip in self.client_opr:
                  opr= self.client_opr[peerip].pop(0)
                  if not self.client_opr[peerip]:
                      self.client_opr.pop(peerip)
                  return opr
          def retr_file(self,filepath):
              f = open(filepath,rb)
              while True:
                  data = f.read(1024)
                  if data:
                      self.request.sendall(data)
                  else:
                      break
              f.close()
          def stor_file(self,filepath):
              f=open(os.path.join(.,baket,filepath),wb)
              while True:
                  data =self.request.recv(1024)
                  if data:
                      f.write(data)
                  else:
                      break
              f.close()
              
      if __name__==__main__:
          server = MyThrTCPServ((127.0.0.1,21),FTPHdl)
          threading.Thread(target=server.serve_forever).start()
          print(FTP start...)
          time.sleep(30)
          server.shutdown()
          
              
              
              
                  
          #coding=gbk
      import os
      import socket
      import threading
      import socketserver
      
      def get_file(host,port,filepath):
          s=socket.socket()
          s.connect((host,port))
          filepath=os.path.join(.,bakt,filepath)
          f=open(filepath,wb)
          data = True
          while data:
              data=s.recv(1024)
              if data:
                  f.write(data)
          s.close()
          f.close()
      def put_file(host,port,filepath):
          s=socket.socket()
          s.connect((host,port))
          f=open(filepath,rb)
          while true:
              data=f.read(1024)
              if data:
                  s.sendall(data)
              else:
                  break
          s.close()
          f.close()
          
      class FtpClient:
          def __init__(self,host=localhost,port=21):
              self.host=host
              self.port=port
              self.cmds=(QUIT,USER,NOOP,TYPE,PASV,PORT,RETP,STOR)
              self.linesep=\n
              self.data_port=None
              self.loged=False
              self.sock=None
              self.pasv_mode=None
              self.pasv_host=None
              self.pasv_port=None
              
          def cmd_connect(self):
              self.sock=socket.socket()
              self.sock.connect((self.host,self.port))
              self.data_port=self.sock.getsockname()[0]
              
          def start(self):
              print(支持的命令:,self.cmds)
              self.cmd_connect()
              self.login()
              while True:
                  cmd=input(请输入FTP命令: )
                  if not cmd:
                      print(FTP命令不能为空。)
                      continue
                  cmd,args=self.split_args(cmd)
                  if not self.send_cmd(cmd,args):
                      continue
                  res = self.readline(self.sock)
                  print(res)
                  if cmd.stratswith(PASV) and res.startswith(227):
                      self.pasv_mode=True
                      servinfo =res[res.index(()+1:res.index())]
                      self.pasv_host=..join(servinfo.split(,)[:4])
                      servinfo =servinfo.split(,)[-2:]
                      self.pasv_port=256*int(servinfo[0])+int(servinfo[1])
                  if cmd.startswith(RETR):
                      if self.pasv_mode:
                          threading.Thread(target=get_file,args=(self.pasv_host,self.pasv_port,args)).start()
                  if cmd.startswith(STOR):
                      if self.pasv_mode:
                          threading.Thread(target=put_file,args=(self.pasv_host,self.pasv_port,args)).start()       
                  if cmd.startswith(QUIT):
                      break
              self.sock.close()
              self.sock=None
              
          def login(self):
              if self.sock:
                  self.send_cmd(USER)
                  res=self.readline(self.sock)
                  if res.startswith(230):
                      print(登录成功!)
                      self.loged
                      
          def readline(self,sock):
              data = ‘‘
              while not data.endswith(self.linesep):
                  d=sock.recv(1)
                  data +=d.decode(utf-8)
              return data
          def split_args(self,cmds):
              if   in cmds:
                  cmdlsts = cmds.split( )
                  cmd = cmdlsts[0]
                  args= .join(cmdlsts[1:])
              else:
                  cmd = cmds
                  args = ‘‘
                  return cmd.upper(),args
          def send_cmd(self,cmd,args=‘‘):
              if self.sock:
                  if args:
                      cmd =  .join((cmd,args))
                  if cmd.startswith(RETR) or cmd.startswith(STOR):
                      if self.pasv_mode is None:
                          print(Please appoint port or stor mode.)
                          return False
                      if not args:
                          return False
                  if cmd.startswith(STOR):
                      if args:
                          if not os.path.exists(args):
                              print(File is not exists)
                              return False
                  cmd+=self.linesep
                  self.sock.sendall(cmd.encode(utf-8))
                  return True
              
      if __name__==__main__:
          fc=FtpClient()
          fc.start()
                          
                  
                  
                  
              
          
      View Code

       

Python--socket和threading编程

标签:

原文地址:http://www.cnblogs.com/Kobe10/p/5714622.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!