zoukankan      html  css  js  c++  java
  • 基于序列化技术(Protobuf)的socket文件传输

    好像好久都没更博文了,没办法,最近各种倒霉事情,搞到最近真的没什么心情,希望之后能够转运吧。

    言归正传,这次我要做的是基于序列化技术的socket文件传输来无聊练一下手。

    一.socket文件传输

           之前我所做的服务器和客户端的tcp/udp通信都是以字符串流来进行单工的,双工的传输,其实关于文件传输的原理也差不多,我的主要方法是通过文件迭代器遍历文件流,并将其读取转化为字符串流,然后将字符串流从服务器端发送,然后客户端在缓冲区中接收数据流,然后并把它写入文件当中,从而实现文件的传输,在下面程序当中,我仅仅是实现由服务器分发,客户端除了接收什么都不做的简单程序。

    服务器端:

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    
    from socket import *
    from time import ctime
    import os
    
    HOST = ''
    PORT = 21567
    BUFSIZ = 4096
    ADDR = (HOST,PORT)
    
    tcpSerSock = socket(AF_INET,SOCK_STREAM)
    tcpSerSock.bind(ADDR)
    tcpSerSock.listen(5)
    
    filename = raw_input('Please input the file name:')
    
    while True:
        print 'waiting for connection...'
        tcpCliSock,addr = tcpSerSock.accept()
        print '...connected from:',addr
    
        print '向客户端发送文件...'
        f = open(filename,'r')
        
        for eachLine in f:
            tcpCliSock.send('%s' % eachLine)
        f.close()
        tcpCliSock.close()
        print 'Done!'
        
    tcpSerSock.close()

    客户端:

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    from socket import *
    
    HOST = 'localhost'
    PORT = 21567
    BUFSIZ = 4096
    ADDR = (HOST,PORT)
    
    tcpCliSock = socket(AF_INET,SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    
    filename = raw_input('Please input the filename you will write to:')
    f = open(filename,'a')  #以追加模式打开文件
    print '正在写文件....'
    while True:
        data = tcpCliSock.recv(BUFSIZ)
        if not data:
            break
        f.write(data)
        
    f.close()
    print 'Done!'
    tcpCliSock.close()

    还有一种比较方便的方法是用框架SocketServer来实现文件传输的功能,相当于重新实现linux的scp命令。

    服务器端:

    #!/usr/bin/env python
    #-*-coding:utf-8-*-
    
    import SocketServer
    
    addr = ('',21568)
    
    class DocHandler(SocketServer.StreamRequestHandler):
        def handle(self):
            doc_len = ord(self.rfile.read(1))  #文件的第一个字符代表文件名的长度
            name = self.rfile.read(doc_len)
            print "接收文件:%s" % name
            f = open('../'+name,'w')
            cont = self.rfile.read(4096)
            while cont:
                f.write(cont)
                cont = self.rfile.read(4096)
            f.close()
            print "Done :%s" % name
    
    server = SocketServer.TCPServer(addr,DocHandler)  #利用tcp传输
    server.serve_forever()

    客户端:

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    
    from socket import *
    import os.path
    
    addr = ('',21568)
    
    def get_header(name):
        n_len = len(name)
        assert n_len < 250
        #assert语句,代表一个肯定的判定语句,如果为false,会抛出AssertError的异常
        return chr(n_len) + name
    
    def send_file(name):
        basename = os.path.basename(name)
        header = get_header(basename)
        cont = open(name).read()
        s = socket(AF_INET,SOCK_STREAM)
        s.connect(addr)
        s.sendall(header)
        s.sendall(cont)
        s.close()
    
    if __name__ == '__main__':
        filename = raw_input("请输入你要传输的文件名:")
        send_file(filename)

    二.序列化技术

    所谓序列化技术,自己可以google一下。

    这次我用的序列化技术的框架是google 的protocol buffer(简称protobuf),关于它的详细介绍,可以看看它的介绍,文档和API,它是一种和平台无关,语言无关的技术。

    这次的程序我本来想用C++写的,但无奈环境搭建失败,用不了,只好再用python的。

    首先为了避免重复劳动,我使用了它原来example的.proto文件,做一个关于名片传输的程序。

    addressbook.proto

    // See README.txt for information and build instructions.
    
    package tutorial;
    
    option java_package = "com.example.tutorial";
    option java_outer_classname = "AddressBookProtos";
    
    message Person {
      required string name = 1;
      required int32 id = 2;        // Unique ID number for this person.
      optional string email = 3;
    
      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }
    
      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }
    
      repeated PhoneNumber phone = 4;
    }
    
    // Our address book file is just one of these.
    message AddressBook {
      repeated Person person = 1;
    }

    输入指令来生成addressbook_pb2.py:(注:如果是C++,则python换为cpp;如果是Java,则python换为java)

    protoc -I=./ --python_out=./ addressbook.proto

    然后先尝试弄个单机版出来吧。 根据官方的demo修改整合出来的。

    add_person.py

    #! /usr/bin/python
    
    # See README.txt for information and build instructions.
    
    import addressbook_pb2
    import sys
    
    # This function fills in a Person message based on user input.
    def PromptForAddress(person):
      person.id = int(raw_input("Enter person ID number: "))
      person.name = raw_input("Enter name: ")
    
      email = raw_input("Enter email address (blank for none): ")
      if email != "":
        person.email = email
    
      while True:
        number = raw_input("Enter a phone number (or leave blank to finish): ")
        if number == "":
          break
    
        phone_number = person.phone.add()
        phone_number.number = number
    
        type = raw_input("Is this a mobile, home, or work phone? ")
        if type == "mobile":
          phone_number.type = addressbook_pb2.Person.MOBILE
        elif type == "home":
          phone_number.type = addressbook_pb2.Person.HOME
        elif type == "work":
          phone_number.type = addressbook_pb2.Person.WORK
        else:
          print "Unknown phone type; leaving as default value."
    
    # Main procedure:  Reads the entire address book from a file,
    #   adds one person based on user input, then writes it back out to the same
    #   file.
    
    filename = raw_input('Please input the file name:')
    
    address_book = addressbook_pb2.AddressBook()
    
    # Read the existing address book.
    try:
      f = open(filename, "rb")
      address_book.ParseFromString(f.read())
      f.close()
    except IOError:
      print sys.argv[1] + ": File not found.  Creating a new file."
    
    # Add an address.
    PromptForAddress(address_book.person.add())
    
    # Write the new address book back to disk.
    f = open(filename, "wb")
    f.write(address_book.SerializeToString())
    f.close()

    list_person.py

    #! /usr/bin/python
    
    # See README.txt for information and build instructions.
    
    import addressbook_pb2
    import sys
    
    # Iterates though all people in the AddressBook and prints info about them.
    def ListPeople(address_book):
      for person in address_book.person:
        print "Person ID:", person.id
        print "  Name:", person.name
        if person.HasField('email'):
          print "  E-mail address:", person.email
    
        for phone_number in person.phone:
          if phone_number.type == addressbook_pb2.Person.MOBILE:
            print "  Mobile phone #:",
          elif phone_number.type == addressbook_pb2.Person.HOME:
            print "  Home phone #:",
          elif phone_number.type == addressbook_pb2.Person.WORK:
            print "  Work phone #:",
          print phone_number.number
    
    # Main procedure:  Reads the entire address book from a file and prints all
    #   the information inside.
    filename = raw_input("Please input the filename:")
    address_book = addressbook_pb2.AddressBook()
    
    # Read the existing address book.
    f = open(filename, "rb")
    address_book.ParseFromString(f.read())
    f.close()
    
    ListPeople(address_book)

     运行过没有任何问题。

    三.整合

    好了,到了最后一步了, 实现的功能时,服务器端先要求你设定你自己要添加自己的名片信息,序列化,然后分发给客户端,客户端再把它反序列化,输出个人信息。

    服务器端:

     1 #!/usr/bin/env python
     2 #-*- coding:utf-8 -*-
     3 
     4 from socket import *
     5 from time import ctime
     6 import os
     7 import addressbook_pb2
     8 
     9 HOST = ''
    10 PORT = 21567
    11 BUFSIZ = 4096
    12 ADDR = (HOST,PORT)
    13 
    14 tcpSerSock = socket(AF_INET,SOCK_STREAM)
    15 tcpSerSock.bind(ADDR)
    16 tcpSerSock.listen(5)
    17 
    18 #添加联系人函数
    19 def PromptForAddress(person):
    20   person.id = int(raw_input("Enter person ID number: "))
    21   person.name = raw_input("Enter name: ")
    22 
    23   email = raw_input("Enter email address (blank for none): ")
    24   if email != "":
    25     person.email = email
    26 
    27   while True:
    28     number = raw_input("Enter a phone number (or leave blank to finish): ")
    29     if number == "":
    30       break
    31 
    32     phone_number = person.phone.add()
    33     phone_number.number = number
    34 
    35     type = raw_input("Is this a mobile, home, or work phone? ")
    36     if type == "mobile":
    37       phone_number.type = addressbook_pb2.Person.MOBILE
    38     elif type == "home":
    39       phone_number.type = addressbook_pb2.Person.HOME
    40     elif type == "work":
    41       phone_number.type = addressbook_pb2.Person.WORK
    42     else:
    43       print "Unknown phone type; leaving as default value."
    44 
    45 filename = raw_input('Please input the file name:')
    46 
    47 address_book = addressbook_pb2.AddressBook()
    48 
    49 try:
    50     f = open(filename,"rb")
    51     address_book.ParseFromString(f.read())
    52     f.close()
    53 except IOError:
    54     print filename + ": File not found. Creating a new file."
    55 
    56 #添加联系人信息
    57 PromptForAddress(address_book.person.add())
    58 
    59 #写进去
    60 f = open(filename,"wb")
    61 f.write(address_book.SerializeToString())
    62 f.close()
    63 
    64 while True:
    65     print 'waiting for connection...'
    66     tcpCliSock,addr = tcpSerSock.accept()
    67     print '...connected from:',addr
    68 
    69     print '向客户端发送文件...'
    70     f = open(filename,'rb')
    71     
    72     for eachLine in f:
    73         tcpCliSock.send('%s' % eachLine)
    74     f.close()
    75     tcpCliSock.close()
    76     print 'Done!'
    77     
    78 tcpSerSock.close()

    客户端:

     1 #!/usr/bin/env python
     2 #-*- coding:utf-8 -*-
     3 from socket import *
     4 import addressbook_pb2
     5 
     6 HOST = 'localhost'
     7 PORT = 21567
     8 BUFSIZ = 4096
     9 ADDR = (HOST,PORT)
    10 
    11 tcpCliSock = socket(AF_INET,SOCK_STREAM)
    12 tcpCliSock.connect(ADDR)
    13 
    14 #输出信息函数
    15 def ListPeople(address_book):
    16   for person in address_book.person:
    17     print "Person ID:", person.id
    18     print "  Name:", person.name
    19     if person.HasField('email'):
    20       print "  E-mail address:", person.email
    21 
    22     for phone_number in person.phone:
    23       if phone_number.type == addressbook_pb2.Person.MOBILE:
    24         print "  Mobile phone #:",
    25       elif phone_number.type == addressbook_pb2.Person.HOME:
    26         print "  Home phone #:",
    27       elif phone_number.type == addressbook_pb2.Person.WORK:
    28         print "  Work phone #:",
    29       print phone_number.number
    30 
    31 
    32 filename = raw_input('Please input the filename you will write to:')
    33 address_book = addressbook_pb2.AddressBook()
    34 
    35 f = open(filename,'ab')  #以追加模式打开文件
    36 print '正在写文件....'
    37 while True:
    38     data = tcpCliSock.recv(BUFSIZ)
    39     if not data:
    40         break
    41     f.write(data)
    42     
    43 f.close()
    44 print 'Done!'
    45 
    46 f = open(filename,"rb")
    47 address_book.ParseFromString(f.read())
    48 f.close()
    49 
    50 ListPeople(address_book)
    51 
    52 tcpCliSock.close()

    搞定!请多多指教!

    转载请注明出处:http://www.cnblogs.com/sysu-blackbear/

  • 相关阅读:
    距离某天还有多久
    U3D各键值说明
    一些比较重要的函数
    U3D功能脚本备忘
    沟边
    渲染排序
    字符串转整数备录
    沟边
    U3D优化
    Unity中的四个路径
  • 原文地址:https://www.cnblogs.com/sysu-blackbear/p/3822039.html
Copyright © 2011-2022 走看看