网络编程基础(一)

    •   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()
    •   用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==b'bye':#为空,断开连接
          break
          else:
          print('receive data :',data.decode('utf-8'))#数据不为空,输出
          s.send(data,addr)#将数据发挥客户端
          s.close()
  • 客户端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()
    •   用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()
  • 备份服务器的服务器端的实现
    •   尝试一个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()
    •   最简备份服务器端建立
      •   同一时间只能连接一个客户并且为其备份
      •   备份客户端的一个目录及其子目录的所有文件
      •   与客户端交互
        •   客户端发送即将要发送文件信息的大小
        •   服务器端接收客户端通知的文件信息的大小
        •   客户端发送文件信息(包括文件大小。文件名)
        •   服务器端依照文件信息的大小接收文件信息
        •   客户端逐个发送文件数据,每发送完一个文件数据,接收该文件的备份结果
        •   服务器端接收文件数据并保存备份至文件系统,每接收完一个文件就返回备份结果
        • #coding=gbk
          from tkinter import *
          from tkinter.ttk import *
          import socket
          import os
          import struct
          import pickle
          #建立一个默认的备份目录
          BAK_PATH=r'e:\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(b'success')
          else:
          clnt.sendall(b'failure') #启动服务器的方法
          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()
  • 备份服务器的基本客户端实现
    •   功能

      •   设置连接服务器的IP地址和端口号
      •   输入备份目录,备份其中的所有文件
      •   显示服务器端发来的备份结果
      •   选择备份时启用压缩备份
  •   客户端与服务器最终版
  • #coding=gbk
    from tkinter import *
    from tkinter.ttk import *
    import socket
    import os
    import struct
    import pickle
    #建立一个默认的备份目录
    BAK_PATH=r'e:\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(b'success')
    else:
    clnt.sendall(b'failure') #启动服务器的方法
    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()
  • 通过多线程实现备份服务器端
    •   单线程服务端问题

      •   启动服务,界面停止响应
      •   一个客户端正在备份,其他客户端不能连接
    •   建立多线程服务器
      •   解决点击启动服务,页面停止响应的问题,实现多个客户端进行交互
      •   退出服务器:将服务线程配置为后台线程(可能使文件丢失);应用线程同步的手段退出服务(这个方法好)
      •   可压缩备份服务
        •   客户端发送已压缩文件
        •   与客户端交互基本流程
  • 通过多线程实现备份客户端
  • 最终版
  • #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=r'e:\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(b'success')
    else:
    clnt.sendall(b'failure') 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()
  • 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==b'bye':
          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()
        •   客户端发过来的数据也可以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()
      •   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()
  • 使用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=r'e:\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(b'success')
      else:
      clnt.sendall(b'failure') 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()
  • 用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(''):
      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(''):
      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()

Python--socket和threading编程的更多相关文章

  1. Python Socket,How to Create Socket Server? - 网络编程实例

    文章出自:Python socket – network programming tutorial by Silver Moon 原创译文,如有版权问题请联系删除. Network programin ...

  2. python socket 编程简单入门

    想讲讲套接字的概念 套接字,即英文socket的中文意译,起源于20世纪70年代,是加利福利亚大学的伯克利版本UNIX(称为BSD UNIX)的一部分.目的是实现主机上运行的一个程序与另一个运行的程序 ...

  3. [Python_7] Python Socket 编程

    0. 说明 Python Socket 编程 1. TCP 协议 [TCP Server] 通过 netstat -ano 查看端口是否开启 # -*-coding:utf-8-*- "&q ...

  4. Python Socket编程基础篇

    Socket网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. ...

  5. Python Socket套接字编程

    Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...

  6. 第九章:Python高级编程-Python socket编程

    第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...

  7. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  8. Python Socket 网络编程

    Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...

  9. Python:使用threading模块实现多线程编程

    转:http://blog.csdn.net/bravezhe/article/details/8585437 Python:使用threading模块实现多线程编程一[综述] Python这门解释性 ...

  10. Python Socket,How to Create Socket Cilent? - 网络编程实例

    文章出自:Python socket – network programming tutorial by Silver Moon 原创译文,如有版权问题请联系删除. Network programin ...

随机推荐

  1. CodeForces - 896D :Nephren Runs a Cinema(卡特兰数&组合数学---比较综合的一道题)

    Lakhesh loves to make movies, so Nephren helps her run a cinema. We may call it No. 68 Cinema. Howev ...

  2. 1151 LCA in a Binary Tree(30 分)

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  3. java 实现树形结构

    package tree; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java ...

  4. 使用window.print()后,未关闭打印页面,原网页不能操作

    使用window.print()后,未关闭打印页面,原网页不能操作,此时可以试着用window.location.reload()重新加载页面解决问题.

  5. UDP打洞原理及代码

    来源:http://www.fenbi360.net/Content.aspx?id=1021&t=jc UDP"打洞"原理 1.       NAT分类 根据Stun协议 ...

  6. Vue 内联样式的数据绑定

    Vue 内联样式的数据绑定 之前学的是数据绑定 class,现在可以将数据绑定到 style 中. <div id="app"> <div v-bind:styl ...

  7. WebForm中创建树节点TreeNode

    Tree: namespace ECTECH.NorthSJ.Web.SysData { public partial class testTree : BasePage { ; protected ...

  8. 机器学习:多项式回归(scikit-learn中的多项式回归和 Pipeline)

    一.scikit-learn 中的多项式回归 1)实例过程 模拟数据 import numpy as np import matplotlib.pyplot as plt x = np.random. ...

  9. Cassandra 学习三 数据模型

       Cassandra如何存储数据的概述. 集群(Cluster) ·Cassandra数据库分布在几个一起操作的机器上.最外层容器被称为集群.对于故障处理,每个节点包含一个副本,如果发生故障,副本 ...

  10. MySQL组合索引最左匹配原则

    几个重要的概念 1.对于mysql来说,一条sql中,一个表无论其蕴含的索引有多少,但是有且只用一条. 2.对于多列索引来说(a,b,c)其相当于3个索引(a),(a,b),(a,b,c)3个索引,又 ...